//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
/// \file TrackerSD.cc
/// \brief Implementation of the TrackerSD class

// This example is provided by the Geant4-DNA collaboration
// Any report or published results obtained using the Geant4-DNA software
// shall cite the following Geant4-DNA collaboration publications:
// Med. Phys. 45 (2018) e722-e739
// Phys. Med. 31 (2015) 861-874
// Med. Phys. 37 (2010) 4692-4708
// Int. J. Model. Simul. Sci. Comput. 1 (2010) 157–178
// The Geant4-DNA web site is available at http://geant4-dna.org
//

#include "TrackerSD.hh"

#include "G4AnalysisManager.hh"
#include "G4SDManager.hh"
#include "G4SystemOfUnits.hh"
#include "Randomize.hh"

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

TrackerSD::TrackerSD(const G4String& name, const G4String& hitsCollectionName)
  : G4VSensitiveDetector(name), fHitsCollection(nullptr)
{
  collectionName.insert(hitsCollectionName);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

TrackerSD::~TrackerSD() = default;

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void TrackerSD::Initialize(G4HCofThisEvent* hce)
{
  // Create hits collection
  fHitsCollection = new TrackerHitsCollection(SensitiveDetectorName, collectionName[0]);

  // Add this collection in hce
  G4int hcID = G4SDManager::GetSDMpointer()->GetCollectionID(collectionName[0]);

  hce->AddHitsCollection(hcID, fHitsCollection);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4bool TrackerSD::ProcessHits(G4Step* aStep, G4TouchableHistory*)
{
  // Energy deposit
  G4double edep = aStep->GetTotalEnergyDeposit();

  if (edep == 0.) return false;

  auto newHit = new TrackerHit();

  newHit->SetTrackID(aStep->GetTrack()->GetTrackID());
  newHit->SetEdep(edep);
  newHit->SetPos(aStep->GetPostStepPoint()->GetPosition());

  if (aStep->GetTrack()->GetTrackID() == 1 && aStep->GetTrack()->GetParentID() == 0) {
    newHit->SetIncidentEnergy(aStep->GetTrack()->GetVertexKineticEnergy());
  }
  fHitsCollection->insert(newHit);
  // newHit->Print();

  return true;
}

void TrackerSD::SetRadius(const G4double& value)
{
  fRadius = value;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void TrackerSD::EndOfEvent(G4HCofThisEvent*)
{
  G4int nofHits = fHitsCollection->entries();

  G4double Einc = 0;

  /*
  G4cout << G4endl
  << "-------->Hits Collection: in this event they are "
  << nofHits
  << " hits in the target volume " << G4endl;
  */

  // PROCESSING OF MICRODOSIMETRY Y & Z SPECTRA

  // *************************************
  // Please select herebelow :
  // the radius of the target sphere:
  // variable name = radius
  // it is set to 5 nm by default)

  G4double radius = fRadius;

  //

  //***************
  // y and z
  //***************

  // Select random hit
  G4int randHit = 0;  // Runs from 0 to number of hits - 1
  randHit = static_cast<G4int>(G4UniformRand() * nofHits);

  /*
  G4cout
  << "======> random selection of hit number randHit ="
  << randHit << G4endl;
  */

  // Get selected random hit position
  G4ThreeVector hitPos = (*fHitsCollection)[randHit]->GetPos();
  // G4cout << "======> random hit position x/nm =" << hitPos.x()/nm << G4endl;
  // G4cout << "======> random hit position y/nm =" << hitPos.y()/nm << G4endl;
  // G4cout << "======> random hit position z/nm =" << hitPos.z()/nm << G4endl;

  // Set random position of center of sphere within radius
  G4double chord = 4. * radius / 3;
  G4double density = 1 * g / cm3;
  G4double mass = (4. / 3) * CLHEP::pi * radius * radius * radius * density;

  // Random placement of sphere: method 1
  /*
  G4ThreeVector randDir = G4RandomDirection();
  G4double randRadius = G4UniformRand()*radius;
  G4ThreeVector randCenterPos = randRadius*randDir + hitPos;
  */

  // Random placement of sphere: method 2

  G4double xRand = 1.01 * radius;
  G4double yRand = 1.01 * radius;
  G4double zRand = 1.01 * radius;
  G4double randRad = 1.01 * radius;
  do {
    xRand = (2 * G4UniformRand() - 1) * radius;
    yRand = (2 * G4UniformRand() - 1) * radius;
    zRand = (2 * G4UniformRand() - 1) * radius;
    randRad = std::sqrt(xRand * xRand + yRand * yRand + zRand * zRand);
  } while (randRad > radius);

  G4ThreeVector randCenterPos(xRand + hitPos.x(), yRand + hitPos.y(), zRand + hitPos.z());

  // Search for neighbouring hits in the sphere and cumulate deposited energy
  //  in epsilon
  G4double epsilon = 0;
  G4int nbEdep = 0;

  for (G4int i = 0; i < nofHits; i++) {
    if ((*fHitsCollection)[i]->GetIncidentEnergy() > 0)
      Einc = (*fHitsCollection)[i]->GetIncidentEnergy();

    G4ThreeVector localPos = (*fHitsCollection)[i]->GetPos();

    // G4cout << i << " " << (*fHitsCollection)[i] << G4endl;
    // G4cout << i << " " << (*fHitsCollection)[i]->GetEdep()/eV << G4endl;

    if ((localPos.x() - randCenterPos.x()) * (localPos.x() - randCenterPos.x())
          + (localPos.y() - randCenterPos.y()) * (localPos.y() - randCenterPos.y())
          + (localPos.z() - randCenterPos.z()) * (localPos.z() - randCenterPos.z())
        <= radius * radius)

    {
      epsilon = epsilon + (*fHitsCollection)[i]->GetEdep();
      nbEdep = nbEdep + 1;
    }
  }

  // For testing only
  /*
  G4cout << "======> for hit number #" << randHit <<
  ", we collect "
  << nbEdep << " energy depositions in a sphere of radius "
  << radius/nm << " nm and mass "
  << mass/kg << " kg for a total of "
  << epsilon/eV << " eV or "
  << (epsilon/joule)/(mass/kg) << " Gy" << G4endl;
  G4cout << "-" << G4endl;
  */

  /*
  FILE* myFile;
  myFile=fopen("yz.txt","a");
  fprintf(myFile,"%e %e %e\n",radius/nm,(epsilon/eV)/(chord/nm),
   (epsilon/joule)/(mass/kg));
  fclose(myFile);
  */

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

  // Fill ntuple including weighting
  analysisManager->FillNtupleDColumn(0, radius / nm);
  analysisManager->FillNtupleDColumn(2, nofHits);
  analysisManager->FillNtupleDColumn(3, nbEdep);
  analysisManager->FillNtupleDColumn(4, (epsilon / eV) / (chord / nm));
  analysisManager->FillNtupleDColumn(5, (epsilon / mass) / gray);
  analysisManager->FillNtupleDColumn(6, Einc / eV);
  analysisManager->AddNtupleRow();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
