/** @file
 * $Source: /home/cvsmanager/yoda/techmodel/McmdReader.cpp,v $
 * $Id: McmdReader.cpp,v 3.0 2005/03/04 15:54:11 kusanagi Exp $
 * $Author: kusanagi $
 * 
 * Implementation of the McmdReader class.
 */

#define BYTE unsigned char
#include <string>
#include <log4cxx/logger.h>
extern "C" {
#include <sys/time.h>
#include "CRC.h"
}

#include <fstream>
#include "stdio.h"
#include "ReaderAlgorithms.h"

#include "event/mcmd/McmdRecord.h"

using namespace pamela;
using namespace pamela::techmodel;

static log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(_T("pamela.techmodel.McmdReader"));
static std::stringstream oss;
/**
 * Constructor. 
 */
McmdReader::McmdReader(void): 
  TechmodelAlgorithm(PacketType::Mcmd, "Mcmd") { 
  logger->debug(_T("Constructor"));
  Mcmd = new McmdEvent();
}

/**
 * Get a string with the version info of the algorithm.
 */
std::string McmdReader::GetVersionInfo(void) const {
  return 
    "$Header: /home/cvsmanager/yoda/techmodel/McmdReader.cpp,v 3.0 2005/03/04 15:54:11 kusanagi 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 from an input file.
 *  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::RunEvent(int EventNumber, long int length) throw (WrongCRCException){
  
  int i = 0;
  char OBT[4];
  char subHeader[10];
  char *subData;
  char subTrailer[2];
  char subPckCRC[2];
  char eventCRC[2];
  long int start;
  unsigned short headerCRC;
  UINT16    subCRC;      //CRC of the subpacket (updated as subPckt is read)
  UINT16    readCRC;     //CRC read from the subpacket
  UINT16    partialCRC;  //partial CRC updated as mcmd packet is read (to compare with CRC read in the total Mcmd header)

  long int lastPosition, dataLength;
  McmdRecord *rec;

  //the 2 bytes subtracted belong to the final event CRC bytes
  lastPosition = (long int)InputFile->tellg() + length - 2;
  Mcmd->Records->Clear();
  TClonesArray &recs = *(Mcmd->Records);
  partialCRC = 0;
  while(InputFile->tellg() < lastPosition) {

//Just to test crc
/*  char tempbuff[length];
  InputFile->read(tempbuff, sizeof(tempbuff));
  subCRC = 0;
  for (int jj = 0; jj < length ; jj++){
    subCRC = CM_Compute_CRC16(subCRC, (BYTE*)(tempbuff+jj), 1);
  }*/

    //Read the OBT preceeding the subpacket then calculate a partial CRC for it
    //and update the partialCRC
    start = InputFile->tellg();
    InputFile->read(OBT, sizeof(OBT));
    subCRC = CM_Compute_CRC16(0, (BYTE*)&OBT, 4);
    partialCRC = CM_Compute_CRC16(partialCRC, (BYTE*)&OBT, 4);
    
    //Read the header for the subPacket and read mcmdLength 
    //12 is the total lenght of subHeader + subTrailer
    InputFile->read(subHeader, sizeof(subHeader));
    subCRC = CM_Compute_CRC16(subCRC, (BYTE*)&subHeader, 10);
    partialCRC = CM_Compute_CRC16(partialCRC, (BYTE*)&subHeader, 10);
    dataLength = (((0x0fff)&((UINT16)subHeader[4]))<<8|((UINT16)subHeader[5])*2) - 12;

    if (dataLength < 0) break; //it should throw an exception ***TBD***
    //read subpacket data according to data length then calculate partial CRC for data
    //and update the partialCRC
    
    // If dataLength > 0 reads the data and compute again a partial CRC
    // else set subData to 0
    //if(dataLength > 0){
        subData = new char[dataLength];
        InputFile->read(subData, sizeof(unsigned char)*dataLength);
        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);
        }
        //subCRC = CM_Compute_CRC16(subCRC, (BYTE*)&subData, dataLength);

    //} else {
    //    subData = "0";
    //}
    
    //Read the CRC inside of MCMD
    //To check sum of all sub packet data, module 256 ****TBD****
    InputFile->read(subTrailer, sizeof(subTrailer));
    subCRC = CM_Compute_CRC16(subCRC, (BYTE*)&subTrailer, 2);
    partialCRC = CM_Compute_CRC16(partialCRC, (BYTE*)&subTrailer, 2);

    //Read the CRC of subPacket
    InputFile->read(subPckCRC, sizeof(subPckCRC));
    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
    if (subCRC == readCRC){
        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)OBT[6]<<24)&0xFF000000) + (((UINT32)OBT[7]<<16)&0x00FF0000) +  (((UINT32)OBT[8]<<8)&0x0000FF00) + (((UINT32)OBT[9])&0x000000FF);
        rec->endID           = (BYTE)subTrailer[1];

        rec->McmdData        = new TArrayC(dataLength, subData);
        delete [] subData;
    } else {
            oss.str("");
            oss << "Wrong CRC on Subpacket internal to TMTC Packet starting at position"
                << start;
            logger->warn(oss.str().c_str());
    }
  }
    //in the end compare the calculated partial CRC with the MCMD packet CRC
    InputFile->read(eventCRC, sizeof(eventCRC));
    readCRC = (((UINT16)(eventCRC[0]<<8))&0xFF00) + (((UINT16)eventCRC[1])&0x00FF);
    
    if(partialCRC != readCRC) throw WrongCRCException(" Wrong Global CRC for MCMD Packet ");
    /*if(!(partialCRC == readCRC)) {
        throw WrongCRCException();
    }*/
}


