package nxm.dsp.prim;

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

/**
  Fits a morphological filter to series of data frames (or just 1)

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

  // this list must match the enumeration in the Morph library
  /** List of available Morphological operations */
  public static final String operList =
    "NOOP,MIN,MAX,MEAN,MEDIAN,MMIN,MMAX,OPEN,CLOSE,ANGOPEN,ANGCLOSE";
  private static final int O_NOOP=0,O_MIN=1,O_MAX=2,O_MEAN=3, O_MEDIAN=4,
    O_MMIN=5,O_MMAX=6,O_OPEN=7,O_CLOSE=8,O_ANGOPEN=9,O_ANGCLOSE=10;

  DataFile hi,ho;
  Data dbi,dbo;
  int oper,width1,width2,width,hwidth,cons,boff,iter,ndata;
  double alpha;
  double[] vt;

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

  public int open() {

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

    oper = MA.getSelectionIndex("OPER",operList,0,-1);

    if (MA.getState("ADDR")) {
      double xd = hi.getXDelta();
      width1 = (int)Math.round( MA.getD("WIDTH1") / xd);
      width2 = (int)Math.round( MA.getD("WIDTH2",MA.getD("WIDTH1")) / xd);
    } else {
      width1 = MA.getL("WIDTH1");
      width2 = MA.getL("WIDTH2",width1);
    }
    iter   = MA.getL("ITER");
    alpha  = MA.getD("ALPHA");

    ho = MA.getDataFile ("OUT",hi,0);
    ho.open(BaseFile.OUTPUT|BaseFile.OPTIONAL);

    if (hi.typeClass==1) {
      ndata = (int)hi.getSize();
      xfer = MA.getL("/TL",Math.min(32768,ndata));
    } else {
      ndata = hi.getSubSize();
      xfer = ndata;
      hi.setDFS(0);
      ho.setDFS(0);
    }

    width = Math.max(width1,width2); hwidth = width/2;
    cons = (xfer<ndata)? xfer-width : -1;
    hi.setXferLength(xfer);
    hi.setConsLength(cons);
    dbi = hi.getDataBuffer(xfer,Data.DOUBLE);
    dbo = ho.getDataBuffer(xfer,Data.DOUBLE);
    vt = new double[xfer];

    return NORMAL;
  }

  public int process() {

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

    // easy out for NOOP operations
    if (oper==O_NOOP || (width1==0 && width2==0)) {
      ho.write(dbi);
      return NORMAL;
    }

    double[] vi = dbi.castD(true);
    double[] vo = dbo.castD(false);

    switch (oper) {
      case O_MIN:    Morph.min (vi,vo,n, width1); break;
      case O_MAX:    Morph.max (vi,vo,n, width1); break;
      case O_MEAN:   Morph.mean (vi,vo,n, width1); break;
      case O_MEDIAN: Morph.median (vi,vo,n, width1); break;
      case O_MMIN:   Morph.mean (vi,vt,n, width1);
                     Morph.min (vt,vo,n, width2); break;
      case O_MMAX:   Morph.mean (vi,vt,n, width1);
                     Morph.max (vt,vo,n, width2); break;
      case O_OPEN:   Morph.min (vi,vt,n, width1);
                     Morph.max (vt,vo,n, width2); break;
      case O_CLOSE:  Morph.max (vi,vt,n, width1);
                     Morph.min (vt,vo,n, width2); break;
    }

    dbi.uncast(vi,false);
    dbo.uncast(vo,true);

    if (cons>0) {
      dbo.boff = boff*dbo.bpe;    // where ho.write() should start
      n -= boff;      // skip front half if boff != 0
      ndata -= cons;      // how many samples left
      if (ndata>0) n -= hwidth;    // skip back half if not finished
      ho.write(dbo,n);      // write good data samples
      dbo.boff = 0;      // reset for regular operation
      boff = hwidth;      // offset past initial transient
    } else {
      ho.write(dbo);
    }

    return NORMAL;
  }

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

}
