package nxm.sys.prim;

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

/**
 Splits type-3000 files into multiple type-1000 files

 Note that this command needs to work around the somewhat spotty system
 support for type-3000 files, primarily by passing everything through a
 big byte buffer in the middle and by careful fiddling with the header.
 As a result, system library changes are rather likely to break this
 command until better library support is available (DR #4874).

 @author Ken Smith
*/
public class split extends Primitive  {

    private DataFile hi;
    private Data data_in;

    private DataFile[] ho;    // We need one output header per record,
    private Data[] data_out;  // similarly for buffers, names, etc
    private String[] formats;
    private String[] names;
    private int[] lengths, offsets, out_types;

    private String root;
    private int num_subrec, elts, recLength, last;
    private double dp_elts;

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

    public int open () {
      int i;
      double old_size;

      // Get command line parameter
      root = MA.getS("ROOT");

      // Open file
      hi = MA.getDataFile ("IN");
      hi.open();

      // Only read one record at a time (this is set by our buffer size)
      recLength = hi.getRecLength();

      num_subrec = hi.getSubSize();
      ho         = new DataFile[num_subrec];
      data_out   = new Data[num_subrec];
      formats    = new String[num_subrec];
      names      = new String[num_subrec];
      lengths    = new int[num_subrec];
      offsets    = new int[num_subrec];
      out_types  = new int[num_subrec];

      // Open our output files
      for (i = 0; i < num_subrec; i++) {
        formats[i] = hi.getRecFormat(i);
        names[i]   = hi.getRecName(i);
        offsets[i] = hi.getRecOffset(i);
        // The subrecord lengths are only stored implicitly, in the offsets

        if (i+1 < num_subrec) {
          lengths[i] = hi.getRecOffset(i+1) - offsets[i];
        } else {
          lengths[i] = recLength - offsets[i];
        }

        // See if output file should be type 1000 or 2000 (there is no
        //   way to split out a type 3000 file, you have to use GROUP
        //   on the results)
        // We just distinguish on length, one element => type 1000,
        //   more => type-2000

        dp_elts = lengths[i]/Data.getBPA(formats[i]);
        elts = (int) Math.rint(dp_elts);
        if ( Math.abs( elts - dp_elts ) > 1e-6 ) {
          M.warning("Invalid length for subrecord " + i);
        }
        if (elts == 1) {
          out_types[i] = 1000;
        } else {
          out_types[i] = 2000;
        }

        ho[i] = new DataFile();
        // If the subrecord names are less than 4 characters,
        // strip off trailing blanks and check the total length
        last = names[i].indexOf(" ");
        if (last < 0) {
          last = names[i].length(); // If no space, use all characters
        }
        String name = root + names[i].substring(0, last);

        ho[i].init(M, name.toLowerCase() );
        ho[i].setType(1000);
        ho[i].setFormat("SB");
        ho[i].setXStart(0.);
        ho[i].setXDelta(1.);

        ho[i].open(BaseFile.OUTPUT);

        // Create our data buffers

        data_out[i] = new Data("SB", lengths[i]);
      }
      data_in = new Data("SB", recLength);

      // Fake out the library routines (so much for encapsulation)

      old_size = hi.size;
      hi.setFormat("SB");  // Avoid reformats inside the read
      hi.setType(1000);
      hi.setFS(0);  // Cause typeClass to be set
      hi.setSize(old_size*recLength);

      return NORMAL;
    }

    public int process() {
      int i, j, offset;

      int n = hi.read(data_in, recLength);
      if (n==0) return NOOP;
      if (n<0) return FINISH;

      // Copy each input record into all of the various output records

      offset = 0;
      for (i = 0; i < num_subrec; i++) {
        offset = offsets[i];
        for (j = 0; j < lengths[i]; j++) {
          data_out[i].buf[j] = data_in.buf[j+offset];
        }
        ho[i].write(data_out[i], lengths[i]);
      }
      return NORMAL;
    }

    public int close() {
      double old_size, new_size;
      hi.close();
      for (int i = 0; i < num_subrec; i++) {
        old_size = ho[i].seek();
        ho[i].close();
        //
        // Things get funky in the libraries here
        // Need to reopen to set size to correct value
        //
        new_size = old_size/lengths[i];
        ho[i].open(BaseFile.INOUT);
        ho[i].setType(out_types[i]);
        if (out_types[i] == 2000) {
          ho[i].setSubSize(lengths[i]/Data.getBPA(formats[i]));
        }
        ho[i].setFormat(formats[i]);
        ho[i].setSize(new_size); // Correct size
        ho[i].update();
        ho[i].close();
      }
      return NORMAL;
    }
}
