package nxm.dsp.prim;

import nxm.sys.lib.Convert;
import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.Keywords;
import nxm.sys.lib.Primitive;

/** A class to upright spectrum data if it is inverted (this is a NOOP if data is already upright).
    In response to SNAP_SPECTRAL_SENSE=INVERTED or
                   DATA_INVERSION_FLAG=1        or
                   /FORCEINVERSION=true). <br>
    The SNAP_SPECTRAL_SENSE keyword takes precedence over the DATA_INVERSION_FLAG
    keyword, and the switch /FORCEINVERSION=true takes precedence over the keywords.

    Note: Precedence switched and Platinum Keyword value corrected in 3.7.0

    @since NeXtMidas 3.5.4
 */
public class uprightspectrum extends Primitive {
  private DataFile inFile;              // The input file
  private DataFile outFile;             // The output file
  private Data     dataBuffer;          // The data buffer
  private boolean  invertData = false;  // Data inverted and needs up-righting (inverting)
//keyword name         , upright val, inverted val  (in order of highest to lowest precedence)

  static final String[][] KEYWORDS_VALUES = {
    // keyword name       ,type,upright val,inverted val  (in order of highest to lowest precedence)
    {"SNAP_SPECTRAL_SENSE","S:","NORMAL","INVERTED"     ,"NONE","INVALID"},  // 1. BLUE keyword
    {"DATA_INVERSION_FLAG","L:","0"     ,"1"            ,"2"   ,"-1"},       // 2. Platinum keyword (actually type L: in file) - corrected in 3.7.0
  };

  static final int KEYWORD_NAME  = 0;
  static final int KEYWORD_TYPE  = 1;
  static final int UPRIGHT_NAME  = 2;
  static final int INVERTED_NAME = 3;

  static final int BLUE_INDEX = 0;
  static final int PLATUNUM_INDEX = 1;

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

  @Override
  public int open() {
    inFile = MA.getDataFile("IN", "1000", "", 0); // only allow type 1000 files
    inFile.open();

    String keyword;
    String dataInversion;
    int kwNum = -1; // Set to -1, since it is pre-incremented
    // Check for the presence of keywords in precedence order
    do {
      keyword = KEYWORDS_VALUES[++kwNum][KEYWORD_NAME];
      dataInversion = Convert.o2s(inFile.getKeyWord(keyword));
    } while (dataInversion == null && kwNum < KEYWORDS_VALUES.length - 1);
    if (dataInversion != null && dataInversion.equals(KEYWORDS_VALUES[kwNum][INVERTED_NAME])) invertData = true;
    // Allow data to be inverted based on a switch, as the keyword may not always be present
    invertData = MA.getState("/FORCEINVERSION", invertData);

    outFile = MA.getDataFile("OUT", inFile, 0);
    outFile.open();

    // If we are uprighting for any reason, add a comment indicating data has been uprighted
    if (invertData) {
      Keywords kw = outFile.getKeywordsObject();
      kw.put("UPRIGHTSPECTRUM_COMMENT", "This data has been uprighted by the UPRIGHTSPECTRUM command");
      // If we are uprighting this data based on a keyword, replace the keyword
      if (invertData && dataInversion != null) {
        kw.put(KEYWORDS_VALUES[kwNum][KEYWORD_TYPE]+keyword, KEYWORDS_VALUES[kwNum][UPRIGHT_NAME]);
        kw.put(KEYWORDS_VALUES[kwNum][KEYWORD_TYPE]+"ORIG_"+keyword, KEYWORDS_VALUES[kwNum][INVERTED_NAME]);
      }

    }

    int xfer = MA.getL("/TL", 1024);
    dataBuffer = inFile.getDataBuffer(xfer);

    return NORMAL;
  }

  @Override
  public int process() {
    int status  = NORMAL;
    int numRead = inFile.read(dataBuffer);

    if (numRead < 0) {
      status = FINISH; // End of file
    } else if (numRead == 0) {
      status = NOOP;   // No data available, wait for pipe.
    } else {
      if (invertData) {
        invertData(numRead);    // invert the data
      }
      outFile.write(dataBuffer, numRead);
    }

    return status;
  }

  /** perform data inversion by multiplying every other value by -1 */
  private void invertData (int numRead) {
    float[] fbuf = dataBuffer.castF(true);
    for (int i=1; i<fbuf.length; i+=2)  fbuf[i] = -fbuf[i];
    dataBuffer.uncast(fbuf, true);
  }

  @Override
  public int close() {
    inFile.close();
    outFile.close();

    return NORMAL;
  }
}
