#include "UserRun.hh"

#include "G4SDManager.hh"
#include "SDDDetectorHit.hh"
#include "DetectorHit.hh"
#include "G4Event.hh"
#include "G4Trajectory.hh"
#include "G4VVisManager.hh"
#include "G4SDManager.hh"
#include "G4UnitsTable.hh"
#include "G4SystemOfUnits.hh"
#include "G4PhysicalConstants.hh"
#include "G4UIcommand.hh"
#include "tls.hh"

#include "ConfigFile.hh"

// Change this header if you want different AnalysisManager output (e.g. XML)
//#include "g4root.hh"
#include "G4AnalysisManager.hh"

UserRun::UserRun()
{
    hitsCollectionIndex = G4SDManager::GetSDMpointer()->GetCollectionID("SCI");
    SDDhitsCollectionIndex = G4SDManager::GetSDMpointer()->GetCollectionID("SDD");
}

UserRun::~UserRun() {;}


void UserRun::RecordEvent(const G4Event* event)
{
	// This method specifies the actions that must be performed at the 
	// end of each event (e.g. retrieve information, score, clean up 
	// things, etc.)
	
	// The following line verifies that a valid hits collection has been 
	// found. If not, nothing happens.
    G4double eventID = event -> GetEventID();


	if((hitsCollectionIndex < 0) && (SDDhitsCollectionIndex<0)) return;
    

    // Now, get the HCofThisEvent: it contains all the hits collections
	// that have been defined.
	G4HCofThisEvent* HCE = event -> GetHCofThisEvent();

	// Retrieve the hit collections.
    DetectorHitsCollection* hitsCollection = 0;
    SDDDetectorHitsCollection* SDDhitsCollection = 0;

//    G4cout << "hitsCollectionIndex, SDDhitsCollectionIndex: "  << hitsCollectionIndex <<", " << SDDhitsCollectionIndex << G4endl;

	if(HCE) {
        if(hitsCollectionIndex == 0 || hitsCollectionIndex == 1){
            hitsCollection = (DetectorHitsCollection*)(HCE -> GetHC(hitsCollectionIndex));
        }
        if(SDDhitsCollectionIndex == 0 || SDDhitsCollectionIndex == 1){
            SDDhitsCollection = (SDDDetectorHitsCollection*)(HCE -> GetHC(SDDhitsCollectionIndex));
        }
	    //G4cout << "*** DEBUG: hitsCollection pointer: " << hitsCollection << G4endl;
	}
    

    
    // If both hit collections are empty, nothing happens.
    int numberHits = 0;
    int SDDnumberHits = 0;

    if(hitsCollection) {numberHits = hitsCollection -> entries();}
    if(SDDhitsCollection) {SDDnumberHits = SDDhitsCollection -> entries();}

    if ((numberHits == 0) && (SDDnumberHits == 0)) return;

    
    // Get primary event informations
	G4double en_primary    = 0.*MeV;
	G4double x_primary     = 0.*cm;
	G4double y_primary     = 0.*cm;
	G4double z_primary     = 0.*cm;
	G4double theta_primary = 0.*deg;
	G4double phi_primary   = 0.*deg;
	G4int nVertex = event -> GetNumberOfPrimaryVertex();
	// G4cout << " *** DEBUG " << " nVertex " << nVertex << G4endl;
	for ( G4int j = 0; j < nVertex; j++) {
		G4int nPart =  event -> GetPrimaryVertex(j)->GetNumberOfParticle();
		//G4cout << " *** DEBUG " << " nPart " << nVertex << G4endl;
		for ( G4int i = 0; i < nPart; i++) {
			G4ThreeVector position = event -> GetPrimaryVertex(j) -> GetPosition();
			G4ThreeVector direction = event -> GetPrimaryVertex(j) -> GetPrimary(i) -> GetMomentum();
			G4double P = direction.mag();
			en_primary = event->GetPrimaryVertex(j)->GetPrimary(i)->GetMass();
			G4double E0 = event->GetPrimaryVertex(j)->GetPrimary(i)->GetG4code()->GetPDGMass();
			en_primary = std::sqrt(P*P+E0*E0);
			en_primary -= E0;
			direction = direction*(1./direction.mag());
			direction = -direction;  // reverse the direction
			phi_primary = direction.phi();
			if (phi_primary <0) phi_primary = phi_primary + twopi;
			theta_primary = direction.theta();
			x_primary=position.x();
			y_primary=position.y();
			z_primary=position.z();
			//G4cout << " *** DEBUG " << " position " << position << G4endl;
			//G4cout << " *** DEBUG " << " direction " << direction << G4endl;
			//G4cout << " *** DEBUG " << " P " << P << G4endl;
			//G4cout << " *** DEBUG " << " en_primary " << en_primary << G4endl;
			//G4cout << " *** DEBUG " << " x " << x_primary << G4endl;
			//G4cout << " *** DEBUG " << " y " << y_primary << G4endl;
			//G4cout << " *** DEBUG " << " z " << z_primary << G4endl;
			//G4cout << " *** DEBUG " << " Theta " << theta_primary/deg << G4endl;
			//G4cout << " *** DEBUG " << " Phi " << phi_primary/deg << G4endl;
		}
	}
    

    // Get analysis manager
    G4AnalysisManager* analysisManager = G4AnalysisManager::Instance();

    // Process scintillator hits
    if(hitsCollection) {
//        G4cout << "*** Debug: Got in hitsCollection: " << hitsCollection << G4endl;

		for(int i = 0; i < numberHits ; i++) {
			DetectorHit* ahit = (*hitsCollection)[i];
			G4double energyDeposit = ahit -> GetEnergyDeposit();
            G4int    xpixel    = ahit -> GetScintID();
		    G4int    ypixel    = ahit -> GetPixelID();
            G4double   locpixelX    = ahit -> GetLocationPixelX();
            G4double   locpixelY    = ahit -> GetLocationPixelY();
            G4double   locpixelZ    = ahit -> GetLocationPixelZ();

			G4double lastStepGlobalTime = ahit -> GetTime();
            
			
            // Write energy deposit and pixel in the ROOT file
            analysisManager->FillNtupleDColumn(0, eventID);
            analysisManager->FillNtupleDColumn(1, energyDeposit/keV);
            analysisManager->FillNtupleDColumn(2, xpixel);      // ScintID
            analysisManager->FillNtupleDColumn(3, -1000);      // ypixel -> PixelID is the slice number for scintillator //! per ora -1000 PixelID is undefined for Scintillator
            analysisManager->FillNtupleDColumn(4, locpixelX/cm);
            analysisManager->FillNtupleDColumn(5, locpixelY/cm);
            analysisManager->FillNtupleDColumn(6, locpixelZ/cm);
            analysisManager->FillNtupleDColumn(7, x_primary/cm);
            analysisManager->FillNtupleDColumn(8, y_primary/cm);
            analysisManager->FillNtupleDColumn(9, z_primary/cm);
            analysisManager->FillNtupleDColumn(10, theta_primary/deg);
            analysisManager->FillNtupleDColumn(11, phi_primary/deg);
            analysisManager->FillNtupleDColumn(12, en_primary/keV);
            analysisManager->FillNtupleDColumn(13, lastStepGlobalTime/ns);
            analysisManager->AddNtupleRow();
            

		}
	}
    
    // Process SDD hits
    if(SDDhitsCollection) {
//        G4cout << "*** Debug: Got in SDDhitsCollection: " << SDDhitsCollection << G4endl;
        
        for(int i = 0; i < SDDnumberHits ; i++) {
            SDDDetectorHit* ahit = (*SDDhitsCollection)[i];
            G4double energyDeposit = ahit -> GetEnergyDeposit();
            G4int    ypixel    = ahit -> GetPixelID();
            G4double   locpixelX    = ahit -> GetLocationPixelX();
            G4double   locpixelY    = ahit -> GetLocationPixelY();
            
            G4double lastStepGlobalTime = ahit -> GetTime();
            
            
            // Write energy deposit and pixel in the ROOT file
            analysisManager->FillNtupleDColumn(0, eventID);
            analysisManager->FillNtupleDColumn(1, energyDeposit/keV);
            analysisManager->FillNtupleDColumn(2, -1000);       // ScintID is undefined for SDDs
            analysisManager->FillNtupleDColumn(3, ypixel);      // TODO: verify if the number is correct
            analysisManager->FillNtupleDColumn(4, locpixelX/cm);
            analysisManager->FillNtupleDColumn(5, locpixelY/cm);
            analysisManager->FillNtupleDColumn(6, -1000);       // Z of pixel is undefined for SDDs
            analysisManager->FillNtupleDColumn(7, x_primary/cm);
            analysisManager->FillNtupleDColumn(8, y_primary/cm);
            analysisManager->FillNtupleDColumn(9, z_primary/cm);
            analysisManager->FillNtupleDColumn(10, theta_primary/deg);
            analysisManager->FillNtupleDColumn(11, phi_primary/deg);
            analysisManager->FillNtupleDColumn(12, en_primary/keV);
            analysisManager->FillNtupleDColumn(13, lastStepGlobalTime/ns);
            analysisManager->AddNtupleRow();
            
            
        }
    }


    
    
    
    // Record the event
    G4cout << "---> (Record Event) End of event: " << event -> GetEventID() << G4endl;
    G4Run::RecordEvent(event);
}

void UserRun::Merge(const G4Run* run)
{
    G4Run::Merge(run);
}
