package nxm.sys.prim;

import nxm.sys.inc.DataTypes;
import nxm.sys.inc.PacketHandler;
import nxm.sys.lib.BaseFile;
import nxm.sys.lib.Convert;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.Primitive;
import nxm.sys.lib.TextFile;
import nxm.sys.libm.Tolerance;

/**
  Lists Midas data on the terminal screen. See nxm.sys.exp.List

  @author Jeff Schoen
*/
public class datalist extends Primitive implements DataTypes {

  private BaseFile         bf;
  private double           start;          // start position
  private double           size;           // number of elements to list
  private int              flags;
  private int              ncols;
  private PacketHandler    pkh;
  private boolean          absc;
  private boolean          showLN;          // show the line number
  private String           form;
  private static final int DEF_SIZE = 32;   // default size (added in 2.9.0)

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

  @Override
  @SuppressWarnings("deprecation")  // for M.terminal - since 3.7.1 for NXM-2813
  public int open () {
    // open input file
    bf = MA.getFile("FILE");
    if (!bf.open()) return FINISH;

    // optional header prints
    if (MA.getState("/HS")) M.println( bf.toString() );        // header string
    if (MA.getState("/H")) M.print( bf.listHeader() );         // since 3.7.1 use M.print instead of terminal.write to allow logging of interactive commands NXM-2813
    if (MA.getState("/K")) M.print( bf.listKeywords() );

    absc  = MA.getState("ABSC");  // AB,ABS,and ABSC all count as TRUE (see Args.java String trueStates)

    // get universal parameters
    String startStr = MA.getS("START");
    if (startStr.startsWith("~")) {
      absc = true;
      startStr = startStr.substring(1);
    }

    // Bug 2210: Start positions should be whole numbers for index mode
    start = Convert.s2d(startStr,M);
    if (!absc && (start % 1) != 0) {
      M.warning("Fractional start position '"+start+"' not allowed for index mode. Using start of "+Math.round(start));
      start = Math.round(start);
    }

    int defSize = DEF_SIZE;
    if (start < 0) {  // 'tail' end of data
      if (bf.isStreaming()) M.error("Use of START="+start+" Not supported for pipes.");
      defSize = (int)Math.ceil(-start); // if NELEM not given and START=-N, show all elements to EOF
      if (bf instanceof TextFile) {   // TextFiles getSize returns bytes so we get the line count
        start = ((TextFile)bf).getNumberOfRows() + start;
        ((TextFile)bf).seek(0);  // have to put the seek pointer back because getNumberOfRows() reads the file
      } else  start = bf.getSize() + start;
    }
    size = (MA.isPresent("NELEM"))? MA.getL("NELEM"): defSize;
    // size  = MA.getL("NELEM");   // changed to lines above per Bug 2209

    // handle abscissa starts (BUG 2210)
    if (absc && bf instanceof DataFile ) {
      DataFile df = (DataFile) bf;
      double remainder = start % 1;
      double delta = df.getDelta();
      if ( (remainder % delta) > Tolerance.getTolerance())  start -= (remainder % delta);   // keep start on a delta value
      start = df.getIndex(start,DataFile.JSTART,MA.getAddressing());
    }

    form  = MA.getS("FORM");

    if (form.equals("")) form=null;
    showLN = MA.getState("/L",true);

    if (bf instanceof DataFile) {
      DataFile df = (DataFile)bf;
      if (form!=null) df.setFormat(form);
      pkh = df.getPacketHandler();
      form=null;
      bf.seek(start);
      if (size<0) size = (int)bf.avail();
      else size = Math.min(size,(int)bf.avail());
    } else {
      if (size<0) size = 10000;  //TODO: This should probably be bf.avail() to be consistent...
    }

    // get the format radix
    if (MA.getState("/HEX")) flags |= BaseFile.RADIX_16;
    else if (MA.getState("/BIN")) flags |= BaseFile.RADIX_2;

    // setup processing buffer
    int tw = MA.getL("/SW", M.terminal.getColumns());
    if (MA.isPresent("/NW")) {
      M.deprecate("Use /SW switch to indicate screen width");
      tw = MA.getL("/NW",tw);
    }
    ncols = bf.listElementsPerLine(tw-(showLN?6:1),form,flags);
    ncols = MA.getL("/NC",ncols);
    ncols = Math.max(1,ncols);

    return NORMAL;
  }

  @Override
  public int process () {

    int nelem = (int)Math.min(ncols,size);

    nelem = (int)Math.min(nelem, bf.getSize()-start);
    // read a line and format it according to file type
    String line = null;
    if (nelem > 0) line = bf.listElements (start,nelem,form,flags);
    if (line==null) return FINISH;

    // list the packet header read with this block of data
    if (pkh!=null && pkh.hasHeader() && MA.getState("/P")) M.println( pkh.listHeader() );

    // prepend line number and list the line of data
    M.print( padi(start)+line );

    start += nelem;
    size -= nelem;

    if (size<=0) return FINISH;
    else return NORMAL;
  }

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

  /** Pad an int type, cast from a double, just add a : if too large */
  private String padi (double start) {
    if (!showLN) return " ";
    String pad="";
    int off = (int)start;
         if (off<10)  pad="  ";
    else if (off<100) pad=" ";
    return pad+off+": ";
  }

}
