package nxm.sys.prim;

import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.FileName;
import nxm.sys.lib.Parser;
import nxm.sys.lib.Primitive;
import nxm.sys.lib.Time;

/**
  Copies data from one file to another.

  @author Jeff Schoen
*/
public class noop extends Primitive {

  /** list of throttle options for processing data */
  public  static final String throttleList = "RealTime,Full,Block";
  private static final int REALTIME=1,FULL=2,BLOCK=3;
  private DataFile hi,ho;
  private Data data;
  private boolean wrap=false;
  private int throttle;
  private double tdelta,tstart,offs,size,tcur,flush,tflush;
  private double rateWindow, rateWindowOffset=0;
  private static double RW_DEF=1.0, RW_MIN=0.01; 	// rate window default and minimum
  //do not propagate timeline keywords - in pipe situation, the propagation of the keywords
  // results in a TimeLine initialized based on the keywords and then added to by
  // PipeResource.propagateTimecode. For proper operation the timeline should be filled out
  // as each TL worth of data is received - 3.8.0
  private boolean noPropagationOfTimelineKeywords;

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

  @Override
  public int open() {

    hi = MA.getDataFile ("IN");
    hi.open();

    size = MA.getD("/SIZE");
    offs = MA.getD("/START",0.0);
    // allow a way to NOT propagate the timeline keywords
    noPropagationOfTimelineKeywords = MA.getState("/NOPROPAGATIONOFTIMELINEKEYWORDS");

    ho = MA.getDataFile ("OUT",hi,0);
    // allow a way to NOT propagate the timeline keywords
    if (noPropagationOfTimelineKeywords) ho.deleteTimeLineKeywords();
    if (size>0) ho.setSize(size);
    ho.open();

    setProgressFeed(hi,"NOOP "+hi.getURL()+" to "+ho.getURL());

    if (MA.find("/PACKET")) {	// extract packets
      FileName pfn = MA.getFileName("/PACKET");
      ho.getPacketHandler().setFileName(ho,pfn);
    }

    xfer = Math.max(1,(int)(bufSize/hi.dbpe));
    xfer = MA.getL("/TL",xfer);
    data = hi.getDataBuffer(xfer);
    wrap = MA.getState("/WRAP");

    tstart = 0;
    setRate( MA.getD("/RATE") );
    setRateWindow( MA.getD("/RATEWINDOW",RW_DEF) );
    throttle = MA.getState("/RT")? REALTIME : FULL;
    throttle = MA.getChoice("/THROTTLE",throttleList,throttle); // Override any /RT input
    flush = MA.getD("/FLUSH");

    if (offs>0) hi.seek(offs);
    if (hi.getFormatMode() != ho.getFormatMode()) {
      M.error("Mixed mode reformats are not supported: "+hi.getFormat()+" -> "+ho.getFormat());
    }
    return (NORMAL);
  }

  @Override
  public int process() {

    double tcur = (flush>0 || throttle==REALTIME)? Time.current() : 0;

    if (state==RESUME) resetWindow();
    if (throttle==REALTIME) {
      if (tstart==0 && hi.avail()<xfer) return (NOOP);
      if (tstart==0) tstart=tcur;
      if (tcur < (tstart+(ho.seek()-rateWindowOffset)*tdelta)) return (NOOP);
      if (tcur > (tstart+rateWindow)) resetWindow();
    }
    else if (throttle==BLOCK) return (NOOP);

    if (size>0 && ho.seek()>=size) return (FINISH);
    int n = hi.read(data);
    if (size>0 && n+ho.seek()>size) n=(int)(size-ho.seek());

    if (n>0) ho.write(data,n);
    else if (n==0) return (NOOP);
    else if (wrap && (hi.isFile() || hi.isJarFile())) hi.seek(0.0);
    else return (FINISH);

    if (flush>0 && tcur-tflush>flush) { ho.flush(); tflush=tcur; }
    return (NORMAL);
  }

  @Override
  public int close() {
    hi.close();
    ho.close();
    return (NORMAL);
  }

  /** set RealTime transfer delta in sec per sample
   *  @param value RealTime transfer delta in sec per sample */
  public void setTDelta (double value) {
    tdelta = value;
  }
  /** set RealTime transfer rate in bytes/sec
   *  @param value RealTime transfer rate in bytes/sec */
  public void setRate (double value) {
    if (value<=0) tdelta = hi.getDelta();
    else tdelta = hi.dbpe / value;
  }

  /** set RealTime throttle mode
   *  @param value RealTime throttle mode */
  public void setThrottle (String value) {
    throttle = Parser.find(throttleList,value,throttle,0,Parser.E_WARNING);
  }
  /** get RealTime throttle mode
      @return throttle mode
      @since 3.5.1
   */
  public String getThrottle () {
    return Parser.get(throttleList, throttle);
  }

  /** set RealTime window (in sec) over which the rate should be controlled
   *  @param value RealTime window (in sec) over which the rate should be controlled */
  public void setRateWindow (double value) {
    if (value < RW_MIN) {
      M.warning("Rate window reset to minimum value of "+RW_MIN+" seconds.");
      rateWindow = RW_MIN;
    } else {
      rateWindow = value;
    }
  }

  private void resetWindow() {
    tstart = Time.current();
    rateWindowOffset = ho.seek();
  }

}
