package nxm.sys.test;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import nxm.sys.inc.InternalUseOnly;
import nxm.sys.lib.Convert;
import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.DataFileHeader;
import nxm.sys.lib.Keywords;
import nxm.sys.lib.Midas;
import nxm.sys.lib.Shell;

/**
 * This is a utility class for generating type 3000 files with unsigned data.
 * @author Kevin Esterheld/Jeanette McCall
 * @since 3.9.3
 */
@InternalUseOnly
public class DataUnsignedUtility {

  private static String AUX_1_PATH = "/midas/data1/"+System.getProperty("user.name");
  private static String KEYWORDS_FILE = AUX_1_PATH+"/unsigned_keywords.tmp";
  private static String RECORD_BASED_FILE = AUX_1_PATH+"/unsigned_values_3000.tmp";
  private String optionsSave;
  private Midas M;

  private static String datDir = DatLocator.getDatDirLocation();

  private DataUnsignedUtility () {
    this.M = Shell.getMidasContext();
  }

  private void setUp() {
    optionsSave = Shell.getMidasContext().io.getOptions();
    Shell.getMidasContext().io.setOptions("+MINIMALSUPPORTFORUNSUPPORTEDDATATYPES");
    Shell.runCommand(M, "WAVEFORM "+KEYWORDS_FILE+" ELEM=128");
  }

  private void tearDown () {
    Shell.getMidasContext().io.setOptions(optionsSave);
  }

  private void addViaDataFileHeader() {
    DataFile df = new DataFile(M, KEYWORDS_FILE);
    DataFileHeader dfh = new DataFileHeader(df);
    dfh.addToFile('V', Integer.MAX_VALUE+1, "VADDEDFROMDFH");
    dfh.addToFile('U', (short) (Short.MAX_VALUE+1), "UADDEDFROMDFH");

  }

  private void create3000File() {
    DataFile in = new DataFile(M,datDir+".test3000.prm");
    in.open();
    DataFile out = new DataFile(M, RECORD_BASED_FILE,in,DataFile.OUTPUT);
    out.open();
    Keywords kw = out.getKeywordsObject();
    kw.setKey("S:6000key", "6000 test value");
    byte[] value1 = new byte[4];
    byte[] value2 = new byte[4];
    Convert.packL(value1,0,Integer.parseUnsignedInt("4294967295"));
    Convert.packL(value2,0,Integer.parseUnsignedInt("1024"));
    byte[] values = new byte[(value1.length + value2.length)];
    System.arraycopy(value1, 0, values, 0, value1.length);
    System.arraycopy(value2, 0 , values, value1.length, value2.length);
    kw.set("V:CVKEY", Data.ULONG, values, values.length, 0);

    out.setSubRecords("(SR2A|2A,SRSL|SL,SRSU|SU,SRSV|SV,SRCV|CV)");
    out.setData (0, "SR2A", "FIRST");
    out.setData (0, "SRSL", 65536);
    writeOneElement(out,0,"SRSU",0,65535,M, 'I');
    writeOneElement(out,0,"SRSV",0,4294967295l,M, 'L');
    writeOneElement(out,0,"SRCV",0,4294967295l,M, 'L');
    writeOneElement(out,0,"SRCV",1,5,M, 'L');
    out.setData (1, "SR2A", "SECOND");
    out.setData (1, "SRSL", 32769);
    out.setData (1, "SRSU", 32768);
    out.setData (1, "SRSV", 2147483648l);
    writeOneElement(out,1,"SRCV",0,2147483648l,M, 'L');
    writeOneElement(out,1,"SRCV",1,4,M, 'L');
    out.close();
    in.close();
    out.close();
    DataFile df = new DataFile(M, RECORD_BASED_FILE);
    DataFileHeader dfh = new DataFileHeader(df);
    dfh.addToFile('V', (int) Integer.toUnsignedLong((int)4294967295l), "V:UINT");
    dfh.addToFile('U', (short) Short.toUnsignedInt((short)65535),"U:USHORT");

  }

  private static void writeOneElement (DataFile df, double offset, String fieldName, int elem, Number num, Midas M, char type) {
    // note: it might possibly be cleaner to use the new moveUnsignedIntoLargerSigned method in DataFile
    int fieldOffset = df.findRecOffset(fieldName);
    double dbpe = df.getBPE();
    offset += ((double)fieldOffset/dbpe);
    int offThisElem = Data.getBPS(type);
    offset += (offThisElem*elem/dbpe);
    if (offset!=df.getOffset()) df.seek(offset);

    String fieldFormat = "S" + type; //(char)df.getRecFormatType(fieldNum);
    Data   data        = new Data(fieldFormat,1,num);
    df.write(data.buf,0,data.buf.length);

  }

  private void manuallyAddKeysToFile() {
    File file = new File(KEYWORDS_FILE);
    File file2 = new File(KEYWORDS_FILE);
    FileInputStream fis;
    byte[] buf;
    try {
      fis = new FileInputStream(file);
      buf = new byte[(int) file.length()];
      fis.read(buf);
      fis.close();
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }

    buf = addToFile('U', (short) 65535, "testUDataType".toUpperCase(), buf);
    buf = addToFile('V', -1, "testVDataType".toUpperCase(), buf);

    try (FileOutputStream fos = new FileOutputStream(file2); BufferedOutputStream bos = new BufferedOutputStream(fos);){
      bos.write(buf);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private byte[] addToFile(char type, Object val, String name, byte[] buf) {
    int extStart = Convert.unpackL(buf, DataFile.OFF_EXTSTART);
    int extSize = Convert.unpackL(buf, DataFile.OFF_EXTSIZE);
    double dataStart = Convert.unpackD(buf,  DataFile.OFF_DATASTART);
    double dataSize = Convert.unpackD(buf,  DataFile.OFF_DATASIZE);
    byte[] data = packTestDataToBuffer(type,val);
    int off = ((extStart*512) + extSize);
    if(off==0) off = (int) (dataStart+dataSize);
    byte ltag = (byte)name.length();
    int len = data.length;
    int length = (len+ltag+15)/8*8;
    short lext = (short)(length - len);
    if (buf.length < (length+off)) {
      int size = buf.length;
      byte[] bufo = buf;
      buf = new byte[length+off];
      System.arraycopy(bufo, 0, buf, 0, size);
    }
    Convert.packL(buf, off+0, length);
    Convert.packI(buf, off+4, lext);
    buf[off+6] = ltag;
    buf[off+7] = (byte) type;
    System.arraycopy (data,0, buf,off+Keywords.HEADER_LEN, len);
    for (int i=0,j=off+Keywords.HEADER_LEN+len; i<ltag;) buf[j++] = (byte)name.charAt(i++); // set tag name
    Convert.packL(buf, DataFile.OFF_EXTSIZE, extSize+length);
    Convert.packL(buf, DataFile.OFF_EXTSTART, off/512);
    return buf;
  }

  private byte[] packTestDataToBuffer(char type, Object val) {
    byte[] res = new byte[type=='U' ? 2 : 4];
    if(type=='U') {
      Convert.packI(res, 0,(short) val);
    }
    else {
      Convert.packL(res, 0,(int) val);
    }

    return res;
  }

  /** Main driver for running DataUnsigned Utility as an instance
   *  @param args commandline arguments
   */
  public static void main(String[] args) {
    DataUnsignedUtility dut = new DataUnsignedUtility();
    dut.setUp();
    dut.create3000File();
    dut.tearDown();
  }
}