package nxm.sys.prim;

import java.util.List;

import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.Message;
import nxm.sys.lib.Primitive;
import nxm.sys.lib.Table;
import nxm.sys.lib.Time;

/**
  Takes input data from a file and outputs to a file/pipe.

  @author Beth Hassler
*/
public class sourcefile extends Primitive {

  /** time of first output element as a double */
  public double offout;
  /** index at which to read the input DataFile */
  public double off;
  /** element at which to stop */
  public double stop[];
  /** pause sourcefile routine */
  public int pause=0;
  /** sourcefile run mode */
  public int replay;
  /** start time of input DataFile */
  public double startTime;
  /** input DataFile */
  public DataFile hin;
  /** output DataFile */
  public DataFile hout;

  private Data dbi,dbo;
  private double dur, slip;
  private double[] start;
  private int red,tl;
  private float[] fbuf;
  private byte[] inbuf;
  private boolean selfpace=true, reset=false;
  private double time, tdelay, tlast=0;
  private int idx, pass=0;

  private static final String replayList="ONESHOT,WAITTOP,ONETOP,CONTINUOUS";
  private static final int R_ONESHOT=1,R_WAITTOP=2,R_ONETOP=3,R_CONT=4;

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

  public int open() {
    // Open input file
    hin = MA.getDataFile("IN");
    hin.open();
    selfpace = hin.isFile();

    // Open output file
    hout = MA.getDataFile("OUT",hin,0);
    hout.open();

    // Get parameters
    idx = 0;
    start = stop = new double[1];
    start[idx] = off = MA.getD("START", 0.0);
    stop[idx]  = MA.getD("STOP", hin.size);
    dur = MA.getD("DURATION", 0.0);
    red = MA.getL("REDUCTION", 1);
    slip = MA.getD("SLIP", 0.0);

    // Setup processing
    tl = MA.getL("/TL",4096);
    tdelay = tl * hin.getXDelta();
    inbuf = new byte[tl];
    dbi = hin.getDataBuffer(tl);
    dbo = hout.getDataBuffer(tl);
    startTime = hin.getXStart();

    replay = MA.getChoice("/REPLAY",replayList,1);
    if (replay==R_WAITTOP || (replay==R_ONETOP && pass==1)) setPause(1);

    return NORMAL;
  }

  /** Opens a new DataFile for input
   *  @param name the name of the new DataFile
   */
  public void openFile(String name) {
    if(verbose)System.out.println("Sourcefile: Opening file "+name);
    hin = new DataFile();
    hin.init(M,name);
    hin.open();
    selfpace = hin.isFile();
    idx = 0;
    start = stop = new double[1];
    start[idx] = off = 0.0;
    stop[idx]  = hin.size;
    tdelay = tl * hin.getXDelta();
    dbi = hin.getDataBuffer(tl);
    dbo = hout.getDataBuffer(tl);
    startTime = hin.getXStart();
  }

  public int process() {

    if (pause==1) return NOOP;

    int n = hin.read(dbi,off,tl);
    if (n==0) {
      pass += 1;
      if (replay==R_ONESHOT) return FINISH;
      else return RESTART;
    }
    if (n<0) {
      pass += 1;
      if (replay==R_ONESHOT) return FINISH;
      else return RESTART;
    }
    off += (double)n;
    if (reset || off>stop[idx]) {
      if (idx<start.length-1) idx+=1; else idx=0;
      if (reset) idx=0;
      off=start[idx];
      reset = false;
    }

    if (selfpace) {
      time=Time.current();
      while ( (time-tlast) < (tdelay-.001) ) {
        Time.sleep(0.001);
        time=Time.current();
      }
      tlast = time;
    }

    dbo = dbi;
    int j = hout.write(dbo, n); // Not used - remove ??
    offout = off*hin.getXDelta()+startTime;

    return NOOP;
  }

  /** Process a message */
  public int processMessage (Message msg) {
    if (msg.name.equals("OPENFILE")) {
      openFile((String)msg.data);
    } else if (msg.name.equals("CLOSEFILE")) {
      closeFile((String)msg.data);
    }
    return NORMAL;
  }

  public int close() {
    hin.close();
    hout.update();
    hout.close();
    return NORMAL;
  }

  /** Set pause state
   *  @param val the pause state
   */
  public void setPause (int val) {
    pause = val;
  }

  /** Set start offset
   *  @param val the start offset ??
   */
  public void setStart (double val) {
    start = new double[1];
    idx = 0;
    off = (val-startTime)/hin.getXDelta();
    offout = off*hin.getXDelta()+startTime;
    start[idx] = off;
  }

  /** Set stopping element
   *  @param val the stopping offset ??
   */
  public void setStop (double val) {
    stop = new double[1];
    idx = 0;
    if (val == -1.0) {
      stop[idx] = hin.size;
    } else {
      stop[idx] = (val-startTime)/hin.getXDelta();
    }
  }

  /** Sets starts and stops from a Table
   *  @param t Table with starts and stops
   */
  public void setStartStops (Table t) {
    int size = t.size();
    if (size>0) {
      start = new double[size];
      stop = new double[size];
      for (Table.Iterator ti=t.iterator(); ti.getNext();) {
        Table tmpt = t.getTable(ti.key);
        int indx = tmpt.getI("INDX");
        if (indx<size) {
          start[indx] = (tmpt.getD("START")-startTime)/hin.getXDelta();
          stop[indx]  = (tmpt.getD("STOP")-startTime)/hin.getXDelta();
        } else
          M.warning("Error in Sourcefile - index out of bounds");
      }
      reset = true;
    }
  }

  /** Set starts from list
   *  TODO: Not yet implemented
   *  @param v the list of starts
   */
  public void setStarts (List<?> v) {
    if (verbose) System.out.println("Sourcefile: TBD - STARTS: "+v.toString());
  }

  /** Set stops from list
   *  TODO: Not yet implemented
   *  @param v the list of stops
   */
  public void setStops (List<?> v) {
    if (verbose) System.out.println("Sourcefile: TBD - STOPS: "+v.toString());
  }

  /** Close input DataFile
   *  @param name not used ??
   */
  public void closeFile(String name) {
    hin.close();
  }

  public int restart() {
    super.restart();
    return NORMAL;
  }

  /** Get replay mode
      @return the replay mode
   */
  public int getReplay() { return replay; }

  /** Set replay mode
   *  @param replay the replay mode
   */
  public void setReplay (int replay) {
    this.replay = replay;
  }

}
