/** @file
 * $Source: /repository/PamOffLineSW/techmodel/McmdReader.cpp,v $
 * $Id: McmdReader.cpp,v 1.7 2008-03-06 18:33:36 messineo Exp $
 * $Author: messineo $
 * 
 * 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: /repository/PamOffLineSW/techmodel/McmdReader.cpp,v 1.7 2008-03-06 18:33:36 messineo 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-2-obt_len-subTrailer_len-subTrailer_len-subPckCRC_len)) 
	{
		//Read the OBT preceeding the subpacket then calculate a partial CRC for it
		//and update the partialCRC 
		for(int m=start; m<obt_len; m++){
		  OBT[m-start]=mysubData[m];
		}		
		start+=obt_len;
		
		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<subHeader_len; m++){
		   subHeader[m-start]=mysubData[m];
	  	}		
		start+=subHeader_len;
	
	    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;  
	    //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<dataLength; m++){
		   subData[m-start]=mysubData[m];
	     }
	   start+=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);
	   }
	
	   //Read the CRC inside of MCMD
	   //To check sum of all sub packet data, module 256 ****TBD****
	   for(int m=start; m<subTrailer_len; m++){
		   subTrailer[m-start]=mysubData[m];
	    }
	   start+=subTrailer_len;
	
	   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<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
	   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 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, 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);  

//	if(partialCRC != readCRC) throw WrongCRCException(" Wrong Global CRC for MCMD Packet ");
	if (partialCRC != readCRC)
	{
		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. ");
	}
}
