//-----------------------------------------------------------------------//
//      -*- Mode: c++ -*-                                                //
// ----------------------------------------------------------------------//

#include "fp13Analysis.hh"
#include <TH1.h>

// ----------------------------------------------------------------------//
// Oeffnen des Files in das die Histogramme geschrieben werden           //
// ----------------------------------------------------------------------//

void fp13Analysis::openDataFile(const char *fileName) {
    
    fHistFile = new TFile("fp13.root", "RECREATE");          // erstellt bzw. ueberschreibt das fp13.root
    fDataFile = new ifstream(fileName); 
    char buff[200]; 
    fDataFile->getline(buff, 200, '\n');
    
    if (!(*fDataFile)) {                                     // prueft, ob das File wirklich geoeffnet wurde
	cout 
	    << "Error: Datafile " 
	    << fileName 
	    << " nicht gefunden!" 
	    << endl;
	abort(); 
    }
} 

// ----------------------------------------------------------------------//
// Erstellen der benoetigten Histogramme                                 //
// ----------------------------------------------------------------------//

void fp13Analysis::bookHistograms() {
    
    fHistFile->cd();
    TH1D *h = new TH1D("h1", "Hits in den Lagen insgesamt; Lage; Anzahl der Hits", 16, 0., 8.);   h->Sumw2();
    h = new TH1D("h2", "Detektor der Nachpulse; Detektor; Anzahl der Hits", 16, 0., 8);
    h = new TH1D("h3", "Lage der Zerfaelle nach oben; Lage; Anzahl der Hits", 16, 0., 8);
    h = new TH1D("h4", "Lage der Zerfaelle nach unten; Lage; Anzahl der Hits", 16, 0., 8);
    h = new TH1D("h5", "Detektor der Nachpulse; Detektor; Anzahl der Hits", 16, 0., 8);
    h = new TH1D("h6", "Anzahl aller Slices; Slices; Anzahl der Hits", 40, 0., 20.);
    h = new TH1D("h7", "Anzahl der genommenen Slices; Slices; Anzahl der Hits", 40, 0., 20.);    
    h = new TH1D("h8", "Startpattern; Detektor; Anzahl der Hits", 16, 0., 8);

    for (int i = 0; i < 8; ++i){                             // Form schreibt einen Array aus hier 8 Histogrammen           
	h = new TH1D(Form("z%i", i), Form("Zeit des ersten Hits fuer Lage %i; Zeit [ns]; Anzahl der Hits", i), 40, 0., 200.);         //h21
	h = new TH1D(Form("x%i", i), Form("Nachpulse fuer Detektor %i; Zeit [ns]; Anzahl der Hits", i), 50, 0., 20000.);              //h22
	h = new TH1D(Form("a%i", i), Form("Zerfall nach oben in Lage %i; Zeit [ns]; Anzahl der Hits", i), 50, 0., 20000.);            //h23
	h = new TH1D(Form("b%i", i), Form("Zerfall nach unten in Lage %i; Zeit [ns]; Anzahl der Hits", i), 50, 0., 20000.);           //h24
	h = new TH1D(Form("w%i", i), Form("Nachpulse2 fuer Detektor %i; Zeit [ns]; Anzahl der Hits", i), 50, 0., 20000.);            //h25
    }
}

// ----------------------------------------------------------------------//
// Einlesen und Formatieren der Daten                                    //
// ----------------------------------------------------------------------//

int fp13Analysis::readEvent() {

    static char buffer[200]; 
    ++fEvent;
    
    if ((fEvent % 10000) == 0){                              // Status wird alle 10000 Events ausgegeben
	cout << "Event " << fEvent << endl;
    }
    
    fBits.clear();                                           // Loeschen der Klassenvariablen 
    fBits.resize(0);
    fTimes.clear(); 
    fTimes.resize(0);fHistFile->cd();


    
    while (fDataFile->getline(buffer, 200, '\n')) {          // liest aus dem File 200 Zeichen in den Buffer ein bis zum Ende der Zeile
	
	int Slices, bitPattern, timeStamp;                   // bitPattern = Hits  
	
	for (int i = 0; i < 200; ++i) {                      // Stringformat: Wird im Buffer ein CarriageReturn gefunden,
	    if (buffer[i] == '\r') {                         // wird es durch ein \0 (= end of string) ersetzt.
		buffer[i] = '\0';
		break;
	    }
	}
	
	if (!strcmp(buffer, "###")) {                        // Ende eines Events bei ###
	    break;
	}
	
	sscanf(buffer, "%d %d %d",
	       &Slices, &bitPattern, &timeStamp);
	
	fBits.push_back(bitPattern);                         // Daten einlesen
	fTimes.push_back(timeStamp);                         // Daten einlesen und in ns konvertieren

	fSlices = Slices;
    }

// ----------------------------------------------------------------------//
// Fuellen fuer alle Slices                                              //
// ----------------------------------------------------------------------//

    fHistFile->cd();                                         // Anzahl der Slices
    TH1D *h6 = (TH1D*)gDirectory->Get("h6");
    h6->Fill(fSlices);

// ----------------------------------------------------------------------//
// Ende Fuellen fuer alle Slices                                         //
// ----------------------------------------------------------------------//

    schieben();                                              // Zusammenschieben "verwackelter" Events
    
    if (fDataFile->eof()) {
	return 1; 
    } else {
	return 0; 
    }   
}

// ----------------------------------------------------------------------//
// Fuellen der Histogramme                                               //
// ----------------------------------------------------------------------//

void fp13Analysis::analyze() {

// ----------------------------------------------------------------------//
// allgemeine Histogramme                                                //
// ----------------------------------------------------------------------//

    for (int il = 0; il < 8; ++il) {                         // Loop ueber alle 8 Lagen

	for (int is = 0; is < (fSlices); ++is) {             // Loop ueber die Slices

	    if (fBits[is] & (1 << il)) {                     // Prueft, ob die jeweilige Lage zur gerade untersuchten Zeit getroffen war.
		
		fHistFile->cd();                             // Spektren pro Lage
		TH1D *h = (TH1D*)gDirectory->Get("h1");
		h->Fill(il);
	    }
	}
    }
    
    for (int is = 0; is < (fSlices); ++is) {
	for (int il = 0; il < 8; ++il) {
	    if (is == 0){
		if (fBits[is] & (1 << il)){
		    fHistFile->cd();                         // Anzeige der Anzahl der auftretenden Zeiten beim ersten Slice in allen Lagen
		    TH1D *h21 = (TH1D*)gDirectory->Get(Form("z%i", il));
		    h21->Fill(fTimes[is]);
		}	      
	    }
	}
    }

    if (fSlices <= 1){
  	return;
    }
    
    int lzno = -1;                                            // lzno: Lage des Zerfalls nach oben
    int lznu = -1;                                              // lznu: Lage des Zerfalls nach unten
    
    for (int i=1;i<fSlices;i++){ // loop ueber die slices

// ----------------------------------------------------------------------//
// Fuellen fuer Zerfall nach oben                                        //
// ----------------------------------------------------------------------//

      
	lzno = -1;                                            // lzno: Lage des Zerfalls nach oben
	
	lzno = zerfallNachOben(i);                                   // Funktion gibt die Lebensdauer beim Zerfall nach oben zurueck, hat kein Zerfall stattgefunden -1
	
	if((lzno > -1) && (dto > 50)){             // es werden nur Zeiten > 50 ns und < 10000 ns betrachtet
	    fHistFile->cd();
	    TH1D *h3 = (TH1D*)gDirectory->Get("h3");
	    h3->Fill(lzno);
	    TH1D *h23 = (TH1D*)gDirectory->Get(Form("a%i", lzno));  // Zerfall nach oben in allen Lagen
	    h23->Fill(dto);
	}

// ----------------------------------------------------------------------//
// Fuellen fuer Zerfall nach unten                                       //
// ----------------------------------------------------------------------//

	lznu = -1;                                              // lznu: Lage des Zerfalls nach unten
	
	lznu = zerfallNachUnten(i);                                  // Funktion gibt die Lebensdauer beim Zerfall nach oben zurueck, hat kein Zerfall stattgefunden -1
	if((lznu > -1) && (dtu > 50)){             // es werden nur Zeiten > 50 ns und < 10000 ns betrachtet
	    fHistFile->cd();
	    TH1D *h4 = (TH1D*)gDirectory->Get("h4");
	    h4->Fill(lznu);
	    TH1D *h24 = (TH1D*)gDirectory->Get(Form("b%i", lznu));  // Zerfall nach oben in allen Lagen
	    h24->Fill(dtu);
	}

// -----
// Fuellen Sie hier mit den aus der Funktion zerfallNachUnten() erhaltenen Werten
// die oben definierten Histogramme h4 und h24.

    
// ----------------------------------------------------------------------//
// Fuellen fuer Nachpulse (allgemein)                                    //
// ----------------------------------------------------------------------//

	int dnpa = -1;                                              // dnpa: Detektor des Nachpulses (allgemeine Methode)
	
	dnpa = nachpulse(i);                                         // allgemeine Version
	
	if ((dnpa!=-1) && (dt > 50)){                          // es werden nur Zeiten > 50 ns und < 10000 ns betrachtet
	    fHistFile->cd();
	    TH1D *h2 = (TH1D*)gDirectory->Get("h2");                // Nachpulsspektrum (lang)
	    h2->Fill(dnpa);
	    fHistFile->cd();
	    TH1D *h22 = (TH1D*)gDirectory->Get(Form("x%i", dnpa));  // Nachpulse in allen Detektoren
	    h22->Fill(dt);
	}
// Fuellen Sie hier mit den aus der Funktion nachpulse() erhaltenen Werten
// die oben definierten Histogramme h2 und h22.


// ----------------------------------------------------------------------//
// Fuellen fuer Nachpulse (speziell)                                     //
// ----------------------------------------------------------------------//

	int dnps = -1;                                              // dnps: Detektor des Nachpulses (spezielle Methode)
	
	dnps = nachpulse2(i);                                       // spezielle Version
	
	if ((dnps!=-1) && (dt2 > 50)){                          // es werden nur Zeiten > 50 ns und < 10000 ns betrachtet
	    fHistFile->cd();
	    TH1D *h5 = (TH1D*)gDirectory->Get("h5");                // Nachpulsspektrum (lang)
	    h5->Fill(dnps);
	    TH1D *h25 = (TH1D*)gDirectory->Get(Form("w%i", dnps));  // Nachpulse in allen Detektoren
	    h25->Fill(dt2);
	}

    } // ende des loops uebr die Slices
     
// ----------------------------------------------------------------------//
// Fuellen fuer genommene Slices                                         //
// ----------------------------------------------------------------------//
     
     if(((lzno > -1) && (dto > 50)) || ((lznu > -1) && (dtu > 50))){
	 
	 fHistFile->cd();                                    // Anzahl der Slices
	 TH1D *h7 = (TH1D*)gDirectory->Get("h7");
	 h7->Fill(fSlices + 1);                              // + 1, da hier Slices [0...n-1], aber in LabVIEW Slices [1...n]
     }
     
// ----------------------------------------------------------------------//
// Fuellen fuer Startpattern                                             //
// ----------------------------------------------------------------------//
     
     int tiefste = -1;
     tiefste = startpattern();
     
     if(tiefste > -1){
	 
	 fHistFile->cd();                                    // Anzahl der Slices
	 TH1D *h8 = (TH1D*)gDirectory->Get("h8");
	 h8->Fill(tiefste);
     }
}

// ----------------------------------------------------------------------//
// Schreiben des .ROOT Files fuer die Histogramme                        //
// ----------------------------------------------------------------------//

void fp13Analysis::dumpHistograms() {
    
    fHistFile->Write();
    fHistFile->Close(); 
}

// ----------------------------------------------------------------------//
// Schreiben des .txt Files fuer die Histogramme                         //
// ----------------------------------------------------------------------//

    void fp13Analysis::dumpAllHistograms(const char *filename) {
	
	ofstream OUT("hist.txt"); 
	
	TFile f(filename); 
	
	f.ReadAll();
	TList *MyList = gFile->GetList();
	TIter next(MyList);
	TObject *obj;
	
	while ((obj = (TObject*)next())) {
	    if (obj->InheritsFrom(TH1::Class())) {
		TH1* h = (TH1*)obj; 
		cout << "dumping " << h->GetName() << endl;
		printHist(h, OUT); 
	    }
	}
	
    }
    
    void fp13Analysis::printHist(TH1 *h, ofstream &OUT) {
	double con(0.), min(0.), max(0.); 
	
	OUT << "----------------------------------------------------------------------" << endl;
	OUT << "Histogram: " << h->GetName() << " Titel: " << h->GetTitle() << endl;
	OUT << "Entries: " << h->GetEntries() << endl;
	OUT << "Bins: " << h->GetNbinsX() 
	    << " von " << h->GetBinLowEdge(1) 
	    << " bis " << h->GetBinLowEdge(h->GetNbinsX() + 1) 
	    << endl;
	
	for (Int_t i = 0; i <= h->GetNbinsX()+1; ++i) {
	    con = h->GetBinContent(i); 
	    min = h->GetBinLowEdge(i);
	    max = min + h->GetBinWidth(i);
	    OUT << Form("%3d ", i) << Form(" %7.3f ", min) << " .. " << Form(" %7.3f ", max) << ":" 
		<< Form(" %12.3f", con) << " +/- " << Form("%12.3f", h->GetBinError(i))
		<< endl;
	}
    }
    
// ----------------------------------------------------------------------//
// Initialisieren einiger Variablen - Konstruktor                        //
// ----------------------------------------------------------------------//

fp13Analysis::fp13Analysis() {

    fEvent  = 0; 
    fSlices = 0; 
}

// ----------------------------------------------------------------------//
// Destruktor                                                            //
// ----------------------------------------------------------------------//

fp13Analysis::~fp13Analysis() { 

}

// ----------------------------------------------------------------------//
// Funktion zur Formatierung der eingelesenen Daten                      //
// ----------------------------------------------------------------------//

void fp13Analysis::schieben() {

    for (int is = 0; is < (fSlices); ++is) {                 // Loop ueber die Slices
	if((fTimes[is+1] - fTimes[is]) == 10) {              // pruefen, ob Zeiten mit nur einer Einheit Unterschied ( = 10ns) auftreten
	    fBits[is] = fBits[is] | fBits[is+1];             // Zusammenfuegen der beiden Bitmuster (das neue erhaelt somit die Zeit des ersten Slice) 
	    fTimes[is] = fTimes[is+1];                    
	    for (int i = 1; i < (fSlices - is); ++i){        // schiebt alle fTimes und fBits um jeweils eins nach vorne
		fTimes[is+i] = fTimes[is+i+1];
		fBits[is+i] = fBits[is+i+1];
	    }
	    fSlices = fSlices - 1;
	    is--;                                            // fasst Sequenzen zusammen (t = 30,40,50 => t = 50)
	}
    }
}

// ----------------------------------------------------------------------//
// Pruefen auf Zerfall nach oben                                         //
// ----------------------------------------------------------------------//

int fp13Analysis::zerfallNachOben(int i){                         // letzter Hit eines kontinuierlichen Durchgangs in Detektor x entspricht
                                                             // Zerfall in Lage x (ueber Richtung noch nichts gesagt)
    int   t1 = -1;                                           // t1: Zeit des kontinuierlichen Durchgang
    int   t2 = -1;                                           // t2: Zeit des verzoegerten Hits
    int   l = -1;                                            // l: Lage des Zerfalls nach oben
    dto = -1;                                                // dto: Zeitdifferenz Delta t beim Zerfall nach oben
    durchgang = -1;                                          // durchgang: letzte Lage des kontinuierlichen Durchgangs
    
    for (int n = 7; n > 1; --n){
	if (fBits[0] == int(pow(2, n))-1){                   // prueft, ob von oben an einige Lagen durchgehend getroffen wurden
	    t1 = fTimes[0];
	    durchgang = n-1;                                 // zur Skalierung des Nachpuls-Histogramms
	    if (fBits[i] == int(pow(2, n-1))){               // prueft, ob die letzte getroffene Lage auch in Slice 2 einen Hit hatte
		t2 = fTimes[i];
		dto = t2 -t1;
		l = n-1;
		goto end;
	    }                             
	}
    }
 end:
    return l;
}

// ----------------------------------------------------------------------//
// Pruefen auf Zerfall nach unten                                        //
// ----------------------------------------------------------------------//

int fp13Analysis::zerfallNachUnten(int i){                        // letzter Hit eines kontinuierlichen Durchgangs in Detektor x entspricht
    
// Schreiben Sie diese Funktion fuer den Zerfall nach unten.
// Definieren Sie sich dazu benoetigte Variablen.
// dtu steht als globale Variable zu Verfuegung.
// Wie muss das Detektormuster eines Zefalls nach unten aussehen?
// Sie benoetigen einen Rueckgabewert.
// Orientierung bietet der Zerfall nach oben.
    
    dtu = -1;    
    int l = -1;
   return l;
}

// ----------------------------------------------------------------------//
// Pruefen auf Nachpulse (allgemein)                                     //
// ----------------------------------------------------------------------//

int fp13Analysis::nachpulse(int j){

// Schreiben Sie hier eine allgemeinere Funktion zum Herausfiltern der Nachpulse.
// Definieren Sie sich dazu benoetigte Variablen.
// dt steht als globale Variable zu Verfuegung.
// Wie koennte ein allgemeineres Detektormusters eines Nachpulses aussehen?
// Sie benoetigen einen Rueckgabewert.
// Orientierung bietet die spezielle Nachpulsfunktion.
    int    l = -1;

    dt = -1;

    return l;
}

// ----------------------------------------------------------------------//
// Pruefen auf Nachpulse (speziell)                                      //
// ----------------------------------------------------------------------//

int fp13Analysis::nachpulse2(int i){

    int    t1 = 0;                                           // t1: Zeit des kontinuierlichen Durchgangs    
    int    t2 = 0;                                           // t2: Zeit des verzoegerten Hits
    int    d = -1;                                           // d: Detektor des Nachpulses;
    dt2 = -1;                                                // dt: Zeitdifferenz Delta t der beiden
    
    if (fBits[0]  == 255){                                   // prueft, ob im ersten Slice alle Detektoren getroffen waren
	t1 = fTimes[0];
	for (int n = 6; n > -1; --n){                        // prueft, ob danach noch ein weiterer Detektor (aus 0..7) an gewesen ist
	    if (fBits[i] == int(pow(2, n))){                 // prueft, ob eine einzelne Lage einen Nachpuls hatte
		t2 = fTimes[i];                              // ausser Lage 7, da hier Nachpuls und echter Hit nicht zu unterscheiden sind
		dt2 = t2 -t1;
		d = n;
		break;
	    }
	}
    }
    return d;
}

// ----------------------------------------------------------------------//
// Pruefen auf Startpattern                                              //
// ----------------------------------------------------------------------//

int fp13Analysis::startpattern(){                            // letzter Hit eines kontinuierlichen Durchgangs in Detektor x entspricht
    int   start = -1;                                        // Zerfall in Lage x (ueber Richtung noch nichts gesagt)
    
    for (int n = 8; n > 1; --n){
	if (fBits[0] == int(pow(2, n))-1){                   // prueft, ob von oben an einige Lagen durchgehend getroffen wurden
	    start = n-1;
	}
    }
    return start;
}
