/// @file AllPixDetectorConstruction.hh
// @brief Header file for the Allpix detector construction class.

#ifndef AllPixDetectorConstruction_h
#define AllPixDetectorConstruction_h 1

// GEANT4 include statements.
#include "globals.hh"
#include "G4VUserDetectorConstruction.hh"
#include "G4SDManager.hh"
#include "G4VisAttributes.hh"
#include "G4Colour.hh"
#include "G4PVDivision.hh"
#include "G4VSolid.hh"
#include "G4SubtractionSolid.hh"
#include "G4ThreeVector.hh"

// Allpix include statements.
#include "AllPixTrackerSD.hh"
#include "AllPixDetectorMessenger.hh"
#include "ReadGeoDescription.hh"

#include "Utils.h"

// ROOT include statements.
#include "TROOT.h"

// Standard include statements.
#include <vector>
#include <stdio.h>
#include <utility>
#include <string>
#include <map>

using namespace std;
using namespace Utils;

class G4Box;
class G4LogicalVolume;
class G4VPhysicalVolume;
class AllPixGeoDsc;
class G4UniformMagField;
class G4QuadrupoleMagField;
class MorourgoMagField;

/// @brief The detector construction class for Allpix.
///
/// @author J. Idarraga (principle author - idarraga@cern.ch)
/// @author T. Whyntie (editor, CERN\@school - t.whyntie@qmul.ac.uk)
/// @date Ed. February 2014
class AllPixDetectorConstruction : public G4VUserDetectorConstruction {

 public:

  /// @brief The constructor.
  ///
  /// @param [in] xmlFilename XML file containing the detector geometry.
  AllPixDetectorConstruction(G4String xmlFileName);

  /// @brief The destructor.
  ~AllPixDetectorConstruction();

  void DefineSensitiveDetector();
  
  void VolumesG4Properties();

  //void BuildMediPix(vector<G4ThreeVector>, vector<G4RotationMatrix *>);
  void BuildPixelDevices(map<int, AllPixGeoDsc *>);

  /// @brief Check the setup.
  void CheckAllPixSetup();

  // Test structure accessor methods.

  /// @brief Set the build test structure flag.
  ///
  /// @param [in] flg Are we building a test structure?
  inline void SetBuildTestStructure(bool flg) { m_buildTestStructureFlag = flg; };

  /// @brief Set the name of the test structure to be built.
  ///
  /// @param [in] name The name of the test structure.
  void SetTestStructureName(G4String name);

//  /// @brief Get the name of the selected test structure.
//  ///
//  /// @return The name of the selected test structure.
//  G4String GetTestStructureName();

  /// @brief Set the position of the test structure.
  ///
  /// @param [in] pos The position of the test structure.
  ///
  /// A vector of position vectors is used, as there may be more than one
  /// Test structure in use.
  void SetTestStructurePosition(G4ThreeVector pos);

  /// @brief Set the rotation of the test structure.
  ///
  /// @param [in] rot The rotation of the test structure.
  void SetTestStructureRotation(G4ThreeVector rot);

  void SetTestStructureDetectorLink(G4int);

  /// @brief Set the test structure shielding thickness.
  ///
  /// @param [in] thk The thickness of the shielding [mm].
  void SetTestStructureShieldingThickness(G4double thk);

  void SetBuildAppliances(bool flg) { m_buildAppliancesFlag = flg; };

  void SetAppliancePosition(G4ThreeVector);

//	void SetWrapperEnhancement(G4ThreeVector);

  void SetMaxStepLengthSensor(G4double);

  // The magnetic field.
  void SetPeakMagField(G4double fieldValue);

	// world volume from macro
	void SetWorldMaterial(G4String);

  // Builder methods.

  void BuildTestStructure(G4int);

  void BuildAppliances(G4int);

  /// @brief Builds the source test bench structure.
  ///
  /// @param [in] num The test structure number.
  void BuildSourceTestBench(G4int num);

  /// @brief Builds the LUCID experiment test structure.
  ///
  /// @param [in] num The test structure number.
  void BuildLUCID(G4int num);

  /// @brief Builds the test beam structure.
  ///
  /// @param [in] num The test structure number.
  void BuildTestBeam(G4int num);

  // Geospatial information accessor methods
  //-----------------------------------------

  /// @brief Get the latitude of the lab frame.
  ///
  /// @return The latitude [deg.].
  inline Double_t GetLabLatitude() { return m_lab_latitude; }

  /// @brief Set the latitude of the lab frame.
  ///
  /// @param [in] lat The latitude [deg.].
  inline void SetLabLatitude(Double_t lat) { m_lab_latitude = lat; }

  /// @brief Get the longitude of the lab frame.
  ///
  /// @return The longitude [deg.].
  inline Double_t GetLabLongitude() { return m_lab_longitude; }

  /// @brief Set the longitude of the lab frame.
  ///
  /// @param [in] lon The longitude [deg.].
  inline void SetLabLongitude(Double_t lon) { m_lab_longitude = lon; }

  /// @brief Get the altitude of the lab frame.
  ///
  /// @return The altitude [km].
  inline Double_t GetLabAltitude() { return m_lab_altitude; }

  /// @brief Set the altitude of the lab frame.
  ///
  /// @param [in] alt The altitude [km].
  inline void SetLabAltitude(Double_t alt) { m_lab_altitude = alt; }

  /// @brief Set the lab frame roll angle.
  ///
  /// @param [in] roll The roll angle of the laboratory frame [deg.].
  inline void SetLabRollAngle(Double_t roll) { m_lab_roll = roll; }

  /// @brief Get the lab frame roll angle.
  ///
  /// @return The roll angle of the laboratory frame [deg.].
  inline Double_t GetLabRollAngle() { return m_lab_roll; }

  /// @brief Set the pitch angle of the laboratory frame.
  ///
  /// @param [in] pitch The pitch angle of the laboratory frame [deg.].
  inline void SetLabPitchAngle(Double_t pitch) { m_lab_pitch = pitch; }

  /// @brief Get the lab frame pitch angle.
  ///
  /// @return The pitch angle of the laboratory frame [deg.].
  inline Double_t GetLabPitchAngle() { return m_lab_pitch; }

  /// @brief Set the yaw angle of the laboratory frame.
  ///
  /// @param [in] yaw The yaw angle of the laboratory frame [deg.].
  inline void SetLabYawAngle(Double_t yaw) { m_lab_yaw = yaw; }

  /// @brief Get the yaw angle of the laboratory frame.
  ///
  /// @return The yaw angle of the laboratory frame [deg.].
  inline Double_t GetLabYawAngle() { return m_lab_yaw; }

  inline void SetSourceID(G4String id) { m_sourceId = id; }
  inline G4String GetSourceID() { return m_sourceId; }


  // Detector position accessor methods.

  void SetDetectorPosition(G4ThreeVector);

  inline Double_t GetSensorPosX(G4int idx) { return m_absolutePosSiWafer[idx].x(); }
  inline Double_t GetSensorPosY(G4int idx) { return m_absolutePosSiWafer[idx].y(); }
  inline Double_t GetSensorPosZ(G4int idx) { return m_absolutePosSiWafer[idx].z(); }

  // Detector rotation accessor methods.

  /// @brief Set the detector rotation.
  ///
  /// @param [in] The detector rotation angles (O_x, O_y, O_z).
  void SetDetectorRotation(G4ThreeVector);

  Double_t GetDetectorRotationX(G4int idx);

  Double_t GetDetectorRotationY(G4int idx);

  Double_t GetDetectorRotationZ(G4int idx);

  void SetDetectorID(G4int);

  /// @brief Set the thickness of the sensor foil.
  ///
  /// @param [in] thk The thickness of the aluminium foil [mm].
  void SetFoilThickness(G4double thk);

  // Detector information accessor methods
  //---------------------------------------

  /// @brief Set the chip ID.
  ///
  /// @param [in] chipid The chip ID.
  inline void SetChipID(int id, G4String chipid) { m_chipIds[id] = chipid; }

  /// @brief Get the chip ID.
  ///
  /// @return The chip ID.
  inline G4String GetChipID(int id) { return m_chipIds[id]; }

  // Detector settings accessor methods
  //------------------------------------
  void SetLowTHL(G4double);
  void UpdateGeometry();

  // Configuration accessor methods
  //--------------------------------

  void SetOutputFilePrefix(G4String);

  G4String GetOutputFilePrefix() { return m_outputFilePrefix; };

  /// @brief Set the number of events to store per frame.
  ///
  /// @param [in] epf The events per frame to store.
  inline void SetEventsPerFrame(G4int epf) { m_eventsPerFrame = epf; }

  /// @brief Get the events per frame that the simulation will store.
  ///
  /// @return The number of (source particle) events that will be stored.
  inline G4int GetEventsPerFrame() { return m_eventsPerFrame; }

  G4VSolid * GetVSolidDetector (G4int detId) {
	  // Check first if the detId is good
	  // otherwise return null pointer
	  return m_Box_log[detId]->GetSolid();
  };

	// Specific EUTelescope
#ifdef _EUTELESCOPE
	void SetScintPos(G4ThreeVector);
#endif

 public:

  G4VPhysicalVolume* Construct();

 private:

  // flags
  bool m_clearanceToBuildGeometry;

  /// @brief The XML file containing the detector geometry information.
  G4String m_xmlFileName;

  // The laboratory frame information
  //----------------------------------

  G4double m_worldRad;

  /// @brief The latitude of the lab frame.
  Double_t m_lab_latitude;

  /// @brief The longitude of the lab frame.
  Double_t m_lab_longitude;

  /// @brief The altitude of the lab frame.
  Double_t m_lab_altitude;

  /// @brief The roll angle of the lab frame.
  Double_t m_lab_roll;

  /// @brief The pitch angle of the lab frame.
  Double_t m_lab_pitch;

  /// @brief The yaw angle of the lab frame.
  Double_t m_lab_yaw;

  /// @brief The source ID.
  G4String m_sourceId;

  // pos rot detector
  vector<G4int> m_detId;

  vector<G4int>::iterator m_detIdItr;

  map<int, G4ThreeVector> m_posVector; // position of medipix(es), key is detector Id

  // The rotation of the detectors.

  map<int, G4RotationMatrix *> m_rotVector; // rotation

  map<int, Double_t> m_rot_x;
  map<int, Double_t> m_rot_y;
  map<int, Double_t> m_rot_z;

  /// @brief The number of detector position vectors stored.
  G4int m_nPositions;

  /// @brief The number of detector rotation vectors stored.
  G4int m_nRotations;

  /// @brief The number of detector IDs.
  G4int m_nIds;

  /// @brief The number of detector foil thicknesses.
  G4int m_nDetFoilThicknesses;

  /// @brief The detector foil thicknesses.
  map<int, G4double> m_detFoilThicknesses;

  vector<G4double> m_lowThlVector; // lowTHL

  /// @brief The absolute position (center) of the silicon wafers.
  ///  
  /// (For user information.)
  //vector<G4ThreeVector> m_absolutePosSiWafer;
  map<int, G4ThreeVector> m_absolutePosSiWafer;

  // Detector information
  //----------------------
  
  /// @brief The chip IDs.
  map<int, G4String> m_chipIds;

  /// @brief The names of the test structure.
  map<int, G4String> m_testStructureNames;

  /// @brief The positions of the test structure.
  map<int, G4ThreeVector> m_posVectorTestStructure;

  /// @brief The rotations of the test structures.
  map<int, G4RotationMatrix *> m_rotVectorTestStructure; 

  /// @brief The test structure shielding thickness [mm].
  map<int, G4double> m_testStructureShieldingThickness;

  map<G4int, G4int> m_detectorLinkTestStructure;

  /// @brief The number of test structure names.
  G4int m_nTestStructureNames;

  /// @brief The number of test structure positions.
  G4int m_nTestPositions;

  /// @brief The number of test structure rotations.
  G4int m_nTestRotation;

  /// @brief The number of test structure shielding thicknesses.
  G4int m_nTestStructureShieldingThicknesses;


	// pos appliance
	//vector<G4ThreeVector> m_posVectorAppliances;
	map<int, G4ThreeVector> m_posVectorAppliances;
	G4int m_nAppliancesPositions;

//	//vector<G4ThreeVector> m_vectorWrapperEnhancement;
//	map<int, G4ThreeVector> m_vectorWrapperEnhancement;
//	G4int m_nWrapperEnhancement;

  // Geometry in G4
  AllPixGeoDsc * gD;   // geo bits for 1 detector

  /// @brief Pointer to the detector geometry description.
  ReadGeoDescription * m_geoDsc;

  /// @brief The laboratory (experimental hall) logical volume.
  G4LogicalVolume * expHall_log;

  /// @brief The laboratory (experimental hall) physical volume.
  G4VPhysicalVolume * expHall_phys;


  map<int, G4LogicalVolume *>    m_wrapper_log;

  map<int, G4VPhysicalVolume *>  m_wrapper_phys;

  /// @brief The PCB logical volumes.
  map<int, G4LogicalVolume *>    m_PCB_log;

  /// @brief The PCB physical volumes.
  map<int, G4VPhysicalVolume*>   m_PCB_phys;

  /// @brief The logical volumes for the copper bases.
  map<int, G4LogicalVolume *> m_CopperBase_log;

  /// @brief The physical volumes for the copper bases.
  map<int, G4VPhysicalVolume*> m_CopperBase_phys;

  /// @brief The logical volume for the readout chip.
  map<int, G4LogicalVolume *> m_ReadoutChip_log;

  /// @brief The physical volume for the readout chip.
  map<int, G4VPhysicalVolume*> m_ReadoutChip_phys;

  map<int, G4LogicalVolume *>    m_Box_log;

  map<int, G4VPhysicalVolume*>   m_Box_phys;

  map<int, G4LogicalVolume *>    m_GuardRings_log;

  map<int, G4VPhysicalVolume*>   m_GuardRings_phys;

  map<int, G4LogicalVolume *>    m_Slice_log;
  
  map<int, G4LogicalVolume *>    m_Pixel_log;

  /// @brief The logical volumes for the aluminium foil.
  map<int, G4LogicalVolume *> m_AlFoil_log;

  /// @brief The physical volume for the aluminium foil.
  map<int, G4VPhysicalVolume *> m_AlFoil_phys;

/* JI commented out
  //////////////////////////////////
  // wrapper
  G4LogicalVolume ** m_wrapper_log;
  G4VPhysicalVolume ** m_wrapper_phys;
  // PCB
  G4LogicalVolume ** m_PCB_log;
  G4VPhysicalVolume ** m_PCB_phys;
  // Box
  G4LogicalVolume ** m_Box_log;
  G4VPhysicalVolume ** m_Box_phys;
  G4LogicalVolume ** m_GuardRings_log;
  G4VPhysicalVolume ** m_GuardRings_phys;
  G4int m_detectorId;

  // slice and pixel
  G4LogicalVolume ** m_Slice_log;
  G4LogicalVolume ** m_Pixel_log;
  //////////////////////////////////
*/

  // The test structure(s)
  //-----------------------

  bool m_buildTestStructureFlag;

  G4LogicalVolume * m_TestStructure_log;

  G4VPhysicalVolume * m_TestStructure_phys;

  // Appliance(s)
  //--------------

  bool  m_buildAppliancesFlag;

  // The detector messenger
  //------------------------

  /// @brief Pointer to the detector messenger.
  AllPixDetectorMessenger* m_detectorMessenger;

  // Materials
  //-----------

  G4Material * m_Air;

  G4Material * m_Vacuum;

  G4Material * m_fillingWorldMaterial;

  bool m_userDefinedWorldMaterial;

  // Magnetic field
  //----------------

  /// @brief Pointer to the magnetic field.
  G4UniformMagField * m_magField;

  //MorourgoMagField * m_magField;

  // User limits
  //-------------

  G4UserLimits * m_ulim;

  G4double m_maxStepLengthSensor;

  // Configuration members
  //-----------------------

  /// @brief Output file prefix, including the path.
  G4String m_outputFilePrefix;

  /// @brief The number of events to store per frame.
  G4int m_eventsPerFrame;

	// Eutelescope specifig
#ifdef _EUTELESCOPE
	vector<G4ThreeVector> m_scintPos;
	vector<G4ThreeVector> m_scintRot;
#endif

};//end of AllPixDetectorConstruction class definition.

#define _BUILD_MEDIPIX_MSG()						\
		G4cout << "  In order to place a detector you must include in your macro at least" << G4endl; \
		G4cout << "  the following lines: (setId must go first !)" << G4endl;			\
		G4cout << "    /allpix/det/setId 5" << G4endl;	\
		G4cout << "    /allpix/det/setPosition 0.0 0.0 0.0 mm" << G4endl;	\
		G4cout << "    /allpix/det/setRotation 0.0 0.0 0.0 deg" << G4endl;	\
		G4cout << "    /allpix/det/setLowTHL 13. keV" << G4endl;		\
		G4cout << "  see an example in \"allpix_vis.in\"" << G4endl;

#define _WRONG_CONFIG_ABORT_MSG()						\
		G4cout << "[ERROR] wrong configuration.  Aborting job." << G4endl;

#endif /*AllPixDetectorConstruction_h*/