////////////////////////////////////////////////////////////////////
/// \class RAT::DS::SOC
///
/// \brief  SOC file class
///
/// \author P G Jones <p.g.jones@qmul.ac.uk>
/// \author Rob Stainforth <R.Stainforth@liverpool.ac.uk> -- contact person
///
/// REVISION HISTORY:\n
///      2 Oct 2012 : P Jones - New File.\n
///      8 Jan 2014 : J Maneira - Added methods for Source pos. from
///                               manipulator, global time offset
///                               and wavelength.\n
///      2014-04-02 : P Jones - Refactor for the DS review.\n
///      2015-05-07 : J Maneira - Added the counting of number of pulses,
///                               also summed up when merging files.\n
///
/// \details The run Summary Optical Calibration data. This is
///         source specific information.
///
////////////////////////////////////////////////////////////////////

#ifndef __RAT_DS_SOC__
#define __RAT_DS_SOC__

#include <RAT/ListHelp.hh>
#include <RAT/DS/SOCPMT.hh>
#include <RAT/DS/FitResult.hh>
#include <RAT/DS/Calib.hh> //MUST Delete when the DB works properly

#include <TObject.h>
#include <map>
#include <string>

namespace RAT
{
namespace DS
{

class SOC : public TObject
{
public:
  SOC() : TObject(), runID(0) { }

  /// Get a SOCPMT using the PMT ID
  ///
  /// @param[in] id of the pmt
  /// @return reference to the SOCPMT
  /// @throws out_of_range if the id is not present
  SOCPMT& GetSOCPMT( const UInt_t id ) { return pmts.at( id ); }
  /// @copydoc GetSOCPMT(size_t)
  const SOCPMT& GetSOCPMT( const UInt_t id ) const { return pmts.at( id ); }

  /// Get the IDs of the SOCPMTs
  ///
  /// @return the number of SOCPMTs
  std::vector<UInt_t> GetSOCPMTIDs() const { return keys_in_map<UInt_t, SOCPMT>( pmts ); }

  /// Check if a SOCPMT with id exists
  ///
  /// @param[in] id of the pmt to check
  /// @return true if it does
  Bool_t SOCPMTExists( const UInt_t id ) const { return pmts.count( id ) > 0; }

  /// Add a new SOCPMT to the collection
  ///
  /// @param[in] pmt to add
  void AddSOCPMT( const SOCPMT& pmt ) { pmts[pmt.GetID()] = pmt; }

  /// Prune/delete the SOCPMT collection
  void PruneSOCPMTs() { pmts.clear(); }

  /// Set the source identifier
  ///
  /// @param[in] id of the source
  void SetSourceID( const std::string& id ) { sourceID = id; }

  /// Get the source identifier
  ///
  /// @return the source id
  std::string GetSourceID() const { return sourceID; }

  /// Set the ID of the run this event belongs to
  ///
  /// @param[in] id the run ID
  void SetRunID( const UInt_t id ) { runID = id; }

  /// Get the ID of the run this event belongs to
  ///
  /// @return the run ID
  UInt_t GetRunID() const { return runID; }

  /// Set the global time offset
  ///
  /// @param[in] offset to set
  void SetGlobalTimeOffset( const Float_t offset ) { globalTimeOffset = offset; }

  /// Get the global time offset
  ///
  /// @return the offset
  Float_t GetGlobalTimeOffset() const { return globalTimeOffset; }

  /// Set the number of triggered pulses
  ///
  /// @param[in] nPulses to set
  void SetNPulsesTriggered( const UInt_t nPulses ) { nPulsesTriggered = nPulses; }

  /// Get the number of triggered pulses
  ///
  /// @return the number of triggered pulses
  Float_t GetNPulsesTriggered() const { return nPulsesTriggered; }


  /// Get the fit names
  ///
  /// @return a vector of the fit names
  std::vector<std::string> GetFitNames() const { return keys_in_map<std::string, FitResult>( fitResults ); }

  /// Get Property for the FitResults
  ///
  /// @param[in] name of fit
  /// @return result of fitter (throws error if not present)
  const FitResult& GetFitResult( const std::string& name ) const { if( fitResults.count( name ) == 0 ) throw FitResult::NoFitResultError("EV", name); return fitResults.at(name); }

  /// Set Property for the FitResults
  ///
  /// param[in] name of fit
  /// param[in] fitResult
  void SetFitResult( const std::string& name, const FitResult& fitResult ) { fitResults[name] = fitResult; }

  /// MUST Delete when the DB works properly
  Calib& GetCalib() { return calib; }
  /// @copydoc GetCalib
  const Calib& GetCalib() const { return calib; }

  /// Merge another SOC class into this, only merges SOCPMT information
  ///
  /// @param[in] soc to merge in
  inline void Merge( const SOC& soc );

  // This ROOT macro adds dictionary methods to this class.
  // The number should be incremented whenever this class's members are changed.
  // It assumes this class has no virtual methods, use ClassDef if change this.
  ClassDefNV(SOC,1);
protected:
  std::map<UInt_t, SOCPMT> pmts; ///< The SOCPMT information indexed by lcn
  std::map<std::string, FitResult> fitResults; ///< The reconstructed source information, indexed by fit name
  Float_t globalTimeOffset; ///< Time offset for this run common to all channels
  UInt_t nPulsesTriggered; // Number of pulses generated or acquired, passing trigger selection
  Calib calib; ///< MUST Delete when the DB works properly

  std::string sourceID; ///< The source ID for simple reference.
  UInt_t runID; ///< The run ID
};

inline void
SOC::Merge( const SOC& soc )
{
  for( std::map<UInt_t, SOCPMT>::const_iterator iTer = soc.pmts.begin(); iTer != soc.pmts.end(); iTer++ )
    {
      if( pmts.count( iTer->first ) == 1 )
        pmts[iTer->first].Merge( iTer->second );
      else
        pmts[iTer->first] = iTer->second;
    }
    nPulsesTriggered += soc.nPulsesTriggered;
}

} // namespace DS

} // namespace RAT

#endif