# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
from __future__ import print_function

from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
from AthenaConfiguration.ComponentFactory import CompFactory

from AthenaCommon import Logging

#the physics region tools
from G4FaserTools.G4PhysicsRegionConfigNew import NeutrinoPhysicsRegionToolCfg, TrackerPhysicsRegionToolCfg, ScintillatorPhysicsRegionToolCfg, EcalPhysicsRegionToolCfg, CavernPhysicsRegionToolCfg

#the field config tools
from G4FaserTools.G4FieldConfigNew import FASERFieldManagerToolCfg, EmulsionFieldManagerToolCfg, VetoFieldManagerToolCfg, VetoNuFieldManagerToolCfg, TriggerFieldManagerToolCfg, PreshowerFieldManagerToolCfg, TrackerFieldManagerToolCfg, DipoleFieldManagerToolCfg, EcalFieldManagerToolCfg, TrenchFieldManagerToolCfg

from G4FaserTools.G4FaserToolsConfigNew import SensitiveDetectorMasterToolCfg

GeoDetectorTool=CompFactory.GeoDetectorTool
from EmulsionGeoModel.EmulsionGeoModelConfig import EmulsionGeometryCfg
from VetoGeoModel.VetoGeoModelConfig import VetoGeometryCfg
from VetoNuGeoModel.VetoNuGeoModelConfig import VetoNuGeometryCfg
from TriggerGeoModel.TriggerGeoModelConfig import TriggerGeometryCfg
from PreshowerGeoModel.PreshowerGeoModelConfig import PreshowerGeometryCfg
from FaserSCT_GeoModel.FaserSCT_GeoModelConfig import FaserSCT_GeometryCfg
from DipoleGeoModel.DipoleGeoModelConfig import DipoleGeometryCfg
from EcalGeoModel.EcalGeoModelConfig import EcalGeometryCfg
from FaserGeoModel.TrenchGMConfig import TrenchGeometryCfg

BoxEnvelope, MaterialDescriptionTool, VoxelDensityTool, G4AtlasDetectorConstructionTool = CompFactory.getComps("BoxEnvelope", "MaterialDescriptionTool", "VoxelDensityTool", "G4AtlasDetectorConstructionTool",)

from AthenaCommon.SystemOfUnits import mm

#ToDo - finish migrating this (dnoel)
#Todo - just return component accumulator
#to still migrate: getCavernWorld, getCavernInfraGeoDetectorTool
#from ForwardRegionProperties.ForwardRegionPropertiesToolConfig import ForwardRegionPropertiesCfg

#put it here to avoid circular import?
G4GeometryNotifierSvc=CompFactory.G4GeometryNotifierSvc
def G4GeometryNotifierSvcCfg(ConfigFlags, name="G4GeometryNotifierSvc", **kwargs):
    kwargs.setdefault("ActivateLVNotifier", True)
    kwargs.setdefault("ActivatePVNotifier", False)
    return G4GeometryNotifierSvc(name, **kwargs)

def EmulsionGeoDetectorToolCfg(ConfigFlags, name='Emulsion', **kwargs):
    #set up geometry
    result=EmulsionGeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "Emulsion")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def VetoGeoDetectorToolCfg(ConfigFlags, name='Veto', **kwargs):
    #set up geometry
    result=VetoGeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "Veto")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def VetoNuGeoDetectorToolCfg(ConfigFlags, name='VetoNu', **kwargs):
    #set up geometry
    result=VetoNuGeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "VetoNu")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def TriggerGeoDetectorToolCfg(ConfigFlags, name='Trigger', **kwargs):
    #set up geometry
    result=TriggerGeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "Trigger")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def PreshowerGeoDetectorToolCfg(ConfigFlags, name='Preshower', **kwargs):
    #set up geometry
    result=PreshowerGeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "Preshower")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def SCTGeoDetectorToolCfg(ConfigFlags, name='SCT', **kwargs):
    #set up geometry
    result=FaserSCT_GeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "SCT")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def DipoleGeoDetectorToolCfg(ConfigFlags, name='Dipole', **kwargs):
    #set up geometry
    result=DipoleGeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "Dipole")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def EcalGeoDetectorToolCfg(ConfigFlags, name='Ecal', **kwargs):
    #set up geometry
    result=EcalGeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "Ecal")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def TrenchGeoDetectorToolCfg(ConfigFlags, name='Trench', **kwargs):
    #set up geometry
    result=TrenchGeometryCfg(ConfigFlags)
    kwargs.setdefault("DetectorName", "Trench")
    #add the GeometryNotifierSvc
    result.addService(G4GeometryNotifierSvcCfg(ConfigFlags))
    kwargs.setdefault("GeometryNotifierSvc", result.getService("G4GeometryNotifierSvc"))
    result.setPrivateTools(GeoDetectorTool(name, **kwargs))
    return result

def generateSubDetectorList(ConfigFlags):
    result = ComponentAccumulator()
    SubDetectorList=[]

    if ConfigFlags.Detector.GeometryEmulsion:
        toolEmulsion = result.popToolsAndMerge(EmulsionGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolEmulsion ]

    if ConfigFlags.Detector.GeometryVeto:
        toolVeto = result.popToolsAndMerge(VetoGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolVeto ]

    if ConfigFlags.Detector.GeometryVetoNu:
        toolVetoNu = result.popToolsAndMerge(VetoNuGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolVetoNu ]

    if ConfigFlags.Detector.GeometryTrigger:
        toolTrigger = result.popToolsAndMerge(TriggerGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolTrigger ]

    if ConfigFlags.Detector.GeometryPreshower:
        toolPreshower = result.popToolsAndMerge(PreshowerGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolPreshower ]

    if ConfigFlags.Detector.GeometryFaserSCT:
        toolSCT = result.popToolsAndMerge(SCTGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolSCT ]

    if ConfigFlags.Detector.GeometryDipole:
        toolDipole = result.popToolsAndMerge(DipoleGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolDipole ]

    if ConfigFlags.Detector.GeometryEcal:
        toolEcal = result.popToolsAndMerge(EcalGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolEcal ]

    if ConfigFlags.Detector.GeometryTrench:
        toolTrench = result.popToolsAndMerge(TrenchGeoDetectorToolCfg(ConfigFlags))
        SubDetectorList += [ toolTrench ]

    result.setPrivateTools(SubDetectorList)
    return result

def FASEREnvelopeCfg(ConfigFlags, name="Faser", **kwargs):
    result = ComponentAccumulator()

    kwargs.setdefault("OffsetX", 0.0 * mm)
    kwargs.setdefault("OffsetY", 0.0 * mm)
    kwargs.setdefault("OffsetZ", 0.0 * mm)
    kwargs.setdefault("dX", 600.0 * mm) 
    kwargs.setdefault("dY", 600.0 * mm) 
    kwargs.setdefault("dZ", 4000.0 * mm) 

    kwargs.setdefault("DetectorName", "Faser")
    SubDetectorList = result.popToolsAndMerge(generateSubDetectorList(ConfigFlags))
    kwargs.setdefault("SubDetectors", SubDetectorList)
    result.setPrivateTools(BoxEnvelope(name, **kwargs))
    return result

def MaterialDescriptionToolCfg(ConfigFlags, name="MaterialDescriptionTool", **kwargs):
    ## kwargs.setdefault("SomeProperty", aValue)
    result = ComponentAccumulator()
    result.setPrivateTools(MaterialDescriptionTool(name, **kwargs))
    return result


def VoxelDensityToolCfg(ConfigFlags, name="VoxelDensityTool", **kwargs):
    ## kwargs.setdefault("SomeProperty", aValue)
    voxelDensitySettings = {}
    # if ConfigFlags.Detector.GeometryITkPixel:
    #     voxelDensitySettings["ITkPixelDetector"] = 0.05
    # if ConfigFlags.Detector.GeometryITkStrip:
    #     voxelDensitySettings["ITkStrip::Barrel"] = 0.05
    #     voxelDensitySettings["ITkStrip::ITkStrip_Forward"] = 0.05
    #     ##The below is only needed temporarily, while we wait for
    #     ##improved naming to be propagated to all necessary geo tags
    #     voxelDensitySettings["ITkStrip::SCT_Forward"] = 0.05
    kwargs.setdefault("VolumeVoxellDensityLevel",voxelDensitySettings)
    result = ComponentAccumulator()
    result.setPrivateTools(VoxelDensityTool(name, **kwargs))
    return result

def getFASER_RegionCreatorList(ConfigFlags):
    regionCreatorList = []

    if ConfigFlags.Detector.GeometryNeutrino:
        regionCreatorList += [NeutrinoPhysicsRegionToolCfg(ConfigFlags)]

    if ConfigFlags.Detector.GeometryTracker:
        regionCreatorList += [TrackerPhysicsRegionToolCfg(ConfigFlags)]

    if ConfigFlags.Detector.GeometryScintillator:
        regionCreatorList += [ScintillatorPhysicsRegionToolCfg(ConfigFlags)]

    if ConfigFlags.Detector.GeometryFaserCalo:
        regionCreatorList += [EcalPhysicsRegionToolCfg(ConfigFlags)]
    
    if ConfigFlags.Detector.GeometryCavern:
        regionCreatorList += [CavernPhysicsRegionToolCfg(ConfigFlags)]

    return regionCreatorList

def FASER_FieldMgrListCfg(ConfigFlags):
    result = ComponentAccumulator()
    fieldMgrList = []

    acc   = FASERFieldManagerToolCfg(ConfigFlags)
    tool  = result.popToolsAndMerge(acc)
    fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryEmulsion:
        acc = EmulsionFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryVeto:
        acc = VetoFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryVetoNu:
        acc = VetoNuFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryTrigger:
        acc = TriggerFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryPreshower:
        acc = PreshowerFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryTracker:
        acc = TrackerFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryDipole:
        acc = DipoleFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryEcal:
        acc = EcalFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]

    if ConfigFlags.Detector.GeometryTrench:
        acc = TrenchFieldManagerToolCfg(ConfigFlags)
        tool  = result.popToolsAndMerge(acc)
        fieldMgrList += [tool]


    result.setPrivateTools(fieldMgrList)
    return result

def getGeometryConfigurationTools(ConfigFlags):
    geoConfigToolList = []
    # The methods for these tools should be defined in the
    # package containing each tool, so G4FaserTools in this case
    result =ComponentAccumulator()
    geoConfigToolList += [result.popToolsAndMerge(MaterialDescriptionToolCfg(ConfigFlags))]
    geoConfigToolList += [result.popToolsAndMerge(VoxelDensityToolCfg(ConfigFlags))]
    return result, geoConfigToolList


def G4AtlasDetectorConstructionToolCfg(ConfigFlags, name="G4FaserDetectorConstructionTool", **kwargs):
    result = ComponentAccumulator()

    ## For now just have the same geometry configurations tools loaded for ATLAS and TestBeam
    geoConfAcc, listOfGeoConfTools = getGeometryConfigurationTools(ConfigFlags)
    result.merge(geoConfAcc)
    kwargs.setdefault("GeometryConfigurationTools", listOfGeoConfTools)

    # Getting this tool by name works, but not if you use getSensitiveDetectorMasterTool()
    tool = result.popToolsAndMerge(SensitiveDetectorMasterToolCfg(ConfigFlags))
    result.addPublicTool(tool)
    kwargs.setdefault("SenDetMasterTool", result.getPublicTool(tool.name))

    toolGeo = result.popToolsAndMerge(FASEREnvelopeCfg(ConfigFlags))
    kwargs.setdefault("World", toolGeo)
    kwargs.setdefault("RegionCreators", getFASER_RegionCreatorList(ConfigFlags))
    #if hasattr(simFlags, 'MagneticField') and simFlags.MagneticField.statusOn:
    if True:
        acc = FASER_FieldMgrListCfg(ConfigFlags)
        fieldMgrList = result.popToolsAndMerge(acc)
        kwargs.setdefault("FieldManagers", fieldMgrList)
    
    result.setPrivateTools(G4AtlasDetectorConstructionTool(name, **kwargs))
    return result