package nxm.sys.lib;

import nxm.sys.inc.Constants;
import nxm.sys.inc.DataTypes;

/**
  Structure for a MPU Library

  This function may have implementations in more than one language.

  The NeXtMidas policy is to code MPU functions in C or Fortran to allow
  code sharing with other versions of Midas.  All other framework and GUI
  code is in Java for portability and deployment options.

  Typically, math functions needed by GUIs are included in the SYS tree.
  Some functions are coded here in Java and in C for performance comparisons.

  The Java code for each function library exposes methods for setting and
  getting each of the library's properties.  If native code is used, the
  Java wrapper loads the native code and calls the standard library interface
  routines as appropriate.

  This class implements a factory for instantiating alternate native implementations.

  Library names should adhere to the following naming conventions:

   Foo.java                          - the java implementation of the function Foo
   FooNative.java / libFooNative.c   - the native C implementation of Foo
   FooGPU.java / libFooGPU.c         - the GPU native C implementation of Foo
   FooVerilog.java / libFooVerilog.c - the verilator (Verilog HDL) C++ simulator implementation of Foo
   FooICE.java / libFooICE.c         - the ICE FPGA implementation of Foo

  @author Jeff Schoen
  @since NeXtMidas 2.9.2
*/
public class MPULib extends Object implements Constants {

  /** implementation factory list */
  public static String factoryList = "Java,Native,GPU,Verilog,ICE";
  /** Use Java implementation */
  public static int JAVA=0;
  /** Use Native implementation */
  public static int NATIVE=1;
  /** Use Graphical Processing Unit implementation */
  public static int GPU=2;
  /** Use VERILOG implementation */
  public static int VERILOG=3;
  /** Use ICE FPGA implementation */
  public static int ICE=4;

  /** Create an instance of the function specified by name and parsing of the Args class
   *  @param name name of function
   *  @param args Args for function
   *  @return instance of the function
   */
  public static MPULib forName (String name, Args args) {
    String path = "nxm."+args.option.toLowerCase()+".lib";
    int impl = args.getSelectionIndex("/MPU",factoryList,0,-1);
    return forName(path,name,impl);
  }

  /** Create an instance of the function specified by path, name, and implementation
   *  @param path path to function
   *  @param name root name is java imp
   *  @param impl implementation type
   *  @return instance of the function
   */
  public static MPULib forName (String path, String name, int impl) {
         if (impl==JAVA)    name = path+"."+name;               // root name is java impl
    else if (impl==NATIVE)  name = path+"."+name+"Native";      // extensions
    else if (impl==GPU)     name = path+"."+name+"GPU";
    else if (impl==ICE)     name = "nxm.ice.lib."+name+"ICE";
    else if (impl==VERILOG) name = "nxm.ice.lib."+name+"Verilog";
    return forName(name);
  }

  /** Create an instance of the function specified by full class name
   *  @param className class name
   *  @return instance of function
   */
  public static MPULib forName (String className) {
    return (MPULib) Shell.getInstanceForClassName(className);
  }

  /** default constructor */
  public MPULib () {
  }

  /** Initialize this engine with current parameters
      @return 0 (generally) */
  public int open() {
    return 0;
  }

  /** process
   *
   *  @param n number to process
   *  @return number processed (or 0)
   */
  public int process(int n) {
    return 0;
  }
  /** process 1 buffer
  *
  *   @param buf1   first buffer
  *   @param n1     number in first buffer to process
  *   @return number processed (or 0)
  */
  public int process(byte[] buf1, int n1) {
    return 0;
  }
  /** process 2 buffers
  *
  *   @param buf1   first buffer
  *   @param n1     number in first buffer to process
  *   @param buf2   second buffer
  *   @param n2     number in second buffer to process
  *   @return number processed (or 0)
  */
  public int process(byte[] buf1, int n1, byte[] buf2, int n2) {
    return 0;
  }
  /** process 3 buffers
  *
  *   @param buf1   first buffer
  *   @param n1     number in first buffer to process
  *   @param buf2   second buffer
  *   @param n2     number in second buffer to process
  *   @param buf3   third buffer
  *   @param n3     number in third buffer to process
  *   @return number processed (or 0)
  */
  public int process(byte[] buf1, int n1, byte[] buf2, int n2, byte[] buf3, int n3) {
    return 0;
  }

  /** close
   *  @return 0 (generally)
   */
  public int close() {
    return 0;
  }


  /** SetKey for generic parameter. Override this method and call super if not handled.
      @param key name of key to store Data parameter
      @param value Data parameter to store at specified key
   */
  public void set (String key, Data value) {
    if (key.startsWith("BOGUS"));
    else throw new MidasException ("Problem setting key="+key+" to value="+value);
  }
  /** SetKey helper for String parameter.
      @param key name of key to store parameter
      @param value string parameter to store at specified key
   */
  public void set (String key, String value) {
    set (key, new Data(value));
  }
  /** SetKey helper for double parameter.
      @param key name of key to store parameter
      @param value double parameter to store at specified key
   */
  public void set (String key, double value) {
    set (key, new Data(value));
  }
  /** SetKey helper for float parameter.
      @param key name of key to store parameter
      @param value float parameter to store at specified key
   */
  public void set (String key, float value) {
    set (key, new Data(value));
  }
  /** SetKey helper for int parameter.
      @param key name of key to store parameter
      @param value int parameter to store at specified key
   */
  public void set (String key, int value) {
    set (key, new Data(value));
  }
  /** SetKey helper for state parameter (stored as int with 1 or 0).
      @param key name of key to store parameter
      @param value state parameter to store at specified key
   */
  public void set (String key, boolean value) {
    set (key, new Data(value?1:0));
  }

  /** GetKey for generic parameter. Override this method and call super if not handled.
      @param key name of key to get parameter value
      @param type data type of parameter
      @return Data parameter value at specified key
   */
  public Data get (String key, byte type) {
    Data value = null;
    if (key.startsWith("BOGUS"));
    else throw new MidasException ("Problem getting key="+key);
    return value;
  }
  /** GetKey helper for String parameter.
      @param key name of key to get parameter value
      @return String parameter value at specified key
   */
  public String getS (String key) {
    Data value = get(key,DataTypes.STRING);
    return value.toString();
  }
  /** GetKey helper for double parameter.
      @param key name of key to get parameter value
      @return double parameter value at specified key
   */
  public double getD (String key) {
    Data value = get(key,DataTypes.DOUBLE);
    return value.toD();
  }
  /** GetKey helper for float parameter.
      @param key name of key to get parameter value
      @return float parameter value at specified key
   */
  public float getF (String key) {
    Data value = get(key,DataTypes.FLOAT);
    return value.toF();
  }
  /** GetKey helper for int parameter.
      @param key name of key to get parameter value
      @return int parameter value at specified key
   */
  public int getL (String key) {
    Data value = get(key,DataTypes.LONG);
    return value.toL();
  }
  /** GetKey helper for state parameter.
      @param key name of key to get parameter value
      @return state parameter value at specified key
   */
  public boolean getState (String key) {
    Data value = get(key,DataTypes.LONG);
    return value.toL()>0;
  }

}
