#include "SDDSensitiveDetector.hh"
#include "SDDDetectorHit.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.
SDDSensitiveDetector::SDDSensitiveDetector(G4String SDuniqueName,
                                     G4double cSide, G4int nX, G4int nY) :
G4VSensitiveDetector(SDuniqueName),
hitsCollectionID(-1),
cellSide(cSide),
numberOfCellsX(nX),
numberOfCellsY(nY)
{
    collectionName.insert(SDuniqueName);
    initArray();
}



// Method for the array allocation
void SDDSensitiveDetector::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
SDDSensitiveDetector::~SDDSensitiveDetector() 
{
    std::free(CellID);
}




void SDDSensitiveDetector::Initialize(G4HCofThisEvent* hitsCollEvent)
{
    // This method is called at each new event, and it gets the HCofThisEvents
    // for the event.
    hitsCollection = new SDDDetectorHitsCollection(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 SDDSensitiveDetector::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;
    
    
//    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();

    // ID number of the row
    G4double sddSideX = numberOfCellsX*cellSide;
    G4double sddSideY = numberOfCellsY*cellSide;

    G4double position_x_reno = position_x + sddSideX/2.;
    G4double position_y_reno = position_y + sddSideY/2.;

    G4int copyIDinX   =  (G4int) position_x_reno/cellSide;
    G4int copyIDinY   =  (G4int) position_y_reno/cellSide;
    

    G4double pixelLoc_x =  copyIDinX*cellSide - sddSideX/2. + cellSide/2.;
    G4double pixelLoc_y =  copyIDinY*cellSide - sddSideY/2. + cellSide/2.;

    G4int icell = 0;

    if (copyIDinX == numberOfCellsX) {--copyIDinX;}
    if (copyIDinY == numberOfCellsY) {--copyIDinY;}
    
    G4int pixelID = copyIDinX + copyIDinY*numberOfCellsY;

	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
		SDDDetectorHit* hit = new SDDDetectorHit(copyIDinX, copyIDinY);
        hit -> SetEnergyDeposit(energyDeposit);
        hit -> SetPixelID(pixelID);
        hit -> SetLocationPixelX(pixelLoc_x);
        hit -> SetLocationPixelY(pixelLoc_y);
        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 SDDSensitiveDetector::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);
}
