/**
 * \file TrkLevel0.cpp
 * \author Elena Vannuccini
 */
#include <TrkLevel0.h>

//......................................
// F77 routines
//......................................
extern "C" {    

    void filladc_(int*);
    void evaluatecn_(int*);
    void subtractped_(int*);


}
using namespace pamela::tracker;

/**
 *
 *   Function to decode the tracker words.
 *   
 *   Tracker words are of three types:
 *   - ADC data 
 *   - strip address
 *   - end of ladder
 *   
 *   The function returns a struct variable (trkword) 
 *   that contains two quantities, type and decode, which are assigned 
 *   the following values:
 *   
 *   type                     decode              
 *   ----------------------------------------------------
 *   -1     error             0          
 *    0     data              0-4095   ADC values
 *    1     address           0-1023   strip address (relative to ladder)
 *    2     end-of-ladder     1,2,3    ladder number     (compressed acq mode)      
 *                            4,5,6    ladder number + 3 (full acq mode)
 *       
 **/

trkword datadecode(int word){
  int type =0;
  int data =0;
  int nodata = word&0xf000;
  int zero = 0;

  trkword thisword;
    
  switch (nodata>>12){

  case 0:      
	
    thisword.decode = word;
    thisword.type = 0;
    //	cout << thisword.decode << "\n";
    return (thisword);              //>>>>> ADC data (0)

  case 1:       

    type = word&0xC00;
    data = word&0x3ff;

    switch(type>>10){

    case 0:   
      thisword.decode = data;
      thisword.type = 1; //>>> address (1)
      return (thisword);          

    case 2:  
      //	    if(data>=4)data = data-3;
      if(data>6){
	printf("Error on data \n");
	thisword.decode = zero;
	thisword.type = -1;
	return (thisword);     //>>>>> error (-1)
      }
      thisword.decode = data;	    
      thisword.type = 2;
      return (thisword);       //>>> end-of-ladder 

    default:
      printf("Error on data \n");
      thisword.decode = zero;
      thisword.type = -1;
      return (thisword);              //>>>>> error (-1)
	    
    }

  default:

    printf("Error on data \n");
    thisword.decode = zero;
    thisword.type = -1;
    return (thisword);              //>>>>> error (-1)
  }
}


/**
 * Pass values from the TrkLevel0 object to a struct cTrkLevel0 (to put data in F77 common).
 */
//void TrkLevel0::GetCommonVar(cTrkLevel0 *l0) {
void TrkLevel0::GetLevel0Struct(cTrkLevel0 *l0) {
     
    Int_t countrk=0;

    l0->good0         = yodaobj->good0;
    l0->TOTDATAlength = yodaobj->TOTDATAlength;
    for(Int_t ii=0;ii<12;ii++){
	l0->DAQmode[ii]         = yodaobj->DAQmode[ii];
	l0->DSPnumber[ii]       = yodaobj->DSPnumber[ii];
	l0->DATAlength[ii]      = yodaobj->DATAlength[ii];
	l0->eventn[ii]          = yodaobj->eventn[ii];
	l0->nclust[ii]          = yodaobj->nclust[ii];
	l0->cutc[ii]            = yodaobj->cutc[ii];
	l0->cutcl[ii]           = yodaobj->cutcl[ii];
	for(Int_t iii=0;iii<3;iii++){
	    l0->addrcluster[iii][ii] = yodaobj->addrcluster[ii][iii];
	    l0->signcluster[iii][ii] = yodaobj->signcluster[ii][iii];
	};
	l0->fc[ii]              = yodaobj->fc[ii];
	l0->compressiontime[ii] = yodaobj->compressiontime[ii];
	l0->fl5[ii]             = yodaobj->fl5[ii];
	l0->fl4[ii]             = yodaobj->fl4[ii];
	l0->fl3[ii]             = yodaobj->fl3[ii];
	l0->fl2[ii]             = yodaobj->fl2[ii];
	l0->fl1[ii]             = yodaobj->fl1[ii];
	l0->fl6[ii]             = yodaobj->fl6[ii];
	l0->checksum[ii]        = yodaobj->checksum[ii];
	for(Int_t j=0;j<yodaobj->DATAlength[ii];j++){
	    l0->datatracker[countrk] = yodaobj->TrackerData.At(countrk);
	    ++countrk;
	};
	l0->pnum[ii]            = yodaobj->pnum[ii];
	l0->cmdnum[ii]          = yodaobj->cmdnum[ii];
	l0->bid[ii]             = yodaobj->bid[ii];
	l0->alarm[ii]           = yodaobj->alarm[ii];
	l0->aswr[ii]            = yodaobj->aswr[ii];
    };

    
}
/**
 * Pass values from the TrkLevel0 object to a struct cTrkLevel0 (to put data in F77 common).
 */
void TrkLevel0::SetFromLevel0Struct(cTrkLevel0 *){ 
    
    cout<<"void TrkLevel0::SetFromLevel0Struct(cTrkLevel0 *) -- not implemented"<<endl;
    
};
/**
 * Method to call the F77 routine that performs level0->level1 processing.
 * The level1 output is stored in a common block, which can be retrieved 
 * by mean of the method TrkLevel1::SetFromLevel1Struct().
 */
int TrkLevel0::ProcessEvent(){

//    cout << "int TrkLevel0::ProcessEvent()" << endl;
    TrkParams::Load(6);
    if( !TrkParams::IsLoaded(6) ){
	cout << "int TrkLevel0::ProcessEvent() -- ERROR -- VK-mask not loaded"<<endl;
	return 0;
    };
    TrkParams::LoadCalib( );
    if( !TrkParams::CalibIsLoaded() ){
	cout << "int TrkLevel0::ProcessEvent() -- ERROR -- Calibration not loaded"<<endl;
	return 0;
    };

    GetLevel0Struct();
    int F77err = 0;
    reductionflight_(&F77err);
    if(F77err < 0){
	cout << "int TrkLevel0::ProcessEvent() -- ERROR -- from F77 routine"<<endl;
	return 0;
    }
//    cout << "...done"<<endl;
    
    return 1;

}
/**
 * Method to evaluate the raw signal (ADC) of each strip (it performs uncompression). 
 * (it calls F77 routine filladc )
 */
bool TrkLevel0::FillADC(){

//    cout << "int TrkLevel0::ProcessEvent()" << endl;
    TrkParams::Load(6);
    if( !TrkParams::IsLoaded(6) ){
	cout << "int TrkLevel0::FillADC() -- ERROR -- VK-mask not loaded"<<endl;
	return false;
    };
    TrkParams::LoadCalib( );
    if( !TrkParams::CalibIsLoaded() ){
	cout << "int TrkLevel0::FillADC() -- ERROR -- Calibration not loaded"<<endl;
	return false;
    };

    GetLevel0Struct();
    int iflag=0;
    filladc_(&iflag);
    if(iflag!=0)return false;
    return true;

}
/**
 * Method to evaluate calibrated signal (ADC-CN-PED) of each view 
 * (it calls F77 routines: filladc + evaluatecn + subtractped)
 * @param iview view number (1-12)
 */
bool TrkLevel0::GetCalibratedEvent(int iview, TGraph* graph){

    if ( iview<1 || iview>12 )return false;
    if ( !FillADC() ) return false;
    evaluatecn_(&iview);
    subtractped_(&iview);

    if(graph){
	graph->Set(NSTRIP);
	for(int i=0; i<NSTRIP; i++)graph->SetPoint(i,(float)i,calibratedsignal_.value[i]);
    }
    

    return true;

}
/**
 * extract compressed/full data for a subset of channels
 *
 */
void TrkLevel0::Decode(int from,int to,bool& COMPRESSED,bool& FULL, int* datacomp, int* datafull){
    //
    int address = 0;
    int ladder = 1;
    Int_t whisto[3072]; for(int i=0; i<3072; i++)whisto[i] = -200;
    //
    COMPRESSED=false;
    FULL=false;

    for(Int_t vi = from ; vi < to ; vi++){
	int word = yodaobj->TrackerData.At(vi);	    
	trkword thisword = datadecode(word);
	switch (thisword.type){
	    
	case 0:  //ADC value
	    whisto[address] = thisword.decode;
//	    cout << vi<<"    data " << thisword.decode << " @ "<<address << "\n"; 
	    address++;    
	    break;
	
	case 1:  //address
	    address = 1024*(ladder-1) + thisword.decode;
//	    cout << vi<<"     adr " << address << "\n";
	    break;
	    
	case 2:  //end-of-ladder
	    ladder = thisword.decode;
//	    cout << vi<<" Ladder " << ladder << "\n"; 
	    if(ladder==3){
		//                  end of compressed data - FILL HISTO
//		cout << ">>> COMPRESSED data" << "\n";
		COMPRESSED=true;
		for(int ii = 0; ii < 3072; ii++){
		    *(datacomp+ii) = whisto[ii];
		    whisto[ii] = -200;
		}
		address = 0;
		ladder = 1;		    
	    }else if(ladder==6){
		//                  end of full data - FILL HISTO
//		cout << ">>> FULL data" << "\n";
		FULL=true;
		for(int ii = 0; ii < 3072; ii++){
		    *(datafull+ii) = whisto[ii];
		    whisto[ii] = -200;
		}
		address = 0;
		ladder = 1;
	    }else{		
		if(ladder>3)	ladder=ladder-3;
		address= ladder*1024;		
		ladder = ladder + 1;	
	    }
	}    
    }     

}

/**
 * Retrieve full event
 *
 */
bool TrkLevel0::GetFullEvent(int iview, TGraph* graph){


    // retrieve the index of the dsp
    int idsp=0;
    do{
	if ( yodaobj->DSPnumber[idsp] == iview )break;
	idsp++;
    }while(idsp<12);
    if(idsp==12)return false;
    //
    // check DAQ mode:
    //  9 full 
    // 10 compressed
    // 11 compressed+full 
    //  8 special: compressed+full <--> compressed alternati
    //
    if( yodaobj->DAQmode[idsp] == 10 )return false;//compressed only
    // 
    int offset =0;
    for(int i=0; i<idsp; i++)offset+=yodaobj->DATAlength[i];
    //
    // decode data
    //
    Int_t whistocomp[3072];
    Int_t whistofull[3072];
    bool COMPRESSED=false;
    bool FULL=false;
    Decode(offset,offset+yodaobj->DATAlength[idsp],COMPRESSED,FULL,whistocomp,whistofull);
    if(!FULL) return false;
    
    if(!graph)graph=new TGraph(3072);
    for(int i=0; i<3072; i++)graph->SetPoint(i,(double)i,(double)whistofull[i]);
    return true;

}

/**
 * Retrieve compressed event
 *
 */
bool TrkLevel0::GetCompressedEvent(int iview, TGraph* graph){


    // retrieve the index of the dsp
    int idsp=0;
    do{
	if ( yodaobj->DSPnumber[idsp] == iview )break;
	idsp++;
    }while(idsp<12);
    if(idsp==12)return false;
    //
    // check DAQ mode:
    //  9 full 
    // 10 compressed
    // 11 compressed+full 
    //  8 special: compressed+full <--> compressed alternati
    //
    if( yodaobj->DAQmode[idsp] == 9 )return false;
    // 
    int offset =0;
    for(int i=0; i<idsp; i++)offset+=yodaobj->DATAlength[i];
    //
    // decode data
    //
    Int_t whistocomp[3072];
    Int_t whistofull[3072];
    bool COMPRESSED=false;
    bool FULL=false;
    Decode(offset,offset+yodaobj->DATAlength[idsp],COMPRESSED,FULL,whistocomp,whistofull);
    if(!COMPRESSED) return false;
    
    if(!graph)graph=new TGraph(3072);
    for(int i=0; i<3072; i++)graph->SetPoint(i,(double)i,(double)whistocomp[i]);
    return true;

}
/**
 * Retrieve uncompressed event event
 */
bool TrkLevel0::GetUnCompressedEvent(int iview, TGraph* graph){

    if(!graph)graph=new TGraph(3072);
    //
    if( !GetCompressedEvent(iview,graph) )return false;
    //   
    double* adc;
    double last=0.;
    adc = graph->GetY();
    for(int i=0; i<3072; i++){
	int ivk = (int)((float)i/128.);
	int is = i%128;
	if(*(adc+i)>0)last = adc[i] - (double)pedsigbad_.pedestal_t[is][ivk][iview-1];
	else{
	    *(adc+i) = last + (double)pedsigbad_.pedestal_t[is][ivk][iview-1];
	}
	graph->SetPoint(i,(double)i,*(adc+i));
    }
    
    return true;

}
/**
 * Retrieve sigmas
 */
bool TrkLevel0::GetSigma(int iview, TGraph* graph){

    if(!graph)graph=new TGraph(3072);
    //
    for(int i=0; i<3072; i++){
	int ivk = (int)((float)i/128.);
	int is = i%128;
	graph->SetPoint(i,(double)i,(double)pedsigbad_.sigma[is][ivk][iview-1]);
    }
    
    return true;

}

ClassImp(TrkLevel0);
