package nxm.sys.prim;

import nxm.sys.lib.BaseFile;
import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.Primitive;

/**
  Creates ramp data file.

  @author Jay Hallam
*/
public class ramp extends Primitive {

  private DataFile hcb;
  private Data data;
  private boolean hasMin, hasMax, cx, fp, useBounds;
  private double dMax, dMin, dWrap;
  private double dValue, dDelta;
  private float fValue, fDelta;
  private int lValue, lDelta;
  private short iValue, iDelta;
  private byte bValue, bDelta;
  private byte type;

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

  public int open() {

    // open output file
    hcb = MA.getDataFile("OUT","1000","SF",0);
    hcb.setFormat (MA.getS("FORM"),"S#,C#");
    hcb.setXStart (MA.getD("START"));
    hcb.setXDelta (MA.getD("DELTA"));
    hcb.setXUnits (MA.getL("UNITS"));
    hcb.setSize   (MA.getD("ELEM"));
    hcb.open(BaseFile.OUTPUT);

    // get inclusive min and exclusive max clip range
    type = hcb.getFormatType();
    fp   = (type==BYTE || type==INT || type==LONG);
    cx   = hcb.getSPA()==2;
    hasMin = MA.find("/MIN");
    hasMax = MA.find("/MAX");
    dMin   = MA.getD("/MIN", Data.getTypeMin(type));
    dMax   = MA.getD("/MAX", Data.getTypeMax(type)+(fp?1:0));
    dWrap  = dMax - dMin;

    // get data start, inc
    dValue = MA.getD("DATASTART");
    dDelta = MA.getD("DATAINC");
    if (dValue>=dMax) { M.warning("Limiting start="+dValue+" to max="+dMax); dValue=dMax; }
    if (dValue< dMin) { M.warning("Limiting start="+dValue+" to min="+dMin); dValue=dMin; }

    // force full slow branch
    useBounds = hasMin || hasMax || (fp && (dValue%1.0!=0 || dDelta%1.0!=0));
    if (useBounds) type = DOUBLE;

    // setup processing
    xfer = MA.getL("/TL",4096);
    data = hcb.getDataBuffer(xfer,type);
    todo(hcb.size);

    // prep format branches
    fValue = (float)dValue; fDelta = (float)dDelta;
    lValue = (int)dValue;   lDelta = (int)dDelta;
    iValue = (short)dValue; iDelta = (short)dDelta;
    bValue = (byte)dValue;  bDelta = (byte)dDelta;

    return NORMAL;
  }

  public int process() {

    int ndo = todo();
    if (ndo==0) return FINISH;

    // size data object for this pass
    data.setSize(ndo);

    // fill the buffer with data
    int i=0, j=0;
    if (useBounds) {
      double[] dBuf = data.castD(false);
      for (; i<ndo; i++) {
        dBuf[j++] = dValue;
        if (cx) dBuf[j++] = dValue;
        dValue += dDelta;
             if (dValue >= dMax) dValue -= dWrap;
        else if (dValue <  dMin) dValue += dWrap;
      }
      data.uncast(dBuf,true);
    }
    else if (type==DOUBLE) {
      double[] dBuf = data.castD(false);
      if (cx) for (; i<ndo; i++) { dBuf[j++] = dValue; dBuf[j++] = dValue; dValue += dDelta; }
      else    for (; i<ndo; i++) { dBuf[i]   = dValue;                     dValue += dDelta; }
      data.uncast(dBuf,true);
    }
    else if (type==FLOAT) {
      float[] fBuf = data.castF(false);
      if (cx) for (; i<ndo; i++) { fBuf[j++] = fValue;  fBuf[j++] = fValue; fValue += fDelta; }
      else    for (; i<ndo; i++) { fBuf[i]   = fValue;                      fValue += fDelta; }
      data.uncast(fBuf,true);
    }
    else if (type==LONG) {
      int[] lBuf = data.castL(false);
      if (cx) for (; i<ndo; i++) { lBuf[j++] = lValue;  lBuf[j++] = lValue; lValue += lDelta; }
      else    for (; i<ndo; i++) { lBuf[i]   = lValue;                      lValue += lDelta; }
      data.uncast(lBuf,true);
    }
    else if (type==INT) {
      short[] iBuf = data.castI(false);
      if (cx) for (; i<ndo; i++) { iBuf[j++] = iValue;  iBuf[j++] = iValue; iValue += iDelta; }
      else    for (; i<ndo; i++) { iBuf[i]   = iValue;                      iValue += iDelta; }
      data.uncast(iBuf,true);
    }
    else if (type==BYTE) {
      byte[] bBuf = data.castB(false);
      if (cx) for (; i<ndo; i++) { bBuf[j++] = bValue;  bBuf[j++] = bValue; bValue += bDelta; }
      else    for (; i<ndo; i++) { bBuf[i]   = bValue;                      bValue += bDelta; }
      data.uncast(bBuf,true);
    }

    // write the data
    hcb.write(data);

    return NORMAL;
  }

  public int close() {
    hcb.close();
    return NORMAL;
  }

}
