//
// ********************************************************************
// * 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.          *
// ********************************************************************
//
//
// GEANT4 Class file
//
// File name: G4PhysicsConstructorRegistry.cc
//
// Author  W. Pokorski  21.09.2012
// -------------------------------------------------------------------

#include "G4ios.hh"
#include <iomanip>

// force REFERENCE macros _not_ to be expanded here
// but wherever the class is used (w/ that .hh use)
#define   G4PhysicsConstructorRegistry_cc 1

#include "G4PhysicsConstructorRegistry.hh"

#include "G4VPhysicsConstructor.hh"
#include "G4PhysicsConstructorFactory.hh"

G4ThreadLocal G4PhysicsConstructorRegistry* G4PhysicsConstructorRegistry::theInstance = nullptr;

G4PhysicsConstructorRegistry* G4PhysicsConstructorRegistry::Instance()
{
  if (nullptr == theInstance) {
    static G4ThreadLocalSingleton<G4PhysicsConstructorRegistry> inst;
    theInstance = inst.Instance();
  }
  return theInstance;
}

G4PhysicsConstructorRegistry::G4PhysicsConstructorRegistry()
{}

G4PhysicsConstructorRegistry::~G4PhysicsConstructorRegistry()
{
  Clean();
}

void G4PhysicsConstructorRegistry::Clean()
{
  for (auto const & ptr : physConstr) { delete ptr; }
  physConstr.clear();
}

void G4PhysicsConstructorRegistry::Register(G4VPhysicsConstructor* p)
{
  if (nullptr == p) { return; }
  for (auto const & ptr : physConstr) { if (p == ptr) { return; } }
  physConstr.push_back(p);
}

void G4PhysicsConstructorRegistry::DeRegister(G4VPhysicsConstructor* p)
{
  if (nullptr == p || physConstr.empty()) { return; }
  std::size_t n = physConstr.size();
  for (std::size_t i=0; i<n; ++i) {
    if ( physConstr[i] == p ) {
      physConstr[i] = nullptr;
      return;
    }
  }
}

void G4PhysicsConstructorRegistry::AddFactory(G4String name, G4VBasePhysConstrFactory* factory)
{
  factories[name] = factory;
}

G4VPhysicsConstructor* G4PhysicsConstructorRegistry::GetPhysicsConstructor(const G4String& name)
{
  // check if factory exists...
  //
  if (factories.find(name)!=factories.end())
    {
        // we could store the list of called factories in some vector and
        // before returning we can could first check if this physics constructor was already instantiated
        // if yes, we can throw an exception saying that this physics can been already registered

      return factories[name]->Instantiate();
    }
  else
    {
      G4ExceptionDescription ED;
      ED << "The factory for the physics constructor ["<< name << "] does not exist!" << G4endl;
      G4Exception("G4PhysicsConstructorRegistry::GetPhysicsConstructor", "PhysicsList001", FatalException, ED);
      return nullptr;
    }
}

G4bool G4PhysicsConstructorRegistry::IsKnownPhysicsConstructor(const G4String& name)
{
  return ( factories.find(name) != factories.end() );
}


std::vector<G4String> G4PhysicsConstructorRegistry::AvailablePhysicsConstructors() const
{
  std::vector<G4String> avail;
  std::map<G4String,G4VBasePhysConstrFactory*>::const_iterator itr;
  for ( itr = factories.begin(); itr != factories.end(); ++itr ) {
    avail.push_back(itr->first);
  }

  return avail;
}

void G4PhysicsConstructorRegistry::PrintAvailablePhysicsConstructors() const
{
  std::vector<G4String> avail = AvailablePhysicsConstructors();
  G4cout << "G4VPhysicsConstructors in G4PhysicsConstructorRegistry are:"
         << G4endl;
  if ( avail.empty() ) G4cout << "... no registered processes" << G4endl;
  else {
    std::size_t n = avail.size();
    for (std::size_t i=0; i<n; ++i ) {
      G4cout << " [" << std::setw(3) << i << "] "
             << " \"" << avail[i] << "\"" << G4endl;
    }
  }
}

// External reference to phy ctor factories for running with 'static'
// libraries to pull the references of the declared factories into the
// same compilation unit as the registry itself.
// No harm having them in the non-static case.
//
// Ideally we'd do the G4_REFERENCE_PHYSCONSTR_FACTORY() macros
// here, but this introduces a circular dependence between the
// ctor_phys_factory library and the other ctor_phys_* libraries
// when creating granular libraries.
// Instead we'll make the references in the location(s) where the
// G4PhysicsConstructorRegistry is _used_ :
//
//#include "G4RegisterPhysicsConstructors.icc"
