/** @file
 * $Source: /afs/ba.infn.it/user/pamela/src/CVS/chewbacca/PamOffLineSW/techmodel/McmdReader.cpp,v $
 * $Id: McmdReader.cpp,v 1.4 2009/09/30 12:21:38 mocchiut Exp $
 * $Author: mocchiut $
 *
 * Implementation of the McmdReader class.
 */

extern "C" {
#include "CRC.h"
}
#include "ReaderAlgorithms.h"
using namespace pamela::techmodel;

/**
 * Constructor.
 */
McmdReader::McmdReader(void) :
  TechmodelAlgorithm(PacketType::Mcmd, "Mcmd") {
  Mcmd = new McmdEvent();
}

/**
 * Get a string with the version info of the algorithm.
 */
std::string McmdReader::GetVersionInfo(void) const {
  return "$Header: /afs/ba.infn.it/user/pamela/src/CVS/chewbacca/PamOffLineSW/techmodel/McmdReader.cpp,v 1.4 2009/09/30 12:21:38 mocchiut Exp $\n";
}

/**
 * Initialize the algorithm with a special run. This will initialize the
 * event reader routines for all packet types.
 */
void McmdReader::Init(PamelaRun *run) {
  // logger->debug(_T("Initialize"));
  // SetInputStream(run);
  run->WriteSubPacket(this, &Mcmd, Mcmd->Class());
}

/**
 * Unpack the Mcmd event
 *  Each subpacket is prafaceded by 4 OBT bytes.
 *  The elementar structure is a kind of
 * --------CPU - OBT---------------------------------
 *  OBT     - 4 Bytes
 * --------Start Sub-Packet---------------------------------
 *  SeqID  - 2 Bytes
 *  T          - 1 bit       | spare  - 7 bits | Mcmd-ID - 1 byte
 *  Spare  - 4 bits     | Mcmd Lenght 12 bits
 *  Time Tag      - 4 Bytes
 *  DATA            - McmdLength * 2 Bytes
 *  don't care    - 1 Byte
 *  END-ID         - 1 Byte is the sum of subPacket Bytes module 256
 * --------End Sub-Packet---------------------------------
 * --------CPU CRC-16 on OBT + subPacket---------------------------------
 * subCRC        - 2 Bytes
 * this structure is repeated one or more times.
 * The last 2 Bytes are CRC16 computed by CPU on all previous (OBT + subpacket data + subCRC) repetitions

 * @Event Number
 * @length is the size in bytes of the event (or packet)
 */
void McmdReader::PKT_RunEvent(char* mysubData, long int length) throw (WrongCRCException_PKTUsed) {
  stringstream oss;
  string msg;

  int i = 0;
  const int obt_len = 4;
  const int subHeader_len = 10;
  const int subTrailer_len = 2;
  const int subPckCRC_len = 2;
  long int dataLength = 0;

  char OBT[obt_len] = { 0 };
  char subHeader[subHeader_len] = { 0 };
  char subTrailer[subTrailer_len] = { 0 };
  char subPckCRC[subPckCRC_len] = { 0 };
  char* subData = NULL;

  UINT16 subCRC = 0; //CRC of the subpacket (updated as subPckt is read)
  UINT16 readCRC = 0; //CRC read from the subpacket
  UINT16 partialCRC = 0; //partial CRC updated as mcmd packet is read (to compare with CRC read in the total Mcmd header)
  McmdRecord *rec;

  Mcmd->Records->Clear();
  TClonesArray &recs = *(Mcmd->Records);

  int start = 0;
  // 	while(start < (length)) {
  // 	  printf(" %i => %X \n",start,(UINT8)mysubData[start]);
  // 	  start++;
  // 	};
  // 	start=0;

  while (start < (length - 2 - obt_len - subTrailer_len - subTrailer_len - subPckCRC_len))
  //	while(start < (length-2))
  {
    //    printf(" 1data %X \n",(UINT8)mysubData[start]);
    //Read the OBT preceeding the subpacket then calculate a partial CRC for it
    //and update the partialCRC
    for (int m = start; m < start + obt_len; m++) {
      OBT[m - start] = mysubData[m];
    }
    start += obt_len;

    //	  printf(" 2data %X \n",(UINT8)mysubData[start]);
    //	  printf(" 2data %X \n",(UINT8)mysubData[start]+1);
    subCRC = CM_Compute_CRC16(0, (BYTE*) &OBT, obt_len);
    partialCRC = CM_Compute_CRC16(partialCRC, (BYTE*) &OBT, obt_len);
    //Read the header for the subPacket and read mcmdLength
    //12 is the total lenght of subHeader + subTrailer
    for (int m = start; m < start + subHeader_len; m++) {
      subHeader[m - start] = mysubData[m];
    }
    start += subHeader_len;

    //	  printf(" 3data %X \n",(UINT8)mysubData[start]);
    //	  printf(" 3data %X \n",(UINT8)mysubData[start+1]);
    subCRC = CM_Compute_CRC16(subCRC, (BYTE*) &subHeader, subHeader_len);
    partialCRC = CM_Compute_CRC16(partialCRC, (BYTE*) &subHeader, subHeader_len);
    dataLength = (((0x0fff) & ((UINT16)subHeader[4])) << 8 | ((UINT16)subHeader[5]) * 2) - subHeader_len
        - subTrailer_len;
    //	    printf(" datalenght = %i  subh 4 %X subd 5 %X \n",dataLength,(UINT8)subHeader[4],(UINT8)subHeader[5]);
    //the line below is exactly how it was in the original version
    if (dataLength < 0)
      break; //it should throw an exception ***TBD***
    //I add also this check
    if (dataLength + start > (length - 2 - subTrailer_len - subPckCRC_len)) {
      oss.str("");
      oss << "MCMD: Error in the MCMD Packet lenght, it is not " << length;
      msg = oss.str();
      PamOffLineSW::mainLogUtil->logWarning(msg);
      break; //it should throw an exception ***TBD***
    }

    //read subpacket data according to data length then calculate partial CRC for data
    //and update the partialCRC

    subData = new char[dataLength];
    for (int m = start; m < start + dataLength; m++) {
      subData[m - start] = mysubData[m];
    }
    start += dataLength;

    //	  printf(" 4data %X \n",(UINT8)mysubData[start]);
    //	  printf(" 4data %X \n",(UINT8)mysubData[start+1]);
    for (int jj = 0; jj < dataLength; jj++) {
      subCRC = CM_Compute_CRC16(subCRC, (BYTE*) (subData + jj), 1);
      partialCRC = CM_Compute_CRC16(partialCRC, (BYTE*) (subData + jj), 1);
    }

    //Read the CRC inside of MCMD
    //To check sum of all sub packet data, module 256 ****TBD****
    for (int m = start; m < start + subTrailer_len; m++) {
      subTrailer[m - start] = mysubData[m];
    }
    start += subTrailer_len;
    //	   printf(" 5data %X \n",(UINT8)mysubData[start]);
    //	   printf(" 5data %X \n",(UINT8)mysubData[start+1]);

    subCRC = CM_Compute_CRC16(subCRC, (BYTE*) &subTrailer, 2);
    partialCRC = CM_Compute_CRC16(partialCRC, (BYTE*) &subTrailer, 2);
    //Read the CRC of subPacket
    for (int m = start; m < start + subPckCRC_len; m++) {
      subPckCRC[m - start] = mysubData[m];
    }
    start += subPckCRC_len;

    partialCRC = CM_Compute_CRC16(partialCRC, (BYTE*) &subPckCRC, 2);
    readCRC = (((BYTE)subPckCRC[0]) << 8) + ((BYTE)subPckCRC[1]);

    //finally check if the RecordCRC is correct
    //and finally update the partialCRC
    //TO DO - if one CRC is wrong also the total one will be corrupted
    //	   printf(" subCRC %X readCRC %X \n",subCRC,readCRC);
    //    if (subCRC == readCRC) { // BUG?! Emiliano 2009 10 01
      rec = new (recs[i++]) McmdRecord(); //aggiungo un nuovo McmdRecord all'evento
      rec->MCMD_RECORD_OBT = (((UINT32)OBT[0] << 24) & 0xFF000000) + (((UINT32)OBT[1] << 16) & 0x00FF0000)
          + (((UINT32)OBT[2] << 8) & 0x0000FF00) + (((UINT32)OBT[3]) & 0x000000FF);
      rec->SeqID = (((BYTE)subHeader[0] << 8) & 0xFF00) + (((BYTE)subHeader[1]) & 0x00FF);
      rec->Tbit = ((BYTE)((subHeader[2] & 0x80)) >> 7);
      rec->ID1 = (BYTE)subHeader[3];
      rec->McmdLength = (0x0fff) & (((UINT16)(subHeader[4] << 8)) + ((UINT16)subHeader[5]));
      rec->TimeTag = (((UINT32)subHeader[6] << 24) & 0xFF000000) + (((UINT32)subHeader[7] << 16) & 0x00FF0000) + (((UINT32)subHeader[8]
          << 8) & 0x0000FF00) + (((UINT32)subHeader[9]) & 0x000000FF);
      rec->endID = (BYTE)subTrailer[1];

      rec->McmdData = new TArrayC(dataLength, subData);
      //     delete [] subData;
    if (subCRC == readCRC) {
      rec->Mcmd_Block_crc_ok = 1;
    }
    else {
      rec->Mcmd_Block_crc_ok = 0;
      oss.str("");
      oss << "Wrong CRC on Subpacket internal to MCMD Packet ";
      msg = oss.str();
      PamOffLineSW::mainLogUtil->logAll(msg);
    }

    delete[] subData;
  }

  if (start != length - 2) {
    oss.str("");
    oss << "MCMD: Error in the MCMD Packet lenght (start!=length-2), it is not " << length;
    msg = oss.str();
    PamOffLineSW::mainLogUtil->logWarning(msg);
  }

  //in the end compare the calculated partial CRC with the MCMD packet CRC
  readCRC = (((UINT16)(mysubData[length - 2] << 8)) & 0xFF00) + (((UINT16)mysubData[length - 1]) & 0x00FF);
  //readCRC = (((UINT16)(mysubData[length - 1]<<8))&0xFF00) + (((UINT16)mysubData[length])&0x00FF);
  //
  //	printf(" CICCIOEND readCRC %X partialCRC %X \n",(UINT16)readCRC,(UINT16)partialCRC);
  //	if(partialCRC != readCRC) throw WrongCRCException(" Wrong Global CRC for MCMD Packet ");
  if (partialCRC != readCRC) {
    Mcmd->Mcmd_crc_ok = 0;
    oss.str("");
    oss << "Wrong CRC for MCMD Packet: " << " CRC COMPUTED= " << partialCRC << " CRC READ=  " << readCRC;
    msg = oss.str();
    PamOffLineSW::mainLogUtil->logWarning(msg);
    throw WrongCRCException_PKTUsed(" Wrong CRC for MCMD Packet. ");
  } else {
    Mcmd->Mcmd_crc_ok = 1;
  }
}

