/** @file
 * $Author: kusanagi $
 * $Date: 2005/08/29 09:46:13 $
 * $Revision: 5.0 $
 * 
 * Implementation of the functions of a sample Algorithm class.
 * This file can be used as a templace to develop your own algorithm.
 */

#include <log4cxx/logger.h>
#include <fstream>
#include "EventReader.h"
#include "ReaderAlgorithms.h"


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

using namespace pamela;
using namespace pamela::techmodel;

static log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(_T("pamela.techmodel.EventReader"));

unsigned int EventReader::maxPackets     = 0;
unsigned int EventReader::prevPckCounter = 0;
unsigned int EventReader::prevPckOBT     = 0;


/**
 * Constructor. 
 */
EventReader::EventReader(int packetsLimit = -1): 
  TechmodelAlgorithm(0, "TechmodelEventReader"){ 
  EventReader::maxPackets = packetsLimit;
  logger->debug(_T("Constructor"));
  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::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));
  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::TestTrk,         new TestTrkReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TestTof,         new TestTofReader));
  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::AcAlarm,         new AcAlarmReader));
  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::TsbT,            new TsbTReader));
  TechmodelAlgorithmMap.insert(AlgorithmMap::value_type(PacketType::TsbB,            new TsbBReader));
}

/**
 * Get a string with the version info of the algorithm.
 */
std::string EventReader::GetVersionInfo(void) const {
  return 
    "$Header: /home/cvsmanager/yoda/techmodel/EventReader.cpp,v 5.0 2005/08/29 09:46:13 kusanagi Exp $\n";
}

/**
 * Initialize the algorithm with a special run. This will initialize the
 * event reader routines for all packet types.
 */
void EventReader::Init(PamelaRun *run) {
    SetInputStream(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++) {
       oss.str("");
       oss << "Initializing algo " << i->second->GetAlgorithmName();
       logger->debug(oss.str().c_str());
       i->second->Init(run);
    }
    Run = dynamic_cast<TechmodelPamelaRun*>(run);
}

static void SkipToNextHeader(ifstream *);

/**
 * Read the next event header, call the reader algorithms that
 * correspond to its packet type, and read the event trailer.
 */
void EventReader::RunEvent(int EventNumber) {
  stringstream oss;
  int step = 0;
  const PacketType* type;
  while (!InputFile->eof() && ((step++ < maxPackets) || (maxPackets == 0))){
    try {
      if (FindStart()) {
            UnpackPscuHeader();
            type = Header->GetPscuHeader()->GetPacketType();
            AlgorithmMap::iterator i = TechmodelAlgorithmMap.find(type); 
            if (i != TechmodelAlgorithmMap.end()) {
                TechmodelAlgorithm *EventAlgorithm(i->second);
                EventAlgorithm->RunEvent(EventNumber, Header->GetPscuHeader()->GetPacketLenght());
                Run->FillTrees(type);
                Header->GetCounter()->Increment(type);
                logger->info(Header->GetPscuHeader()->Print());
            } else {
                oss.str("");
                oss << "\n No way to read events of type  " << type->GetName().c_str() << Header->GetPscuHeader()->Print();
                throw NotExistingAlgorithmException(oss.str().c_str()); //to exctract to an higher level and delete the logger!
            }
      }
    // In case of exception have to save the packet in a specific root file??
    } catch (NotExistingAlgorithmException exc) {
      oss.str("");
      oss << exc.print() << " " << Header->GetPscuHeader()->Print();
      logger->error(oss.str().c_str());
    } catch (WrongCRCHeaderException exc) {
      oss.str("");
      oss << exc.print();
      logger->error(oss.str().c_str());
    } catch (WrongCRCException exc) {
      oss.str("");
      oss << exc.print() << " " << Header->GetPscuHeader()->Print();
      logger->error(oss.str().c_str());
      InputFile->seekg( (-1)*(Header->GetPscuHeader()->GetPacketLenght() + 15) , std::ios::cur);
    } catch (UnidentifiedPacketException exc) {
      oss.str("");
      oss << exc.print() << " " << Header->GetPscuHeader()->Print();
      logger->error(oss.str().c_str());
    } catch (NotExistingCounterException exc) {
      oss.str("");
      oss << exc.print() << " " << Header->GetPscuHeader()->Print();
      logger->error(oss.str().c_str());
    } catch (LengthException exc) {
      oss.str("");
      oss << exc.print() << " " << Header->GetPscuHeader()->Print();
      logger->error(oss.str().c_str());
    } catch (BackwardCounterException exc) {
      oss.str("");
      oss << exc.print() << " " << Header->GetPscuHeader()->Print();
      logger->error(oss.str().c_str());
    } catch (...) {
      logger->error("Couldn't read the event. Skipping to the next header. \n");
    }
    if ((step%1000) == 0) std::cout << step/1000 << "K \n";
    oss.str("");
    oss << "----endPck " << Header->GetPscuHeader()->GetCounter() << "\n";
    logger->info(oss.str().c_str());
  }
    Header->GetCounter()->PrintCounters();
}

/**
 * Unpack the PSCU header from a file into the structure.
 */
void EventReader::UnpackPscuHeader(void) throw (WrongCRCHeaderException, LengthException, BackwardCounterException) {
  stringstream oss;
  int response = 0;
  char buff[16];
  InputFile->read(buff, sizeof(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   = buff[15];


  if (Counter < prevPckCounter){
    response = prevPckCounter - Counter;
    //oss.str("");
    //oss << "Packet counter is less than before of " << (prevPckCounter - Counter);
    //throw BackwardCounterException(oss.str().c_str());
    //logger->error(oss.str().c_str());
  }

  if (Counter > prevPckCounter + 1){
    oss.str("");
    oss << "Packet counter is greater than before of " << (Counter - prevPckCounter);
    logger->error(oss.str().c_str());
  }
  
  if ((OrbitalTime == prevPckOBT) & (PacketId1 == 0x10)){
    oss.str("");
    oss << "Onboard Time of this packet is equal to the previous packet OBT";
    logger->error(oss.str().c_str());
    logger->error(Header->GetPscuHeader()->Print());
  }

  if (OrbitalTime < prevPckOBT){
    oss.str("");
    oss << " Onboard Time is less than before of " << (prevPckOBT - OrbitalTime);
    logger->error(oss.str().c_str());
  }

  if (((BYTE)CM_Compute_CRC16(0, (BYTE*)&buff, 15) == (BYTE)buff[15]) && (PacketId1 == PacketId2)){
    prevPckCounter = Counter;
    prevPckOBT     = OrbitalTime;
    long int initPos = InputFile->tellg();
    long int finalPos;
    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(((long int)(InputFile->tellg()) - 16));
  } else {
     /*Here i should extract the block of Data for later analysis */
    InputFile->seekg(-(13), std::ios::cur);
    oss.str("");
    oss << "CRC Header Error on packet:" << PscuHeader::Print(buff);
    throw WrongCRCHeaderException(oss.str().c_str());
  }

  if (response > 0){
    oss.str("");
    oss << "Packet counter is less than before of " << response;
    throw BackwardCounterException(oss.str().c_str());
  }
}

/**
 * Unpack the trailer of a PSCU event into the structure.
 */
void EventReader::UnpackPscuTrailer(void) throw (std::exception) {

}

/**
 * Find the next starting poin for the PSCU event looking for a {0xFA, 0xFE, 0xDE} sequence
 */
bool EventReader::FindStart(void) throw (std::exception) {
//search an hexadecimal sequence in a file
//subSign    ------> pointer to the sequence buffer
//subSignDim ------> dimension of the buffer
// at exit
// return true if founds a match (else false)
// subF point rigth after the match, if found. Else EOF.
//Maurizio 15/11/2002-----------------------
  const int subSignDim = 3;
  const unsigned char subSign[subSignDim]={0xFA, 0xFE, 0xDE};
//------------------------------------------
  int subIndex = 0;
  char dataByte;
 
  int  buffSize = 64;
  int  index = 0;
  int  loop = -1;
  char buffer[buffSize];
  bool flagOverPad = false;

  while (!InputFile->eof()) {
    InputFile->read(buffer, sizeof(buffer));
    index = 0;
    loop++;
    while (index < buffSize){
        dataByte = buffer[index++];
        if (dataByte == (char)(*(subSign+subIndex))){
            if (subIndex++ == (subSignDim-1)) {
                InputFile->seekg( (index - (subIndex + buffSize)), std::ios::cur);
                if (flagOverPad){
                    oss.str("");
                    oss << "\n This packet beginning is farther than 64 byte from the end of the previous."
                        << "\n Below the is the last already unpacked packet";
                    logger->error(oss.str().c_str());
                    logger->error(Header->GetPscuHeader()->Print());
                }
                return true;
            } 
        } else {
            index = index - (subIndex);
            subIndex = 0;
        }
    }
    //Needs to guarantee the overap of the buffer(s) in several loop
    flagOverPad = true;
    InputFile->seekg( (-1)*(subSignDim + 1) , std::ios::cur);
  }
  return false;
} 

ClassImp(EventReader)
