#include "SensitiveDetector.hh"
#include "DetectorHit.hh"
#include "G4HCofThisEvent.hh"
#include "G4Step.hh"
#include "G4TouchableHistory.hh"
#include "G4SDManager.hh"
#include "G4VProcess.hh"
#include "G4StepPoint.hh"
#include "G4SystemOfUnits.hh"
#include "G4PhysicalConstants.hh"

#include <iostream>
#include <unistd.h>

// Constructor.
SensitiveDetector::SensitiveDetector(G4String SDuniqueName,
                                     int nSide,
                                     G4double bar_side, G4double bar_length,
                                     int slice) :
G4VSensitiveDetector(SDuniqueName),
hitsCollectionID(-1),
numberOfBarsPerSide(nSide),
barLength(bar_length),
barSide(bar_side),
sliceLength(bar_length/slice),
numberOfCellsX(nSide*nSide),
numberOfCellsY(slice)
{
    collectionName.insert(SDuniqueName);
    initArray();
}



// Method for the array allocation
void SensitiveDetector::initArray()
{
    // Dynamic allocation of 2-D arrays
	// We use the sizeof() a pointer to ensure 32 and 64-bit portability
	CellID = (int **) std::calloc(numberOfCellsX, sizeof(*CellID));
	for(int iRow=0; iRow < numberOfCellsX; iRow++)
    {
        CellID[iRow] = (int *) std::calloc(numberOfCellsY, sizeof(*CellID));
    }
}



// Destructor
SensitiveDetector::~SensitiveDetector() 
{
    std::free(CellID);
}	



void SensitiveDetector::Initialize(G4HCofThisEvent* hitsCollEvent)
{
    // This method is called at each new event, and it gets the HCofThisEvents
    // for the event.
    hitsCollection = new DetectorHitsCollection(SensitiveDetectorName, collectionName[0]);
    
    hitsCollectionID = G4SDManager::GetSDMpointer()->GetCollectionID(collectionName[0]);
    hitsCollEvent->AddHitsCollection(hitsCollectionID, hitsCollection);
    
    // Initialize vector of cell hits
    for(int iRow=0; iRow < numberOfCellsX; iRow++)
    {
        for(G4int iCol=0; iCol < numberOfCellsY; iCol++)
        {
            CellID[iRow][iCol] = -1;
        }
    }
}


G4bool SensitiveDetector::ProcessHits(G4Step* step, G4TouchableHistory* ROhist) 
{
	// This method is called when a particle goes through a sensitive 
	// detector. It possibly creates a new hit, and add its to the collection.
	// It eventually returns a boolean value. 
	// The arguments it gets are the G4Step and the G4TouchableHistory.
	//
	// The touchable history that we get in this method contains the full information about the 
	// genealogy of the current volume in the read-out geometry. One can use the touchable to 
	// access the information on the position/rotation of a given volume.

	//if(!ROhist) return false;

    // Retrieve the energy deposited from the step
    G4double energyDeposit = step -> GetTotalEnergyDeposit();
    G4double energy_threshold = 0.05 *keV;
    if (energyDeposit < energy_threshold) return false;
    
    G4Track* track = step -> GetTrack();
    G4double globaltime = track -> GetGlobalTime();
    
    
    // debug code
    //G4StepPoint* point1 = step->GetPreStepPoint();
    //const G4VProcess* aProcess1 = point1->GetProcessDefinedStep();
    //G4cout << aProcess1->GetProcessName() << G4endl;
    //G4StepPoint* point2 = step->GetPostStepPoint();
    //const G4VProcess* aProcess2 = point2->GetProcessDefinedStep();
    //G4cout << aProcess2->GetProcessName() << G4endl;
    
	// Get the interaction volume
    G4int replicaNumber = track -> GetVolume() -> GetCopyNo();
    
//    G4ThreeVector translation = track -> GetVolume() -> GetTranslation();
//    G4cout << "*** DEBUG *** translation " << translation/mm << G4endl;


    // Get position
    G4ThreeVector position = track -> GetPosition();
    G4double position_x = position.x();
    G4double position_y = position.y();
    G4double position_z = position.z();
    
    G4double pixelLoc_x ;
    G4double pixelLoc_y ;
    G4double pixelLoc_z ;

    G4int bin_slice = 0;
    

    // ID number of the row
    G4int barNumberX = replicaNumber % numberOfBarsPerSide;
    G4int barNumberY = replicaNumber / numberOfBarsPerSide;
    
    
    

    G4double zCell = 0.*cm;
    
    G4int rowID;
    G4int columnID;
    zCell = - barLength/2.;
    
    rowID = barNumberX - numberOfBarsPerSide/2;
    columnID = barNumberY - numberOfBarsPerSide/2;
    
    if (numberOfBarsPerSide % 2 != 0) {
        // Odd number of bars per side
        pixelLoc_x = rowID*barSide ;
        pixelLoc_y = columnID*barSide ;
    }
    else
    {
        // Even number of bars per side
        pixelLoc_x = rowID*barSide + barSide/2.;
        pixelLoc_y = columnID*barSide + barSide/2.;
    }

    // Get the slice
    G4double slice_length = barLength/numberOfCellsY;
    
    
    bin_slice = (G4int) -position_z/slice_length;
    pixelLoc_z = bin_slice * slice_length;

    
    G4int copyIDinX   =  replicaNumber;
    G4int copyIDinY   =  bin_slice % numberOfCellsY;

    G4int icell = 0;



	if(CellID[copyIDinX][copyIDinY]==-1) // if no energy depositions before in this cell
	{
		// Now we create a new hit object, corresponding to the cell, and fill it with values to be stored
		DetectorHit* hit = new DetectorHit(copyIDinX, copyIDinY);
		hit -> SetEnergyDeposit(energyDeposit);
		hit -> SetScintID(copyIDinX);
		hit -> SetPixelID(copyIDinY);
        hit -> SetLocationPixelX(pixelLoc_x);
        hit -> SetLocationPixelY(pixelLoc_y);
        hit -> SetLocationPixelZ(pixelLoc_z);

		hit -> SetTime(globaltime);
		icell = hitsCollection -> insert(hit);
		CellID[copyIDinX][copyIDinY] = icell - 1;
    }
	else // if energy depositions before in this cell
	{
		(*hitsCollection)[CellID[copyIDinX][copyIDinY]] -> AddEnergyDeposit(energyDeposit);
		(*hitsCollection)[CellID[copyIDinX][copyIDinY]] -> SetTime(globaltime);

	}

	return true;
}


void SensitiveDetector::EndOfEvent(G4HCofThisEvent* hitsCollEvent) 
{
//	// Method called at the end of an event.
//	if(hitsCollectionID < 0) {
//		hitsCollectionID =
//		G4SDManager::GetSDMpointer() -> GetCollectionID(hitsCollection);
//	}
//	// Finally, the hits collection just defined (with its ID and its hits) 
//	// is registered to the HCOfThisEvent of the current event. 
//	hitsCollEvent -> AddHitsCollection(hitsCollectionID, hitsCollection);
}
