//==========================================================================
//  AIDA Detector description implementation 
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// All rights reserved.
//
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
//
// Author     : M.Frank
//
//==========================================================================
#ifndef EXAMPLES_CONDITIONS_SRC_CONDITIONEXAMPLEOBJECTS_H
#define EXAMPLES_CONDITIONS_SRC_CONDITIONEXAMPLEOBJECTS_H

// Framework include files
#include "DD4hep/Detector.h"
#include "DD4hep/Printout.h"
#include "DD4hep/Conditions.h"
#include "DD4hep/ConditionsMap.h"
#include "DD4hep/ConditionDerived.h"
#include "DD4hep/ConditionsPrinter.h"
#include "DD4hep/ConditionsProcessor.h"
#include "DD4hep/DetectorProcessor.h"

#include "DDCond/ConditionsSlice.h"
#include "DDCond/ConditionsManager.h"

/// Namespace for the AIDA detector description toolkit
namespace dd4hep {

  /// Namespace for conditions examples
  namespace ConditionExamples {

    using cond::UserPool;
    using cond::ConditionsPool;
    using cond::ConditionsSlice;
    using cond::ConditionsContent;
    using cond::ConditionsPrinter;
    using cond::ConditionsManager;
    using cond::ConditionUpdateCall;
    using cond::ConditionUpdateContext;
    using cond::conditionsCollector;
    
    /// Helper to reduce the number of lines of code
    /**
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class OutputLevel {
    public:
      /// Output level
      PrintLevel printLevel = DEBUG;
      /// Default constructor
      OutputLevel() = default;
      /// Initializing constructor
      OutputLevel(PrintLevel p) : printLevel(p) {}
      /// Default destructor
      virtual ~OutputLevel() = default;
    };

    /// Specialized condition only offering non-default ctor
    /**
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class NonDefaultCtorCond  {
    private:
      /// Inhibit default constructor
      NonDefaultCtorCond() = delete;
      /// Inhibit move constructor
      NonDefaultCtorCond(NonDefaultCtorCond&& copy) = delete;
      /// Inhibit copy constructor
      NonDefaultCtorCond(const NonDefaultCtorCond& copy) = delete;
      /// Inhibit move assignment
      NonDefaultCtorCond& operator=(NonDefaultCtorCond&& copy) = delete;
      /// Inhibit copy assignment
      NonDefaultCtorCond& operator=(const NonDefaultCtorCond& copy) = delete;
    public:
      int a, b, c, d;
      /// Initializing constructor only
      NonDefaultCtorCond(int aa, int bb, int cc);
      /// Default descructor
      virtual ~NonDefaultCtorCond();
      /// Set data member
      void set(int val);
    };
    
    /// Specialized conditions update callback 
    /**
     *  Used by clients to update a condition.
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class ConditionNonDefaultCtorUpdate1 : public ConditionUpdateCall, public OutputLevel  {
    public:
      /// Initializing constructor
      ConditionNonDefaultCtorUpdate1(PrintLevel p) : OutputLevel(p) {    }
      /// Default destructor
      virtual ~ConditionNonDefaultCtorUpdate1() = default;
      /// Interface to client Callback in order to update the condition
      virtual Condition operator()(const ConditionKey& key, ConditionUpdateContext& context) override  final;
      /// Interface to client Callback in order to update the condition
      virtual void resolve(Condition condition, ConditionUpdateContext& context) override  final;
    };

    /// Specialized conditions update callback with an unresolvable reference
    /**
     *  Used by clients to update a condition.
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class ConditionUpdateUnresolved : public ConditionUpdateCall, public OutputLevel  {
    public:
      /// Initializing constructor
      ConditionUpdateUnresolved(PrintLevel p) : OutputLevel(p) {    }
      /// Default destructor
      virtual ~ConditionUpdateUnresolved() = default;
      /// Interface to client Callback in order to update the condition
      virtual Condition operator()(const ConditionKey& key, ConditionUpdateContext& context) override  final;
      /// Interface to client Callback in order to update the condition
      virtual void resolve(Condition condition, ConditionUpdateContext& context) override  final;
    };

    /// Specialized conditions update callback 
    /**
     *  Used by clients to update a condition.
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class ConditionUpdate1 : public ConditionUpdateCall, public OutputLevel  {
    public:
      /// Initializing constructor
      ConditionUpdate1(PrintLevel p) : OutputLevel(p) {    }
      /// Default destructor
      virtual ~ConditionUpdate1() = default;
      /// Interface to client Callback in order to update the condition
      virtual Condition operator()(const ConditionKey& key, ConditionUpdateContext& context) override  final;
      /// Interface to client Callback in order to update the condition
      virtual void resolve(Condition condition, ConditionUpdateContext& context) override  final;
    };

    /// Specialized conditions update callback 
    /**
     *  Used by clients to update a condition.
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class ConditionUpdate2 : public ConditionUpdateCall, public OutputLevel  {
    public:
      /// Initializing constructor
      ConditionUpdate2(PrintLevel p) : OutputLevel(p) {    }
      /// Default destructor
      virtual ~ConditionUpdate2() = default;
      /// Interface to client Callback in order to update the condition
      virtual Condition operator()(const ConditionKey& key, ConditionUpdateContext& context) override   final;
      /// Interface to client Callback in order to update the condition
      virtual void resolve(Condition condition, ConditionUpdateContext& context) override  final;
    };

    /// Specialized conditions update callback 
    /**
     *  Used by clients to update a condition.
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class ConditionUpdate3 : public ConditionUpdateCall, public OutputLevel  {
    public:
      /// Initializing constructor
      ConditionUpdate3(PrintLevel p) : OutputLevel(p) {    }
      /// Default destructor
      virtual ~ConditionUpdate3() = default;
      /// Interface to client Callback in order to update the condition
      virtual Condition operator()(const ConditionKey& key, ConditionUpdateContext& context) override  final;
      /// Interface to client Callback in order to update the condition
      virtual void resolve(Condition condition, ConditionUpdateContext& context) override  final;
    };
    
    /// Specialized conditions update callback 
    /**
     *  Used by clients to update a condition.
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class ConditionUpdate4 : public ConditionUpdateCall, public OutputLevel  {
    public:
      /// Initializing constructor
      ConditionUpdate4(PrintLevel p) : OutputLevel(p) {    }
      /// Default destructor
      virtual ~ConditionUpdate4() = default;
      /// Interface to client Callback in order to update the condition
      virtual Condition operator()(const ConditionKey& key, ConditionUpdateContext& context) override  final;
    };
    
    /// Specialized conditions update callback 
    /**
     *  Used by clients to update a condition.
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class ConditionUpdate5 : public ConditionUpdateCall, public OutputLevel  {
    public:
      /// Initializing constructor
      ConditionUpdate5(PrintLevel p) : OutputLevel(p) {    }
      /// Default destructor
      virtual ~ConditionUpdate5() = default;
      /// Interface to client Callback in order to update the condition
      virtual Condition operator()(const ConditionKey& key, ConditionUpdateContext& context) override  final;
    };
    
    /// Specialized conditions update callback 
    /**
     *  Used by clients to update a condition.
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \ingroup DD4HEP_CONDITIONS
     */
    class ConditionUpdate6 : public ConditionUpdateCall, public OutputLevel  {
    public:
      size_t max_deps = 0;
      size_t min_deps = 999999999999UL;
      size_t num_deps = 0;
      size_t call_count = 0;
      /// Initializing constructor
      ConditionUpdate6(PrintLevel p) : OutputLevel(p) {    }
      /// Default destructor
      virtual ~ConditionUpdate6();
      /// Interface to client Callback in order to update the condition
      virtual Condition operator()(const ConditionKey& key, ConditionUpdateContext& context) override  final;
    };
    
    /// This is important, otherwise the register and forward calls won't find them!
    /**
     *  \author  M.Frank
     *  \version 1.02
     *  \date    01/04/2016
     */
    class ConditionsKeys : public OutputLevel {
    public:
      /// Content object to be filled
      ConditionsContent& content;
      /// Constructor
      ConditionsKeys(ConditionsContent& c, PrintLevel p) : OutputLevel(p), content(c) {}
      /// Callback to process a single detector element
      virtual int operator()(DetElement de, int level) const final;
    };

    /// Example how to populate the detector description with derived conditions
    /**
     *  This is simply a DetElement crawler...
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \date    01/04/2016
     */
    struct ConditionsDependencyCreator : public OutputLevel  {
      /// Content object to be filled
      ConditionsContent&   content;
      /// Three different update call types
      std::shared_ptr<ConditionUpdateCall> scall1, call1, call2, call3, call4, call5, call6, callUnresolved;
      /// Flag for special setup for ROOT persistency
      bool persist_conditions;
      /// Flag to indicate increased complexity
      int  extended;
      /// Constructor
      ConditionsDependencyCreator(ConditionsContent& c, PrintLevel p, bool persist=false, int extended=0);
      /// Destructor
      virtual ~ConditionsDependencyCreator() = default;
      /// Callback to process a single detector element
      virtual int operator()(DetElement de, int level) const final;
    };

    /// Example how to populate the detector description with conditions constants
    /**
     *  This is simply a DetElement crawler...
     *
     *  \author  M.Frank
     *  \version 1.0
     *  \date    01/04/2016
     */
    struct ConditionsCreator  : public OutputLevel  {
      /// Content object for which conditions are supposedly created
      ConditionsSlice& slice;
      /// Conditions pool the created conditions are inserted to (not equals user pool!)
      ConditionsPool&  pool;
      /// Constructor
      ConditionsCreator(ConditionsSlice& s, ConditionsPool& p, PrintLevel l=DEBUG)
        : OutputLevel(l), slice(s), pool(p)  {}
      /// Destructor
      virtual ~ConditionsCreator() = default;
      /// Callback to process a single detector element
      virtual int operator()(DetElement de, int level)  const final;
      template<typename T>
      Condition make_condition(DetElement de,
			       const std::string& name,
			       const T& val)  const;
      template<typename T, typename... Args>
      Condition make_condition_args(DetElement de,
				    const std::string& name,
				    Args... args)  const;
    };

    /// Example how to access the conditions constants from a detector element
    /**
     *  \author  M.Frank
     *  \version 1.0
     *  \date    01/04/2016
     */
    struct ConditionsDataAccess : public OutputLevel   {
      /// Reference to the IOV to be checked
      const IOV&     iov;
      /// Reference to the conditions map to access conditions
      ConditionsMap& map;

      /// Constructor
      ConditionsDataAccess(const IOV& i, ConditionsMap& m, PrintLevel l=DEBUG)
        : OutputLevel(l), iov(i), map(m) {}
      /// Destructor
      virtual ~ConditionsDataAccess() = default;
      /// Callback to process a single detector element
      virtual int operator()(DetElement de, int level)  const;
      /// Common call to access selected conditions
      virtual int accessConditions(DetElement de,
                                   const std::vector<Condition>& conditions)  const;
    };

    /// Helper to run DetElement scans
    typedef DetectorScanner Scanner;
    
    /// Install the conditions and the conditions manager
    ConditionsManager installManager(Detector& description);
  }       /* End namespace condExamples             */
}         /* End namespace dd4hep                         */
#endif // EXAMPLES_CONDITIONS_SRC_CONDITIONEXAMPLEOBJECTS_H
