package nxm.dsp.prim;

import nxm.sys.inc.Units;
import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.DataOp;
import nxm.sys.lib.Primitive;

/**
  Performs Frame Synchronous Video Integration functions.

  @author Jeff Schoen
  @since NeXtMidas 3.1.0
*/
public class fsvi extends Primitive {

  /** List of modes */
  public static final String modeList = "Integrate,Average,Exponetial,Maximum";
  /** Integrate Mode   */ public static final int M_INT = 1;
  /** Average Mode     */ public static final int M_AVG = 2;
  /** Exponential Mode */ public static final int M_EXP = 3;
  /** Maximum Mode     */ public static final int M_MAX = 4;

  private DataFile hi,ho;
  private Data dbi,dbo,dbf;

  private int mag;      // Compute magnitude (1), mag squared (2), or none (0)
  private boolean in2d; // input is a type 2000 file
  private boolean cxm;  // input is a complex file
  private boolean out1d;// output is a type 1000 file

  private int mode;     // integration mode
  private int frame;    // frame size
  private int nblk,iblk;// frame block factor
  private double pri;   // pulse repetition rate
  private double delx;  // sample time delta
  private double size;  // file size
  private double factor;// averaging factor

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

  public int open () {

    hi = MA.getDataFile("IN", "1000,2000", "S#,C#", 0);
    hi.open(DataFile.WARNEMPTY);

    in2d = (hi.typeClass==2);
    cxm  = (hi.spa==2);
    delx = hi.getXDelta();
    size = hi.getSize();

    nblk = MA.getL("BLOCKS");
    pri  = MA.getD("PRI");
    mode = MA.getSelectionIndex("MODE",modeList,0);
    mag  = MA.getL("/MAG",cxm?1:0);
    out1d= MA.getState("/1D",(nblk<0));

         if (pri>0) frame = (int)Math.round(pri/delx);
    else if (pri<0) frame = (int)Math.max(1.0,-pri);
    else if (in2d)  frame = hi.getSubSize();
    else M.error("Illegal PRI value = "+pri);

    if (in2d) size *= frame;
    if (nblk<0) nblk = (int)Math.floor(size/frame);
    factor = 1.0/nblk;
    int nblko = (mode==M_EXP)? 1:nblk;
    size = Math.floor(size/frame/nblko);

    ho = MA.getDataFile("OUT",hi,0);
    if (in2d) {
      ho.setYDelta(hi.getYDelta()*nblko);
    } else {
      ho.setType(2000);
      ho.setSubSize(frame);
      ho.setYStart(hi.getXStart());
      ho.setYDelta(delx*frame*nblko);
      ho.setYUnits(Units.TIME);
      ho.setXStart(0.0);
    }
    ho.setSize(size);
    byte dtype = FLOAT;
    ho.setFormatType(dtype);
    if (mag>0) ho.setFormatMode(SCALAR);
    ho.open(DataFile.WARNEMPTY);

    int fsi = (in2d && frame==hi.getSubSize())? 1 : frame;
    dbi = hi.getDataBuffer(fsi,dtype);
    dbo = ho.getDataBuffer(1,dtype);
    dbf = ho.getDataBuffer(1,dtype);

    iblk=1;
    return NORMAL;
  }

  public int process () {

    int n = hi.read(dbi);
    if (n<0) return FINISH; // EOF

         if (mag==2) DataOp.mag2(dbi,dbf);
    else if (mag==1) DataOp.mag(dbi,dbf);
    else dbf = dbi;

    if (mode==M_EXP && iblk>1) {
      DataOp.scale(dbf,dbf,frame,factor);
      DataOp.scale(dbo,dbo,frame,1.0-factor);
    }

    if (iblk==1) DataOp.copy(dbf,dbo);
    else if (mode==M_MAX) DataOp.max(dbf,dbo,dbo);
    else DataOp.add(dbf,dbo,dbo);

    if (++iblk > nblk) {
      if (mode==M_AVG) DataOp.scale(dbo,dbo,frame,factor);
      ho.write (dbo);
      if (mode!=M_EXP) iblk=1;
    }
    return NORMAL;
  }

  public int close () {
    if (out1d) ho.setFS(0);
    hi.close();
    ho.close();
    return NORMAL;
  }

  /** get the number of input frames to block average for each output frame.
      @return number of input frames to block average
   */
  public int getBlock () { return nblk; }

  /** get the fractional overlap, zero for no overlap, negative to skip.
      @return pulse repetition rate
   */
  public double getPRI () { return pri; }

  /** set the number of input frames to block average for each output frame.
      @param value number of input frames to block average
   */
  public void setBlock (int value) {
    nblk = value;
    factor = 1.0/nblk;
  }

  /** set the fractional overlap, zero for no overlap, negative to skip.
      @param value new pulse repetition rate
   */
  public void setPRI (double value) {
    if (value==pri) return;
    MA.put("PRI",""+value);
    setState(RESTART);
  }

}
