package nxm.sys.lib;

import nxm.sys.inc.DataTypes;

/**
  Structure for a MPU Native Library

  Native code libraries should have the following methods:
    alloc()  -  allocates the handle to an instance of this library
    setkey() -  sets a parameter
    getkey() -  gets a parameter
    init()   -  initialize the library for the current set of parameters
    work()   -  perform the desired function on a segment of data
    free()   -  release all resources

  @author Jeff Schoen
*/
public class MPULibNative extends MPULib {

  protected long handle;

  /** allocate memory
   *  @return pointer to allocated memory
   */
  public native long alloc();
  /** set key
  *
  *   @param handle pointer to memory
  *   @param key   key
  *   @param value value of key (return value)
  *   @param len   length
  *   @return 0 (generally)
  */
  public native int setkey (long handle, String key, byte[] value, int len);
  /** get key
   *
   *  @param handle pointer to memory
   *  @param key   key
   *  @param value value of key (buffer to be set)
   *  @param len   length
   *  @return 0 (generally)
   */
  public native int getkey (long handle, String key, byte[] value, int len);
  /** Initialize this engine with current parameters
   *
   * @param handle pointer to memory
   * @return 0 (generally)
   */
  public native int init (long handle);
  /** work
   *
   *  @param handle pointer to memory
   *  @param n number to process
   *  @return number processed (or 0)
   */
  public native int work (long handle, int n);
  /** work on 1 buffer
   *
   *  @param handle pointer to memory
   *  @param buf1   buffer
   *  @param n1     number in buffer to process
   *  @return number processed (or 0)
   */
  public native int work1 (long handle, byte[] buf1, int n1);
  /** work on 2 buffers
   *
   *  @param handle pointer to memory
   *  @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 native int work2 (long handle, byte[] buf1, int n1, byte[] buf2, int n2);
  /** work on 3 buffers
   *
   *  @param handle pointer to memory
   *  @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 native int work3 (long handle, byte[] buf1, int n1, byte[] buf2, int n2, byte[] buf3, int n3);
  /** free the allocated memory
   *
   *  @param handle pointer to memory
   *  @return 0 (generally)
   */
  public native int free (long handle);

  /** Create an instance of this function with native implementation */
  public MPULibNative () {
    loadNative(getClass().getName());
  }
  /** Create an instance of this function with native implementation
   *
   *  @param name library name
   */
  public MPULibNative (String name) {
    loadNative(name);
  }

  /** load Native code
   *  @param libName library name
   */
  public void loadNative (String libName) {
    boolean ok = Shell.loadLibrary(libName);
    if (!ok) throw new MidasException ("Problem with load of native library: "+libName);
    handle = alloc();
    if (handle==0) throw new MidasException ("Problem with alloc of native library: "+libName);
  }

  public void set (String key, Data value) {
    char ctype = (char)value.type;
    if (Data.isString(value.type)) ctype=DataTypes.STRING;
    int len = setkey (handle,ctype+":"+key,value.buf,value.size*value.bpe);
    if (len<=0) super.set(key,value);
  }

  public Data get (String key, byte type) {
    byte[] buffer = new byte[256];
    char ctype = (char)type;
    int len = getkey (handle,ctype+":"+key,buffer,buffer.length);
    if (type==DataTypes.STRING) {
      type = DataTypes.ASCII;
      for (;len%8!=0;) buffer[len++]=0;
    }
    if (len>0) return new Data(buffer,0,len,type,1);
    else return super.get(key,type);
  }

  public int open() {
    return init(handle);
  }

  public int process(int n) {
    return work(handle,n);
  }
  public int process(byte[] buf1, int n1) {
    return work1(handle,buf1,n1);
  }
  public int process(byte[] buf1, int n1, byte[] buf2, int n2) {
    return work2(handle,buf1,n1,buf2,n2);
  }
  public int process(byte[] buf1, int n1, byte[] buf2, int n2, byte[] buf3, int n3) {
    return work3(handle,buf1,n1,buf2,n2,buf3,n3);
  }

  public int close() {
    return free(handle);
  }

}
