/** @file
 * $Author: messineo $
 * $Date: 2008-03-12 13:48:30 $
 * $Revision: 1.21 $
 * 
 * Implementation of the functions of a sample Algorithm class.
 * This file can be used as a templace to develop your own algorithm.
 */

#include "EventReader.h"
#include "ReaderAlgorithms.h"

extern "C" {
    #include "CRC.h"
}

using namespace pamela;
using namespace pamela::techmodel;

EventReader::~EventReader(){
	if(Header){delete Header; Header=NULL;}
} 

/**
 * Constructor. 
 */

EventReader::EventReader(): 
	TechmodelAlgorithm(0, "TechmodelEventReader"){
	Header = new EventHeader();  
	
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::PhysEndRun,      new PhysEndRunReader));  
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibCalPulse1,  new CalibCalPulse1Reader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibCalPulse2,  new CalibCalPulse2Reader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Physics,         new PhysicsReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibTrkBoth,    new CalibTrkBothReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibTrk1,       new CalibTrk1Reader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibTrk2,       new CalibTrk2Reader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibTof,        new CalibTofReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibS4,         new CalibS4Reader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibCalPed,     new CalibCalPedReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Calib1_Ac1,      new Calib1_Ac1Reader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Calib2_Ac1,      new Calib2_Ac1Reader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Calib1_Ac2,      new Calib1_Ac2Reader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Calib2_Ac2,      new Calib2_Ac2Reader));
  //TODO: marco maybe a bug?:  due volte Calib2_Ac2. ??
//  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Calib2_Ac2,      new CalibCalReader));
  //TODO: marco maybe a bug?  
  //	questa dovrebbe essere cosi ma poi c'e' un errore in Pamela Run: in WriteHeaders non ho CalibCal
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibCal,        new CalibCalReader));  
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::RunHeader,       new RunHeaderReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::RunTrailer,      new RunTrailerReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibHeader,     new CalibHeaderReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalibTrailer,    new CalibTrailerReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::InitHeader,      new InitHeaderReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::InitTrailer,     new InitTrailerReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::EventTrk,        new EventTrkReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Log,             new LogReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::VarDump,         new VarDumpReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::ArrDump,         new ArrDumpReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TabDump,         new TabDumpReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Tmtc,            new TmtcReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Mcmd,            new McmdReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::ForcedFECmd,     new ForcedFECmdReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Ac1Init,         new Ac1InitReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalInit,         new CalInitReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TrkInit,         new TrkInitReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TofInit,         new TofInitReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TrgInit,         new TrgInitReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::NdInit,          new NdInitReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::S4Init,          new S4InitReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Ac2Init,         new Ac2InitReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::CalAlarm,        new CalAlarmReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Ac1Alarm,        new Ac1AlarmReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TrkAlarm,        new TrkAlarmReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TrgAlarm,        new TrgAlarmReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TofAlarm,        new TofAlarmReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::S4Alarm,         new S4AlarmReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::Ac2Alarm,        new Ac2AlarmReader));  
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TsbT,            new TsbTReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TsbB,            new TsbBReader));
}

/**
 * Initialize the algorithm with a special run. This will initialize the
 * event reader routines for all packet types.
 */
void EventReader::Init(PamelaRun *run) {	
	//Create the structure of directories and create xxx.Header.root files
	  run->WriteHeaders(this, &Header);   
    //Create the xxx.root in it's specific directory
    for (AlgorithmMap::iterator i = TechmodelAlgorithmMap.begin(); 
       i != TechmodelAlgorithmMap.end(); i++) {
       // cout << " Initializing algo " << i->second->GetAlgorithmName()<<endl;
       i->second->Init(run);
    }

    if ( Header ) { delete Header; Header = new EventHeader(); };  // EMILIANO, reset Counter when opening a new file

    Run=run;
}


/**
 * Read the next event header, call the reader algorithms that
 * correspond to its packet type, and read the event trailer.
 * if the packet is saved return 0.
 * 
 * return values:      meaning:
		0            packet good
	    1            exception but used    //never happen
	    2    		 CRC exception but used
	    3    		 CALIBRATION PACKET with error but used
	   -1    		 CRC exception  packet DISCARDED
	   -2    		 FATAL exception packet DISCARDED //never happen
	   -3    		 No way to read events of this type. packet DISCARDED( es CalibCal)
	//in PacketUser I will put 	ret = 10 
	 ret = 10 if the packet is good but comes from a cadre with VRL problems
	
 * 
 */
int EventReader::PKT_RunEvent(char* headerPkt, char* pamPkt, long int length, const PacketType* type){
	stringstream oss;
	string msg;
	int ret=0;
	PKT_UnpackPscuHeader(headerPkt); 	
	AlgorithmMap::iterator i = TechmodelAlgorithmMap.find(type); 
	if (i != TechmodelAlgorithmMap.end()) 
	{
		TechmodelAlgorithm* EventAlgorithm(i->second);
		try{
			EventAlgorithm->PKT_RunEvent(pamPkt, length);
		}		
		catch (WrongCRCException_PKTUsed &exc){
			//questo tipo di eccezione NON mi preoccupa, 
			//il pacchetto e' stato utilizzato e salvato ugualmente
			ret=2;
			oss.str("");
			oss << exc.print();    	 
			oss << " Return "<<ret;
			msg=oss.str();
			PamOffLineSW::mainLogUtil->logWarning(msg);	
		}  
		catch (CalibException &exc){			
			//questo tipo di eccezione mi preoccupa un pochino, 
			//il pacchetto e' stato utilizzato e salvato ugualmente 
			ret=3;
			oss.str("");
			oss << exc.print();    	 
			oss << " Return "<<ret;
			msg=oss.str();
			PamOffLineSW::mainLogUtil->logWarning(msg);
		}           
		catch (WrongCRCException &exc){
			//questo tipo di eccezione  mi preoccupa, 
			//il pacchetto non e' stato utilizzato ne salvato 
			ret=-1;
			oss.str("");
			oss << exc.print();
			oss << " The Packet is Discarded ";  
			oss << " Return "<<ret;
			msg=oss.str();
			PamOffLineSW::mainLogUtil->logError(msg);			
			return ret; 	
		}
		catch (FatalException &exc){
			ret=-2;
			oss.str("");
			oss << exc.print();
			oss << " The Packet is Discarded ";  
			oss << " Return "<<ret;
			msg=oss.str();
			PamOffLineSW::mainLogUtil->logError(msg);					
			return ret; 
		}
		catch (Exception &exc){
			ret=1;
			oss.str("");
			oss << exc.print();
			oss << " Return "<<ret;
			msg=oss.str();
			PamOffLineSW::mainLogUtil->logWarning(msg);
		}      
		// catch (NotExistingAlgorithmException exc) {} 

		Run->FillTrees(type);
		Header->GetCounter()->Increment(type);
	}
	else
	{
		ret=-3;
		oss.str("");
		oss << " No way to read events of type  " << type->GetName().c_str()<<". The Packet is skipped ";  
		oss << " Return "<<ret;
		msg=oss.str();
		PamOffLineSW::mainLogUtil->logWarning(msg);   
		return ret;
	}
	return ret;
}

/**
 * Unpack the PSCU header from the pkt into the structure.
 */	
//fill the header structure, 
void EventReader::PKT_UnpackPscuHeader(char* buff) 
{
  unsigned char PacketId1    = buff[3];
  unsigned char PacketId2    = buff[4];
  unsigned int  Counter      = (((UINT32)buff[5]<<16)&0x00FF0000) + (((UINT32)buff[6]<<8)&0x0000FF00) + (((UINT32)buff[7])&0x000000FF);
  unsigned int  OrbitalTime  = (((UINT32)buff[8]<<24)&0xFF000000) + (((UINT32)buff[9]<<16)&0x00FF0000) +  (((UINT32)buff[10]<<8)&0x0000FF00) + (((UINT32)buff[11])&0x000000FF);
  unsigned int  PacketLenght = (((UINT32)buff[12]<<16)&0x00FF0000) +  (((UINT32)buff[13]<<8)&0x0000FF00) + (((UINT32)buff[14])&0x000000FF);
  unsigned char CRC          = buff[15];
  unsigned char FileOffset   = 0;// ??

  Header->GetPscuHeader()->SetPacketId(PacketId1, PacketId2);
  Header->GetPscuHeader()->SetCounter(Counter);
  Header->GetPscuHeader()->SetOrbitalTime(OrbitalTime);
 //PacketLength is the length of the whole DATApacket starting from the first byte after the header
  //plus the CRC legth (which varies for each type of packet)
  Header->GetPscuHeader()->SetPacketLenght(PacketLenght);
  
  Header->GetPscuHeader()->SetCRC(CRC);
  Header->GetPscuHeader()->SetFileOffset(0);//I don't need this !!
}



/**
 * Get a string with the version info of the algorithm.
 */
std::string EventReader::GetVersionInfo(void) const {
	return 
    "$Header: /repository/PamOffLineSW/techmodel/EventReader.cpp,v 1.21 2008-03-12 13:48:30 messineo Exp $\n";
}


ClassImp(EventReader)
