#include "DetectorConstruction.hh"
#include "ModuleParameterisation.hh"
#include "MaskParameterisation.hh"
#include "PCBParameterisation.hh"
#include "SDDSensitiveDetector.hh"
#include "SensitiveDetector.hh"

#include "G4SystemOfUnits.hh"
#include "G4PhysicalConstants.hh"

#include "G4NistManager.hh"
#include "G4RunManager.hh"
#include "G4RegionStore.hh"
#include "G4SDManager.hh"
#include "G4Material.hh"
#include "G4Box.hh"
#include "G4VSolid.hh"
#include "G4SubtractionSolid.hh"
#include "G4Tubs.hh"
#include "G4Polyhedra.hh"
#include "G4Trap.hh"
#include "G4RotationMatrix.hh"
#include "G4LogicalVolume.hh"
#include "G4ThreeVector.hh"
#include "G4PVPlacement.hh"
#include "G4PVReplica.hh"
#include "G4Transform3D.hh"
#include "G4VPVParameterisation.hh"
#include "G4PVParameterised.hh"
#include "globals.hh"

#include "G4GeometryManager.hh"
#include "G4PhysicalVolumeStore.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4SolidStore.hh"
#include "G4SDManager.hh"

#include "G4VisAttributes.hh"
#include "G4Colour.hh"

#include "G4GDMLParser.hh"

#include <iostream>
#include <fstream>
#include <vector>
#include <string>

#include "ConfigFile.hh"

// Constructor 
DetectorConstruction::DetectorConstruction() 
:	experimentalHall_log(0), 
	scint_log(0),
    sddUpper_log(0),
    sddLower_log(0),
    optCoupler_log(0),
    polyprop_log(0),
    coll_sideXp_log(0),
    coll_sideXm_log(0),
    coll_sideYp_log(0),
    coll_sideYm_log(0),
    bus1_log(0),
    bus2_log(0),
    mask_log(0),
    kapton_log(0),
    aluminum_log(0),
    coll_upper1Xp_log(0),
    coll_upper1Xm_log(0),
    coll_upper1Yp_log(0),
    coll_upper1Ym_log(0),
    coll_upper2Xp_log(0),
    coll_upper2Xm_log(0),
    coll_upper2Yp_log(0),
    coll_upper2Ym_log(0), 
	scintParam(0),
    maskParam(0),
	experimentalHall_phys(0), 
	scint_phys(0),
    sddUpper_phys(0),
    sddLower_phys(0),
    optCoupler_phys(0),
    polyprop_phys(0),
    coll_sideXp_phys(0),
    coll_sideXm_phys(0),
    coll_sideYp_phys(0),
    coll_sideYm_phys(0),
    bus1_phys(0),
    bus2_phys(0),
    mask_phys(0),
    kapton_phys(0),
    aluminum_phys(0),
    coll_upper1Xp_phys(0),
    coll_upper1Xm_phys(0),
    coll_upper1Yp_phys(0),
    coll_upper1Ym_phys(0),
    coll_upper2Xp_phys(0),
    coll_upper2Xm_phys(0),
    coll_upper2Yp_phys(0),
    coll_upper2Ym_phys(0),
	defaultMaterial(0)
{
	// Size of the experimental hall
    worldSide         = 100.0 *m;

	// Define the materials
	DefineMaterials();	

	// Define the parameters
	DefineParameters();
		
	// Print parameters
	PrintParameters();
	
;}		



// Destructor 
DetectorConstruction::~DetectorConstruction() { } 	



// Definition of the parameters
void DetectorConstruction::DefineParameters()
{ 
		
	// New configuration file readout!	
	// Uses the ConfigFile class by Rick Wagner
	// *** NOTE: Parameter filename should not be hardcoded!
	ConfigFile config( "detector_params.inp" );
		
	// The default parameters are inserted as the third argument
	// of the readInto function.
	// Geometrical dimensions
	// Using a std::double variable as a pivot btv. ConfigFile and Geant doubles (with units)
	double read = 0.;
	int read_int = 0;
	config.readInto(read, "SCINT_X", 0.45);
	G4double s_x = read *cm;
	config.readInto(read, "SCINT_Y", 0.45);
	G4double s_y = read *cm;
	config.readInto(read, "SCINT_Z", 3.0);
	G4double s_z = read *cm;
    config.readInto(read, "SCINT_GAP", 0.05);
    G4double s_g = read *cm;
	config.readInto(read_int, "N_SIDE", 10);
	G4int nSide = read_int;
	config.readInto(read_int, "N_SLICES_PER_BAR", 80);
	G4int slicesPerBar = read_int;
    config.readInto(read_int, "N_MASK_ELEM_PER_SIDE", 8);
    G4int maskelemside = read_int;
    config.readInto(read, "COLL_SIDE_THICK", 0.1);
    G4double c_t = read *cm;
    config.readInto(read, "BUS_THICK", 10.);
    G4double b_t = read *cm;
    config.readInto(read, "BUS_HEIGHT", 150.);
    G4double b_h = read *cm;
    config.readInto(read, "BUS_SIDE", 100.);
    G4double b_s = read *cm;

    config.readInto(read, "SDD_THICK", 450.);
    G4double s_t = read *um;
    config.readInto(read, "MASK_X", 50.);
    G4double m_x = read *cm;
    config.readInto(read, "MASK_Y", 50.);
    G4double m_y = read *cm;
    config.readInto(read, "MASK_THICK", 3000.);
    G4double m_t = read *um;
    config.readInto(read, "MASK_FOCAL", 70.);
    G4double m_f = read *cm;
    config.readInto(read, "COLL_UPPER_1_THICK", 1000.);
    G4double cu1_t = read *um;
    config.readInto(read, "COLL_UPPER_2_THICK", 1000.);
    G4double cu2_t = read *um;
    config.readInto(read, "COLL_UPPER_2_HEIGHT", 70.);
    G4double cu2_h = read *cm;
    config.readInto(read, "OPTICAL_COUPLER_THICK", 1.);
    G4double opt_t = read *mm;
    config.readInto(read, "POLYPROPYLENE_THICK", 15.);
    G4double p_t = read *um;
    config.readInto(read, "KAPTON_THICK", 7.6);
    G4double k_t = read *um;
    config.readInto(read, "ALUMINUM_THICK", 0.2);
    G4double a_t = read *um;



	SetScintX(s_x) ;
	SetScintY(s_y) ;
	SetScintZ(s_z) ;
    SetScintGap(s_g) ;
    SetNumberOfBars(nSide);
    SetNumberOfSlicesPerBar(slicesPerBar);
    SetNumberOfMaskElementsPerSide(maskelemside);
    SetCollSideThick(c_t) ;
    SetBusThick(b_t) ;
    SetBusHeight(b_h) ;
    SetBusSide(b_s) ;
    SetCollSideHeight(s_z+2*s_t+opt_t);
    SetSDDThick(s_t) ;
    SetMaskSideX(m_x) ;
    SetMaskSideY(m_y) ;
    SetMaskFocalDistance(m_f) ;
    SetMaskThick(m_t) ;
    SetCollUpper1Thick(cu1_t) ;
    SetCollUpper2Thick(cu2_t) ;
    SetCollUpper2Height(cu2_h) ;
    SetOptCouplerThick(opt_t) ;
    SetPolypropThick(p_t) ;
    SetKapThick(k_t) ;
    SetAlumThick(a_t) ;


	// Materials
	G4String readm;
	config.readInto<G4String>(readm, "SCINT_MATERIAL", "G4_CESIUM_IODIDE");
	SetScintMaterial(readm);
    config.readInto<G4String>(readm, "COLL_SIDE_MATERIAL", "G4_W");
    SetCollSideMaterial(readm);
    config.readInto<G4String>(readm, "BUS_MATERIAL", "G4_W");
    SetBusMaterial(readm);
    config.readInto<G4String>(readm, "SDD_MATERIAL", "G4_Si");
    SetSDDMaterial(readm);
    config.readInto<G4String>(readm, "MASK_MATERIAL", "G4_W");
    SetMaskMaterial(readm);
    config.readInto<G4String>(readm, "COLL_UPPER_1_MATERIAL", "G4_Al");
    SetCollUpper1Material(readm);
    config.readInto<G4String>(readm, "COLL_UPPER_2_MATERIAL", "G4_W");
    SetCollUpper2Material(readm);
    config.readInto<G4String>(readm, "OPTICAL_COUPLER_MATERIAL", "Silicone");
    SetOptCouplerMaterial(readm);
    config.readInto<G4String>(readm, "POLYPROPYLENE_MATERIAL", "G4_POLYPROPYLENE");
    SetPolyMaterial(readm);
    config.readInto<G4String>(readm, "KAPTON_MATERIAL", "G4_KAPTON");
    SetKapMaterial(readm);
    config.readInto<G4String>(readm, "ALUMINUM_MATERIAL", "G4_Al");
    SetAlumMaterial(readm);

}


// Definition of the materials
void DetectorConstruction::DefineMaterials()
{ 
	G4double a;			// Atomic mass
	G4double z;			// Atomic number
	G4double density;	// Density
	G4int nel;			// Number of elements in a compound
    G4int natoms;
	G4int ncomponents;	// Number of components in a mixture
	G4double fractionmass;
	
	// Elements
	G4Element*  H  = new G4Element("Hydrogen"  , "H" , z = 1. , a =  1.008*g/mole);
	G4Element*  He = new G4Element("Helium"    , "He", z = 2. , a =  4.003*g/mole);
    G4Element*  C  = new G4Element("Carbon"    , "C" , z = 6. , a =  12.01*g/mole);
    G4Element*  N  = new G4Element("Nitrogen"  , "N" , z = 7. , a =  14.01*g/mole);
    G4Element*  O  = new G4Element("Oxygen"    , "O" , z = 8. , a =  16.00*g/mole);
    // G4Element*  Na = new G4Element("Sodium"    , "Na", z = 11., a =  22.99*g/mole);
	G4Element*  Al = new G4Element("Aluminium" , "Al", z = 13., a =  26.98*g/mole);
	G4Element*  Si = new G4Element("Silicon"   , "Si", z = 14., a =  28.08*g/mole);
    // G4Element*  K  = new G4Element("Potassium" , "K" , z = 19., a =  39.10*g/mole);
    // G4Element*  Ti = new G4Element("Titanium"  , "Ti", z = 22., a =  47.87*g/mole);
	G4Element*  Cu = new G4Element("Copper"    , "Cu", z = 29., a =  63.55*g/mole);
    G4Element*  Ge = new G4Element("Germanium"   , "Ge", z = 32., a =  72.63*g/mole);
    // G4Element*  As = new G4Element("Arsenic"   , "As", z = 33., a =  74.92*g/mole);
	G4Element*  Br = new G4Element("Bromine"   , "Br", z = 35., a =  79.90*g/mole);
    // G4Element*  Mo = new G4Element("Molybdenum", "Mo", z = 42., a =  95.94*g/mole);
    G4Element*  I  = new G4Element("Iodine"    , "I" , z = 53., a = 126.90*g/mole);
    G4Element*  Cs = new G4Element("Caesium"   , "Cs", z = 55., a = 132.90*g/mole);
    G4Element*  La = new G4Element("Lanthanium", "La", z = 57., a = 138.90*g/mole);
    // G4Element*  Ta = new G4Element("Tantalum"  , "Ta", z = 73., a = 180.94*g/mole);
    G4Element*  W  = new G4Element("Tungsten"  , "W" , z = 74., a = 183.84*g/mole);
    // G4Element*  Pb = new G4Element("Lead"      , "Pb", z = 82., a = 207.20*g/mole);
    G4Element*  Bi = new G4Element("Bismuth"   , "Bi", z = 83., a = 208.98*g/mole);

	// Materials 
	// Vacuum
	G4Material* Vacuum = new G4Material("Vacuum", density = 1.e-25*g/cm3, nel = 1);
	Vacuum -> AddElement(H, 100*perCent);


//	// Silicon Solid
//	G4Material* SiliconSolid = new G4Material("SiliconSolid", density = 2.33*g/cm3, nel = 1);
//	SiliconSolid -> AddElement(Si, 100*perCent);
//
//	// Aluminium Solid
//	G4Material* AluminiumSolid = new G4Material("AluminiumSolid", density = 2.700*g/cm3, nel = 1);
//	AluminiumSolid -> AddElement(Al, 100*perCent);
//
//	// Copper Solid
//	G4Material* CopperSolid = new G4Material("CopperSolid", density = 8.94*g/cm3, nel = 1);
//	CopperSolid -> AddElement(Cu, 100*perCent);
//    
//    // Tungsten Solid
//    G4Material* TungstenSolid = new G4Material("TungstenSolid", density = 19.25*g/cm3, nel = 1);
//    TungstenSolid -> AddElement(W, 100*perCent);


    // From NIST database
    G4NistManager* man = G4NistManager::Instance();
    G4Material* G4_CESIUM_IODIDE = man->FindOrBuildMaterial("G4_CESIUM_IODIDE");
    G4Material* G4_Si = man->FindOrBuildMaterial("G4_Si");
    G4Material* G4_W = man->FindOrBuildMaterial("G4_W");
    G4Material* G4_BGO = man->FindOrBuildMaterial("G4_BGO");
    G4Material* G4_AIR = man->FindOrBuildMaterial("G4_AIR");
    G4Material* G4_Al = man->FindOrBuildMaterial("G4_Al");
    G4Material* G4_Fe = man->FindOrBuildMaterial("G4_Fe");
    G4Material* G4_POLYPROPYLENE = man->FindOrBuildMaterial("G4_POLYPROPYLENE");
    G4Material* G4_KAPTON = man->FindOrBuildMaterial("G4_KAPTON");
    G4Material* G4_SILICON_DIOXIDE = man->FindOrBuildMaterial("G4_SILICON_DIOXIDE");

    // LaBr3O
    G4Material* LaBr3 = new G4Material("LaBr3", density = 5.06 *g/cm3, nel = 2);
    LaBr3 -> AddElement(La, 36.68*perCent);
    LaBr3 -> AddElement(Br, 63.32*perCent);
	
    // Silicone
    G4Material* Silicone = new G4Material("Silicone", density = 1.08 *g/cm3, nel = 2);
    Silicone -> AddElement(Si, natoms=1);
    Silicone -> AddElement(O,  natoms=2);

    // Diglycidyl Ether of Bisphenol A (First compound of epoxy resin Epotek 301-1)
    G4Material* Epoxy_1 = new G4Material("Epoxy_1", density = 1.16*g/cm3, nel = 3);
    Epoxy_1 -> AddElement(C, natoms=19);
    Epoxy_1 -> AddElement(H, natoms=20);
    Epoxy_1 -> AddElement(O, natoms=4);
    
    // 1,4-Butanediol Diglycidyl Ether (Second compound of epoxy resin Epotek 301-1)
    G4Material* Epoxy_2 = new G4Material("Epoxy_2", density = 1.10*g/cm3, nel = 3);
    Epoxy_2 -> AddElement(C, natoms=10);
    Epoxy_2 -> AddElement(H, natoms=18);
    Epoxy_2 -> AddElement(O, natoms=4);
    
    // 1,6-Hexanediamine 2,2,4-trimetyl (Third compound of epoxy resin Epotek 301-1)
    G4Material* Epoxy_3 = new G4Material("Epoxy_3", density = 1.16*g/cm3, nel = 3);
    Epoxy_3 -> AddElement(C, natoms=9);
    Epoxy_3 -> AddElement(H, natoms=22);
    Epoxy_3 -> AddElement(N, natoms=2);
    
    // Epoxy resin Epotek 301-1
    G4Material* Epoxy_Resin = new G4Material("Epoxy_Resin", density = 1.19*g/cm3, ncomponents=3);
    Epoxy_Resin -> AddMaterial(Epoxy_1, fractionmass=56*perCent);
    Epoxy_Resin -> AddMaterial(Epoxy_2, fractionmass=24*perCent);
    Epoxy_Resin -> AddMaterial(Epoxy_3, fractionmass=20*perCent);
    
    // FR4 PCB material
    G4Material* FR4 = new G4Material("FR4", density = 1.8*g/cm3, ncomponents=2);
    FR4 -> AddMaterial(G4_SILICON_DIOXIDE, fractionmass=60*perCent);
    FR4 -> AddMaterial(Epoxy_Resin,  fractionmass=40*perCent);
    fr4Material = FR4; // G4Material::GetMaterial("FR4");

	// Default materials of the World: vacuum
	defaultMaterial  = Vacuum;
}


// Detector construction
G4VPhysicalVolume* DetectorConstruction::Construct()
{
	// Clean old geometry, if any
	G4GeometryManager::GetInstance()->OpenGeometry();
	G4PhysicalVolumeStore::GetInstance()->Clean();
	G4LogicalVolumeStore::GetInstance()->Clean();
	G4SolidStore::GetInstance()->Clean();



	// ! Experimental hall (world volume)
    
	// Solid
	G4Box* experimentalHall_box = new G4Box("experimentalHall_box",
									worldSide/2., worldSide/2., worldSide/2.);
	// Logical
	experimentalHall_log  = new G4LogicalVolume(experimentalHall_box,
                                            defaultMaterial, "experimentalHall_log", 0, 0, 0);
	// Physical
	experimentalHall_phys = new G4PVPlacement(0,
											G4ThreeVector(0,0,0),
											experimentalHall_log,
											"experimentalHall_phys",
											0,
											false,
											0);
    
    
    // ! Scintillator
    
    // Module parameterisation
    G4int numberOfBars = n_side*n_side;
    G4cout << "Number of scintillator bars: " << numberOfBars << G4endl;

    // Scintillation logical container
    G4double scint_container_side_x = (scint_x + scint_gap)*n_side;
    G4double scint_container_side_y = (scint_y + scint_gap)*n_side;
    G4double scint_container_side_z = scint_z;
    G4Box* scint_container_box = new G4Box("scint_container_box",
                                            scint_container_side_x/2., scint_container_side_y/2., scint_container_side_z/2.);

    G4LogicalVolume* scint_container_log = new G4LogicalVolume(scint_container_box,
                                                                defaultMaterial, "scint_container_log", 0, 0, 0);

    G4VPhysicalVolume* scint_container_phys = new G4PVPlacement(0,
                                                                G4ThreeVector(0.,0.,-scint_z/2.),
                                                                scint_container_log,
                                                                "scint_container_phys",
                                                                experimentalHall_log,
                                                                false,
                                                                0);

	// Solid
	G4Box* scint_box = new G4Box("scint_box", scint_x/2., scint_y/2., scint_z/2.);
	// Logical
	scint_log = new G4LogicalVolume(scint_box, scintMaterial, "scint_log", 0, 0, 0);
	// Physical
	scintParam = new ModuleParameterisation(scint_x+scint_gap, scint_z, n_side);
	
	scint_phys = new G4PVParameterised("scint_phys",       	// their name
									 scint_log,    	        // their logical volume
									 scint_container_log,  // Mother logical volume
									 kXAxis,          		// Are placed along this axis 
									 numberOfBars,      // Number of replicas
									 scintParam);   		// The parametrisation


    // ! Side Collimator
    
    G4double coll_side_x = scint_container_side_x;
    G4double coll_side_y = scint_container_side_y;

    // Solid
    G4Box* coll_sideXp_box = new G4Box("coll_sideXp_box", coll_side_thick/2., coll_side_x/2., coll_side_height/2.);
    // Logical
    coll_sideXp_log = new G4LogicalVolume(coll_sideXp_box, collSideMaterial, "coll_sideXp_log", 0, 0, 0);
    // Physical
    G4double coll_displacement_x = (coll_side_x/2. + coll_side_thick/2.);
    G4double coll_displacement_y = 0;
    G4double coll_displacement_z = -scint_z + coll_side_height/2.;
    coll_sideXp_phys = new G4PVPlacement(0,
                                        G4ThreeVector(coll_displacement_x,coll_displacement_y,coll_displacement_z),
                                        coll_sideXp_log,
                                        "coll_sideXp_phys",
                                        experimentalHall_log,
                                        false,
                                        0);
    
    // Solid
    G4Box* coll_sideXm_box = new G4Box("coll_sideXm_box", coll_side_thick/2., coll_side_x/2., coll_side_height/2.);
    // Logical
    coll_sideXm_log = new G4LogicalVolume(coll_sideXm_box, collSideMaterial, "coll_sideXm_log", 0, 0, 0);
    // Physical
    coll_displacement_x = -(coll_side_x/2. + coll_side_thick/2.);
    coll_displacement_y = 0.;
    coll_displacement_z = -scint_z + coll_side_height/2.;
    coll_sideXm_phys = new G4PVPlacement(0,
                                        G4ThreeVector(coll_displacement_x,coll_displacement_y,coll_displacement_z),
                                        coll_sideXm_log,
                                        "coll_sideXm_phys",
                                        experimentalHall_log,
                                        false,
                                        0);
    
    
    // Solid
    G4Box* coll_sideYp_box = new G4Box("coll_sideYp_box", coll_side_y/2. +coll_side_thick, coll_side_thick/2., coll_side_height/2.);
    // Logical
    coll_sideYp_log = new G4LogicalVolume(coll_sideYp_box, collSideMaterial, "coll_sideYp_log", 0, 0, 0);
    // Physical
    coll_displacement_x = 0.;
    coll_displacement_y = (coll_side_y/2. + coll_side_thick/2.);
    coll_displacement_z = -scint_z + coll_side_height/2.;
    coll_sideYp_phys = new G4PVPlacement(0,
                                        G4ThreeVector(coll_displacement_x,coll_displacement_y,coll_displacement_z),
                                        coll_sideYp_log,
                                        "coll_sideYp_phys",
                                        experimentalHall_log,
                                        false,
                                        0);
    
    // Solid
    G4Box* coll_sideYm_box = new G4Box("coll_sideYm_box", coll_side_y/2.+coll_side_thick, coll_side_thick/2., coll_side_height/2.);
    // Logical
    coll_sideYm_log = new G4LogicalVolume(coll_sideYm_box, collSideMaterial, "coll_sideYm_log", 0, 0, 0);
    // Physical
    coll_displacement_x = 0.;
    coll_displacement_y = -(coll_side_y/2. + coll_side_thick/2.);
    coll_displacement_z = -scint_z + coll_side_height/2.;
    coll_sideYm_phys = new G4PVPlacement(0,
                                        G4ThreeVector(coll_displacement_x,coll_displacement_y,coll_displacement_z),
                                        coll_sideYm_log,
                                        "coll_sideYm_phys",
                                        experimentalHall_log,
                                        false,
                                        0);
    
    
    // ! Spacecraft bus (box approximation)
    // TODO: insert here new S/C model (hexagonal, rotated wrt XGIS unit)
    
    // Solid
    G4Box* bus1_box = new G4Box("bus1_box",
                                        (coll_side_x + 2*coll_side_thick)/2., (coll_side_y + 2*coll_side_thick)/2., bus_thick/2.);
    // Logical
    bus1_log = new G4LogicalVolume(bus1_box, busMaterial, "bus1_log", 0, 0, 0);
    // Physical
    coll_displacement_x = 0.;
    coll_displacement_y = 0;
    coll_displacement_z = -scint_z - optCoupler_thick - sdd_thick  - bus_thick/2.;
    bus1_phys = new G4PVPlacement(0,
                                    G4ThreeVector(coll_displacement_x,coll_displacement_y,coll_displacement_z),
                                    bus1_log,
                                    "bus1_phys",
                                    experimentalHall_log,
                                    false,
                                    0);

//    // Solid
//    G4Box* bus1_box = new G4Box("bus1_box",
//                                (coll_side_x + 2*coll_side_thick)/2., (coll_side_y + 2*coll_side_thick)/2., bus_thick/2.);
//
//    // Logical
//    bus1_log = new G4LogicalVolume(bus1_box, busMaterial, "bus1_log", 0, 0, 0);
//    // Physical
//    coll_displacement_x = 0.;
//    coll_displacement_y = 0;
//    coll_displacement_z = -scint_z - optCoupler_thick - sdd_thick  - bus_thick/2.;
//    bus1_phys = new G4PVPlacement(0,
//                                  G4ThreeVector(coll_displacement_x,coll_displacement_y,coll_displacement_z),
//                                  bus1_log,
//                                  "bus1_phys",
//                                  experimentalHall_log,
//                                  false,
//                                  0);

    
    // ! SDDs
    
    // Solid
    G4Box* sdd_box = new G4Box("sdd_box", coll_side_x/2., coll_side_y/2., sdd_thick/2.);
    // Logical
    sddUpper_log = new G4LogicalVolume(sdd_box, sddMaterial, "sddUpper_log", 0, 0, 0);
    sddLower_log = new G4LogicalVolume(sdd_box, sddMaterial, "sddLower_log", 0, 0, 0);

    // Physical
    G4double sddUpper_x = 0;
    G4double sddUpper_y = 0;
    G4double sddUpper_z = sdd_thick/2. + optCoupler_thick;
    sddUpper_phys = new G4PVPlacement(0,
                                        G4ThreeVector(sddUpper_x,sddUpper_y,sddUpper_z),
                                        sddUpper_log,
                                        "sddUpper_phys",
                                        experimentalHall_log,
                                        false,
                                        0);

    G4double sddLower_x = 0;
    G4double sddLower_y = 0;
    G4double sddLower_z = -scint_z - sdd_thick/2.;
    sddLower_phys = new G4PVPlacement(0,
                                        G4ThreeVector(sddLower_x,sddLower_y,sddLower_z),
                                        sddLower_log,
                                        "sddLower_phys",
                                        experimentalHall_log,
                                        false,
                                        0);
    
    
    // ! Optical coupler
    
    // Solid
    G4Box* optCoupler_box = new G4Box("sdd_box", coll_side_x/2., coll_side_y/2., sdd_thick/2.);
    // Logical
    optCoupler_log = new G4LogicalVolume(optCoupler_box, optCouplerMaterial, "optCoupler_log", 0, 0, 0);
    
    // Physical
    G4double optCoupler_x = 0;
    G4double optCoupler_y = 0;
    G4double optCoupler_z = optCoupler_thick/2.;
    optCoupler_phys = new G4PVPlacement(0,
                                        G4ThreeVector(optCoupler_x,optCoupler_y,optCoupler_z),
                                        optCoupler_log,
                                        "optCoupler_phys",
                                        experimentalHall_log,
                                        false,
                                        0);


    // ! PCB

    // Solid
    G4double pcb_side_x = coll_side_x;
    G4double pcb_side_y = coll_side_y;
    G4double pcb_thick  = 1.6 *mm;       // ! si potrebbe mettere come parametro
    G4double pcb_height = optCoupler_thick+sdd_thick+pcb_thick;

    G4Box* pcb_container_box = new G4Box("pcb_container_box", pcb_side_x/2., pcb_side_y/2., pcb_thick/2.);
    
    // Logical
    G4LogicalVolume* pcb_container_log = new G4LogicalVolume(pcb_container_box, defaultMaterial, "pcb_container_log", 0, 0, 0);

    // Physical
    G4VPhysicalVolume* pcb_container_phys = new G4PVPlacement(0,
                                                            G4ThreeVector(0,0,pcb_height),
                                                            pcb_container_log,
                                                            "pcb_container_phys",
                                                            experimentalHall_log,
                                                            false,
                                                            0);


	// Solid
    G4double pcb_elem_side_x = 3.*mm;
    G4double pcb_elem_side_y = pcb_side_y;
    G4cout << "Materiale FR4: " << fr4Material->GetName() << G4endl;

	G4Box* pcb_box = new G4Box("pcb_box", pcb_elem_side_x/2., pcb_elem_side_y/2., pcb_thick/2.);
	
    // Logical
	pcb_log = new G4LogicalVolume(pcb_box, fr4Material, "pcb_log", 0, 0, 0);
    
	// Physical
    G4int n_rep = (n_side-1)/2; // repliche di PCB usando il numero di scintillatori per side

	pcbParam = new PCBParameterisation(pcb_elem_side_x, pcb_elem_side_y, pcb_height, pcb_thick, n_rep);

   G4cout << " pcb_elem_side_x " << pcb_elem_side_x/mm << G4endl;
   G4cout << " pcb_elem_side_y " << pcb_elem_side_y/mm << G4endl;
   G4cout << " pcb_height " << pcb_height << G4endl;
   G4cout << " pcb_thick " << pcb_thick/mm << G4endl;

   G4cout << " pcb_sie_x " << pcb_side_x << G4endl;
   G4cout << " n_rep " << n_rep << G4endl;

	
	pcb_phys = new G4PVParameterised("pcb_phys",       	// their name
									 pcb_log,    	        // their logical volume
									 pcb_container_log,  // Mother logical volume
									 kXAxis,          		// Are placed along this axis 
									 n_rep,            // Number of replicas
									 pcbParam);   		// The parametrisation


    // ! Mask

    // Solid
    G4Box* mask_container_box = new G4Box("mask_box", mask_side_x/2., mask_side_y/2., mask_thick/2.);

    // Logical
    G4LogicalVolume* mask_container_log = new G4LogicalVolume(mask_container_box, defaultMaterial, "mask_container_log", 0, 0, 0);
    
    // Physical
    G4VPhysicalVolume* mask_container_phys = new G4PVPlacement(0,
                                                                G4ThreeVector(0,0,mask_focalDistance+sdd_thick+optCoupler_thick),
                                                                mask_container_log,
                                                                "mask_container_phys",
                                                                experimentalHall_log,
                                                                false,
                                                                0);
    

    // G4double mask_elem_side_x = mask_side_x/(2*n_mask_elem_side);
    // G4double mask_elem_side_y = mask_side_y/(2*n_mask_elem_side);
    // G4int n_mask_elem_replicas = (2*n_mask_elem_side) * (2*n_mask_elem_side) * 1/2;
    
    G4double mask_elem_side_x = 1.027*cm;
    G4double mask_elem_side_y = 1.027*cm;
    //G4double mask_elem_side_x = (mask_side_x/n_mask_elem_side)*cm;
    //G4double mask_elem_side_y = (mask_side_y/n_mask_elem_side)*cm;
    G4double gap = (mask_side_x/n_mask_elem_side) - mask_elem_side_x;
    
    // Solid
    G4Box* mask_box = new G4Box("mask_box", mask_elem_side_x/2., mask_elem_side_y/2., mask_thick/2.);

    // Logical
    mask_log = new G4LogicalVolume(mask_box, maskMaterial, "mask_log", 0, 0, 0);

    // Creazione dell'array per le posizioni degli zeri
    std::vector<std::pair<int, int>> zeroPositions;

    // Physical
    MaskParameterisation* maskParam = new MaskParameterisation(mask_elem_side_x, mask_elem_side_y, n_mask_elem_side, gap, "mask.dat");

    int totalZeros = maskParam->GetTotalZeros();
    
    if (totalZeros != -1) {
        // for (const auto& pos : zeroPositions) {
        //     G4cout << "Zero a posizione: (" << pos.first << ", " << pos.second << ")" << G4endl;
        // }
        // G4cout << "Numero totale di zeri: " << totalZeros << G4endl;
        // G4cout << "gap: " << gap << G4endl;

        // Posizionamento parametrizzato delle repliche
        mask_phys = new G4PVParameterised(
                                        "mask_phys",            // Nome
                                        mask_log,               // Logica dell'elemento maschera
                                        mask_container_log,     // Volume madre
                                        kXAxis,                 // Asse lungo cui avviene il posizionamento
                                        totalZeros,             // Numero di repliche
                                        maskParam               // Parametrizzazione
            );
    } else {
        G4cerr << "Errore durante il caricamento del file mask.dat." << G4endl;
    }
    
    // mask_phys = new G4PVParameterised("mask_phys",           // their name
    //                                    mask_log,                // their logical volume
    //                                    mask_container_log,  // Mother logical volume
    //                                    kXAxis,                  // Are placed along this axis
    //                                    n_mask_elem_replicas,      // Number of replicas
    //                                    maskParam);           // The parametrisation


    // ! MLI Kapton + Aluminum

    // Solid
    G4Box* kapton_box = new G4Box("kapton_box", mask_side_x/2., mask_side_y/2., kap_thick/2.);
    G4Box* aluminum_box = new G4Box("aluminum_box", mask_side_x/2., mask_side_y/2., al_thick/2.);

    // Logical
    kapton_log = new G4LogicalVolume(kapton_box, kapMaterial, "kapton_log", 0, 0, 0);
    aluminum_log = new G4LogicalVolume(aluminum_box, alumMaterial, "aluminum_log", 0, 0, 0);

    // Physical
    G4double kapton_x = 0;
    G4double kapton_y = 0;
    G4double kapton_z = optCoupler_thick + sdd_thick + mask_focalDistance + mask_thick + kap_thick/2.;
    kapton_phys = new G4PVPlacement(0,
                                    G4ThreeVector(kapton_x,kapton_y,kapton_z),
                                    kapton_log,
                                    "kapton_phys",
                                    experimentalHall_log,
                                    false,
                                    0);
    G4double aluminum_x = 0;
    G4double aluminum_y = 0;
    G4double aluminum_z = optCoupler_thick + sdd_thick + mask_focalDistance + mask_thick + kap_thick + al_thick/2.;
    aluminum_phys = new G4PVPlacement(0,
                                    G4ThreeVector(aluminum_x,aluminum_y,aluminum_z),
                                    aluminum_log,
                                    "aluminum_phys",
                                    experimentalHall_log,
                                    false,
                                    0);
    
    
    // ! Upper Collimator, internal
    
    // Slabs with trapezoidal shape, around the SDD
    // Remember the calling sequence for the G4Trap class:
    // 	   pDz     Half-length along the z-axis
    //     pTheta  Polar angle of the line joining the centres of the faces
    //             at -/+pDz
    //     pPhi    Azimuthal angle of the line joing the centre of the face at
    //             -pDz to the centre of the face at +pDz
    //     pDy1    Half-length along y of the face at -pDz
    //     pDx1    Half-length along x of the side at y=-pDy1 of the face at -pDz
    //     pDx2    Half-length along x of the side at y=+pDy1 of the face at -pDz
    //     pAlp1   Angle with respect to the y axis from the centre of the side
    //             at y=-pDy1 to the centre at y=+pDy1 of the face at -pDz
    //
    //     pDy2    Half-length along y of the face at +pDz
    //     pDx3    Half-length along x of the side at y=-pDy2 of the face at +pDz
    //     pDx4    Half-length along x of the side at y=+pDy2 of the face at +pDz
    //     pAlp2   Angle with respect to the y axis from the centre of the side
    //             at y=-pDy2 to the centre at y=+pDy2 of the face at +pDz
    
    G4double base_side_x = coll_side_x + 2*coll_side_thick;
    G4double base_side_y = coll_side_y + 2*coll_side_thick;

    //G4cout << "base_side_x, base_side_y (cm) " << base_side_x/cm << " " << base_side_y/cm << G4endl;

    // Rotation angles for the y-side and x-side collimators (i.e. inclination)
    double xrotTheta = atan((mask_side_x + coll_upper1_thick/2 - base_side_x)/(2*mask_focalDistance));
    double yrotTheta = atan((mask_side_y + coll_upper1_thick/2 - base_side_y)/(2*mask_focalDistance));

    // G4cout << "xrotTheta, yrotTheta (rad) " << xrotTheta << " " << yrotTheta << G4endl;
    // G4cout << "xrotTheta, yrotTheta (deg) " << 180/3.141592*(xrotTheta) << " " << 180/3.141592*(yrotTheta) << G4endl;

    // Solid (for yPlus, yMinus)
    G4double   pDzY   = mask_focalDistance/cos(yrotTheta)/2. ;              // half-height
    G4double   pDy1   = coll_upper1_thick/2.;                               // half-thickness of the collimator
    G4double   pDx1   = coll_side_x/2.;    				                    // detector side
    G4double   pDx2   = pDx1; 						                        // the other sides of the lower face are the same
    G4double   pDy2   = pDy1;						                        // the other sides of the lower face are the same
    G4double   pDx3   = (base_side_x + (mask_side_x - base_side_x))/2.;     // mask side
    G4double   pDx4   = pDx3; 						                        // the other side of the upper face is the same
    G4Trap* coll_upper1_trap_y = new G4Trap("coll_upper1_trap_y",
                                        pDzY, 0, 0, pDy1, pDx1, pDx2, 0, pDy2, pDx3, pDx4, 0);
    
    // Solid (for xPlus, xMinus)
    G4double pDzX    = mask_focalDistance/cos(xrotTheta)/2. ;               // half-height
    pDx1   = base_side_y/2.;    								            // detector side (here we simply switch X and Y)
    pDx2   = pDx1;
    pDx3   = (base_side_y  + (mask_side_y - base_side_y))/2.;      		    // mask side
    pDx4   = pDx3;
    G4Trap* coll_upper1_trap_x = new G4Trap("coll_upper1_trap_x",
                                        pDzX, 0, 0, pDy1, pDx1, pDx2, 0, pDy2, pDx3, pDx4, 0);
    
    // Logical
    coll_upper1Yp_log = new G4LogicalVolume(coll_upper1_trap_y,
                                        collUpper1Material, "coll_upper1Yp_log", 0, 0, 0);
    coll_upper1Ym_log = new G4LogicalVolume(coll_upper1_trap_y,
                                        collUpper1Material, "coll_upper1Ym_log", 0, 0, 0);
    coll_upper1Xp_log = new G4LogicalVolume(coll_upper1_trap_x,
                                        collUpper1Material, "coll_upper1Xp_log", 0, 0, 0);
    coll_upper1Xm_log = new G4LogicalVolume(coll_upper1_trap_x,
                                        collUpper1Material, "coll_upper1Xm_log", 0, 0, 0);
    
    // Physical
    // yPlus collimator
    G4RotationMatrix* yRotPlus = new G4RotationMatrix;
    yRotPlus->rotateX(-yrotTheta);
    G4ThreeVector yShiftPlus(0, base_side_y/2.+ coll_upper1_thick/2. + pDzY*sin(yrotTheta), pDzY*cos(yrotTheta));
    coll_upper1Yp_phys = new G4PVPlacement(
                                        G4Transform3D(*yRotPlus, yShiftPlus),
                                        coll_upper1Yp_log,
                                        "collUpper1Yp_phys",
                                        experimentalHall_log,
                                        false,
                                        0);
    
    // yMinus collimator
    G4RotationMatrix* yRotMinus = new G4RotationMatrix;
    yRotMinus->rotateX(yrotTheta);
    G4ThreeVector yShiftMinus(0, -(base_side_y/2.+ coll_upper1_thick/2. + pDzY*sin(yrotTheta)), pDzY*cos(yrotTheta));
    coll_upper1Ym_phys = new G4PVPlacement(
                                        G4Transform3D(*yRotMinus, yShiftMinus),
                                        coll_upper1Ym_log,
                                        "collUpper1Ym_phys",
                                        experimentalHall_log,
                                        false,
                                        0);
    
    // xPlus collimator
    G4RotationMatrix* xRotPlus = new G4RotationMatrix;
    xRotPlus->rotateZ(90*deg);
    xRotPlus->rotateY(xrotTheta);
    G4ThreeVector xShiftPlus(base_side_x/2.+ coll_upper1_thick/2. + pDzX*sin(xrotTheta), 0, pDzX*cos(xrotTheta));
    coll_upper1Xp_phys = new G4PVPlacement(
                                        G4Transform3D(*xRotPlus, xShiftPlus),
                                        coll_upper1Xp_log,
                                        "collUpper1Xp_phys",
                                        experimentalHall_log,
                                        false,
                                        0);

    // xMinus collimator
    G4RotationMatrix* xRotMinus = new G4RotationMatrix;
    xRotMinus->rotateZ(90*deg);
    xRotMinus->rotateY(-xrotTheta);
    G4ThreeVector xShiftMinus(-(base_side_x/2.+ coll_upper1_thick/2. + pDzX*sin(xrotTheta)), 0, pDzX*cos(xrotTheta));
    coll_upper1Xm_phys = new G4PVPlacement(
                                        G4Transform3D(*xRotMinus, xShiftMinus),
                                        coll_upper1Xm_log,
                                        "collUpper1Xm_phys",
                                        experimentalHall_log, 
                                        false, 
                                        0);
    
    
    // ! Upper Collimator, external
    
    // Slabs with trapezoidal shape, around the SDD
    // Remember the calling sequence for the G4Trap class:
    //        pDz     Half-length along the z-axis
    //     pTheta  Polar angle of the line joining the centres of the faces
    //             at -/+pDz
    //     pPhi    Azimuthal angle of the line joing the centre of the face at
    //             -pDz to the centre of the face at +pDz
    //     pDy1    Half-length along y of the face at -pDz
    //     pDx1    Half-length along x of the side at y=-pDy1 of the face at -pDz
    //     pDx2    Half-length along x of the side at y=+pDy1 of the face at -pDz
    //     pAlp1   Angle with respect to the y axis from the centre of the side
    //             at y=-pDy1 to the centre at y=+pDy1 of the face at -pDz
    //
    //     pDy2    Half-length along y of the face at +pDz
    //     pDx3    Half-length along x of the side at y=-pDy2 of the face at +pDz
    //     pDx4    Half-length along x of the side at y=+pDy2 of the face at +pDz
    //     pAlp2   Angle with respect to the y axis from the centre of the side
    //             at y=-pDy2 to the centre at y=+pDy2 of the face at +pDz
    
    
    // Solid (for yPlus, yMinus)
    G4cout <<" mask_focalDistance " << mask_focalDistance/cm << G4endl;
    G4cout <<" coll_upper2_height " << coll_upper2_height/cm << G4endl;
    
    G4double scaled_mask_side_x = (mask_side_x-base_side_x)*coll_upper2_height/mask_focalDistance+base_side_x;
    G4double scaled_mask_side_y = (mask_side_y-base_side_y)*coll_upper2_height/mask_focalDistance+base_side_y;

    G4double   p2DzY   = coll_upper2_height/cos(yrotTheta)/2. ;              // half-height
    G4double   p2Dy1   = coll_upper2_thick/2.;                               // half-thickness of the collimator
    G4double   p2Dx1   = coll_side_x/2.;                                        // detector side
    G4double   p2Dx2   = p2Dx1;                                                 // the other sides of the lower face are the same
    G4double   p2Dy2   = p2Dy1;                                                // the other sides of the lower face are the same
    G4double   p2Dx3   = (base_side_x + (scaled_mask_side_x - base_side_x))/2.;     // mask side
    G4double   p2Dx4   = p2Dx3;                                                 // the other side of the upper face is the same
    G4Trap* coll_upper2_trap_y = new G4Trap("coll_upper2_trap_y",
                                        p2DzY, 0, 0, p2Dy1, p2Dx1, p2Dx2, 0, p2Dy2, p2Dx3, p2Dx4, 0);
    
    // Solid (for xPlus, xMinus)
    G4double p2DzX    = coll_upper2_height/cos(xrotTheta)/2. ;               // half-height
    p2Dx1   = base_side_y/2.;                                                // detector side (here we simply switch X and Y)
    p2Dx2   = p2Dx1;
    p2Dx3   = (base_side_y  + (scaled_mask_side_y - base_side_y))/2.;                  // mask side
    p2Dx4   = p2Dx3;
    G4Trap* coll_upper2_trap_x = new G4Trap("coll_upper2_trap_x",
                                        p2DzX, 0, 0, p2Dy1, p2Dx1, p2Dx2, 0, p2Dy2, p2Dx3, p2Dx4, 0);
    
    // Logical
    coll_upper2Yp_log = new G4LogicalVolume(coll_upper2_trap_y,
                                            collUpper2Material, "coll_upper2Yp_log", 0, 0, 0);
    coll_upper2Ym_log = new G4LogicalVolume(coll_upper2_trap_y,
                                            collUpper2Material, "coll_upper2Ym_log", 0, 0, 0);
    coll_upper2Xp_log = new G4LogicalVolume(coll_upper2_trap_x,
                                            collUpper2Material, "coll_upper2Xp_log", 0, 0, 0);
    coll_upper2Xm_log = new G4LogicalVolume(coll_upper2_trap_x,
                                            collUpper2Material, "coll_upper2Xm_log", 0, 0, 0);
    
    // Physical
    // yPlus collimator
    G4ThreeVector yShiftPlus2(0, base_side_y/2.+ coll_upper1_thick/cos(yrotTheta) + coll_upper2_thick/2. + p2DzY*sin(yrotTheta), p2DzY*cos(yrotTheta));
    coll_upper2Yp_phys = new G4PVPlacement(
                                            G4Transform3D(*yRotPlus, yShiftPlus2),
                                            coll_upper2Yp_log,
                                            "collUpper2Yp_phys",
                                            experimentalHall_log,
                                            false,
                                            0);
    
    // yMinus collimator
    G4ThreeVector yShiftMinus2(0, -(base_side_y/2.+ coll_upper1_thick/cos(yrotTheta) + coll_upper2_thick/2. + p2DzY*sin(yrotTheta)), p2DzY*cos(yrotTheta));
    coll_upper2Ym_phys = new G4PVPlacement(
                                            G4Transform3D(*yRotMinus, yShiftMinus2),
                                            coll_upper2Ym_log,
                                            "collUpper2Ym_phys",
                                            experimentalHall_log,
                                            false,
                                            0);
    
    // xPlus collimator
    G4ThreeVector xShiftPlus2(base_side_x/2.+ coll_upper1_thick/cos(xrotTheta) + coll_upper2_thick/2. + p2DzX*sin(xrotTheta), 0, p2DzX*cos(xrotTheta));
    coll_upper2Xp_phys = new G4PVPlacement(
                                            G4Transform3D(*xRotPlus, xShiftPlus2),
                                            coll_upper2Xp_log,
                                            "collUpper2Xp_phys",
                                            experimentalHall_log,
                                            false,
                                            0);
    
    // xMinus collimator
    G4ThreeVector xShiftMinus2(-(base_side_x/2.+ coll_upper1_thick/cos(xrotTheta) + coll_upper2_thick/2. + p2DzX*sin(xrotTheta)), 0, p2DzX*cos(xrotTheta));
    coll_upper2Xm_phys = new G4PVPlacement(
                                            G4Transform3D(*xRotMinus, xShiftMinus2),
                                            coll_upper2Xm_log,
                                            "collUpper2Xm_phys",
                                            experimentalHall_log,
                                            false,
                                            0);


    // ! Polypropylene

    // Solid
    G4double poly_height = 10.*cm;
    G4double poly_side_x = coll_side_x + (2*coll_side_thick) + (2*poly_height) * tan(xrotTheta);
    G4double poly_side_y = coll_side_y + (2*coll_side_thick) + (2*poly_height) * tan(yrotTheta);

    G4Box* polyprop_box = new G4Box("polyprop_box", poly_side_x/2., poly_side_y/2., poly_thick/2.);

    // Logical
    polyprop_log = new G4LogicalVolume(polyprop_box, polypropMaterial, "polyprop_log", 0, 0, 0);

    // Physical
    G4double polyprop_x = 0;
    G4double polyprop_y = 0;
    G4double polyprop_z = poly_height;
    polyprop_phys = new G4PVPlacement(0,
                                    G4ThreeVector(polyprop_x,polyprop_y,polyprop_z),
                                    polyprop_log,
                                    "polyprop_phys",
                                    experimentalHall_log,
                                    false,
                                    0);
    
    
	// Set visualization attributes
	// RGB components
	G4Colour white   (1.0, 1.0, 1.0);
	G4Colour red     (1.0, 0.0, 0.0);
	G4Colour green   (0.0, 1.0, 0.0);
    G4Colour dgreen  (0.0, 0.8, 0.0);
	G4Colour blue    (0.0, 0.0, 1.0);
	G4Colour yellow  (1.0, 1.0, 0.0);
	G4Colour magenta (1.0, 0.0, 1.0);
	G4Colour cyan    (0.0, 1.0, 1.0);
    G4Colour brown   (0.65, 0.16, 0.16);
    G4Colour gold    (1.0, 0.84, 0.0);
    G4Colour silver  (0.75, 0.75, 0.75);

	experimentalHall_log -> SetVisAttributes(G4VisAttributes::GetInvisible());  

	G4VisAttributes* ScintVisAtt= new G4VisAttributes(yellow);
	scint_log -> SetVisAttributes(ScintVisAtt);
    
    G4VisAttributes* CollVisAtt= new G4VisAttributes(blue);
    coll_sideXp_log -> SetVisAttributes(CollVisAtt);
    coll_sideXm_log -> SetVisAttributes(CollVisAtt);
    coll_sideYp_log -> SetVisAttributes(CollVisAtt);
    coll_sideYm_log -> SetVisAttributes(CollVisAtt);
    bus1_log -> SetVisAttributes(CollVisAtt);
    //bus2_log -> SetVisAttributes(CollVisAtt);

    G4VisAttributes* SDDVisAtt= new G4VisAttributes(red);
    sddUpper_log -> SetVisAttributes(SDDVisAtt);
    sddLower_log -> SetVisAttributes(SDDVisAtt);
    
    G4VisAttributes* OptCouplerVisAtt= new G4VisAttributes(cyan);
    optCoupler_log -> SetVisAttributes(OptCouplerVisAtt);

//    G4VisAttributes* PCBContainerVisAtt= new G4VisAttributes(white);
    pcb_container_log -> SetVisAttributes(G4VisAttributes::GetInvisible());

    G4VisAttributes* PCBVisAtt= new G4VisAttributes(dgreen);
    pcb_log -> SetVisAttributes(PCBVisAtt);

    G4VisAttributes* PolyVisAtt= new G4VisAttributes(brown);
    polyprop_log -> SetVisAttributes(PolyVisAtt);

//    G4VisAttributes* MaskContainerVisAtt= new G4VisAttributes(white);
    mask_container_log -> SetVisAttributes(G4VisAttributes::GetInvisible());
    
    G4VisAttributes* MaskVisAtt= new G4VisAttributes(magenta);
    mask_log -> SetVisAttributes(MaskVisAtt);

    G4VisAttributes* KapVisAtt= new G4VisAttributes(gold);
    kapton_log -> SetVisAttributes(KapVisAtt);

    G4VisAttributes* AlumVisAtt= new G4VisAttributes(silver);
    aluminum_log -> SetVisAttributes(AlumVisAtt);

    G4VisAttributes* Coll1VisAtt= new G4VisAttributes(blue);
    coll_upper1Xp_log -> SetVisAttributes(Coll1VisAtt);
    coll_upper1Xm_log -> SetVisAttributes(Coll1VisAtt);
    coll_upper1Yp_log -> SetVisAttributes(Coll1VisAtt);
    coll_upper1Ym_log -> SetVisAttributes(Coll1VisAtt);
    
    G4VisAttributes* Coll2VisAtt= new G4VisAttributes(green);
    coll_upper2Xp_log -> SetVisAttributes(Coll2VisAtt);
    coll_upper2Xm_log -> SetVisAttributes(Coll2VisAtt);
    coll_upper2Yp_log -> SetVisAttributes(Coll2VisAtt);
    coll_upper2Ym_log -> SetVisAttributes(Coll2VisAtt);



	// Dump the geometry in a GDML file, solo se non esiste
	G4String fWriteFile = "geometry.gdml";

    // questo pezzo non va bene perche se modifico non me lo sovrascrivr //! MIGLIORARE
    // std::ifstream fileCheck(fWriteFile.c_str());
    // if (fileCheck.good()) {
    //     G4cout << "il file geometry.gdml esiste già" << G4endl;
    // } else {
    //     G4GDMLParser parser;
	//     //std::remove(fWriteFile.c_str());
	//     parser.Write(fWriteFile, experimentalHall_phys, false);
    // }

	// The function must return the physical volume of the world
	return experimentalHall_phys;
}



void DetectorConstruction::ConstructSDandField()
{
    // Sensitive volume
    // So far, the detector created above is not yet a real detector: it is just
    // a geometrical object (with some attributes) placed within the world volume.
    // To make the volume a detector, which can record e.g. hits, one must
    // define a sensitive volume associated with it.
    // For this purpose, a SensitiveDetector object is instantiated.
    // SD for the Scint
    
    auto sdman = G4SDManager::GetSDMpointer(); // Mandatory since Geant v. 4.10.03

    // Instantiation of the sensitive detector and readout geometry
    SensitiveDetector* scint_SD  = new SensitiveDetector("SCI",
                                        n_side,
                                        scint_x+scint_gap, scint_z,
                                        NbOfSlicesPerBar);
    
    
    sdman->AddNewDetector(scint_SD); // Mandatory since Geant v. 4.10.03
    SetSensitiveDetector(scint_log, scint_SD);
    
    // Instantiation of the sensitive detector and readout geometry
    SDDSensitiveDetector* sdd_SD  = new SDDSensitiveDetector("SDD",
                                                            scint_x+scint_gap,
                                                            n_side, n_side);
    sdman->AddNewDetector(sdd_SD); // Mandatory since Geant v. 4.10.03
    SetSensitiveDetector(sddUpper_log, sdd_SD);
}


void DetectorConstruction::SetScintMaterial(G4String materialChoice)
{
	G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);     
	if (pttoMaterial) scintMaterial = pttoMaterial;
}

void DetectorConstruction::SetCollSideMaterial(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) collSideMaterial = pttoMaterial;
}

void DetectorConstruction::SetBusMaterial(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) busMaterial = pttoMaterial;
}

void DetectorConstruction::SetSDDMaterial(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) sddMaterial = pttoMaterial;
}

void DetectorConstruction::SetMaskMaterial(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) maskMaterial = pttoMaterial;
}

void DetectorConstruction::SetCollUpper1Material(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) collUpper1Material = pttoMaterial;
}

void DetectorConstruction::SetCollUpper2Material(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) collUpper2Material = pttoMaterial;
}

void DetectorConstruction::SetOptCouplerMaterial(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) optCouplerMaterial = pttoMaterial;
}

void DetectorConstruction::SetPolyMaterial(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) polypropMaterial = pttoMaterial;
}

void DetectorConstruction::SetKapMaterial(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) kapMaterial = pttoMaterial;
}

void DetectorConstruction::SetAlumMaterial(G4String materialChoice)
{
    G4Material* pttoMaterial = G4Material::GetMaterial(materialChoice);
    if (pttoMaterial) alumMaterial = pttoMaterial;
}


void DetectorConstruction::PrintParameters()
{
	G4cout  << "\n-----------------------------------------------------------------------------------\n"
			<< "          Geometrical and physical parameters:          "
			<< "\n Scint Side X                     (cm) : " 	<< scint_x/cm
			<< "\n Scint Side Y                     (cm) : " 	<< scint_y/cm
			<< "\n Scint Side Z                     (cm) : " 	<< scint_z/cm
            << "\n mask_side_x                      (cm) : "    << mask_side_x/cm
            << "\n mask_side_y                      (cm) : "    << mask_side_y/cm
            << "\n n_mask_elem_side                      : "    << n_mask_elem_side
            << "\n Scint material                        : " 	<< scintMaterial -> GetName()
            << "\n Mask material                         : "    << maskMaterial -> GetName()
            << "\n Collimator 1 material                 : "    << collUpper1Material -> GetName()
            << "\n Collimator 2 material                 : "    << collUpper2Material -> GetName();
	G4cout  << "\n-----------------------------------------------------------------------------------\n";
}



void DetectorConstruction::UpdateGeometry()
{  
	G4RunManager::GetRunManager()->DefineWorldVolume(Construct());
	G4RunManager::GetRunManager()->GeometryHasBeenModified();
}


