#ifndef _fevt_EyeRecData_h_
#define _fevt_EyeRecData_h_

#include <fevt/Eye.h>
#include <fevt/CoordinateData.h>

#include <utl/TimeStamp.h>
#include <utl/AxialVector.h>
#include <utl/MultiTabulatedFunctionErrors.h>
#include <utl/ShadowPtr.h>
#include <utl/LameShadowPtr.h>

#include <boost/iterator/indirect_iterator.hpp>

#include <vector>

namespace evt {
  class ShowerFRecData;
}


namespace fevt {

  /*!
    
  \class EyeRecData
  \brief
  Eye-specific shower reconstruction data

  \author Stefano Argiro'
  \version $Id: EyeRecData.h 18993 2011-04-11 14:42:44Z kruppke $
  \ingroup fevt
*/


  class Pixel;

  class EyeRecData {

  private:
    typedef std::vector<fevt::Pixel*> InternalPixelContainer;
    typedef InternalPixelContainer::iterator InternalPixelIterator;
    typedef InternalPixelContainer::const_iterator ConstInternalPixelIterator;

  public:
    unsigned int GetEyeId() const { return fEyeId; }

    const utl::AxialVector& GetSDP() const { return fSDP; }
    double GetZeta() const { return fZeta; }
    double GetSDPThetaError() const { return fSDPThetaError; }
    double GetSDPPhiError() const { return fSDPPhiError; }
    double GetSDPCorrThetaPhi() const { return fSDPThetaPhiCorrelation; }
    double GetSDPFitChiSquare() const { return fSDPFitChiSquare; }
    unsigned int GetSDPFitNDof() const { return fSDPFitNDof; }
      
    double GetTZero() const { return fTZero; }
    double GetTZeroError() const { return fTZeroError; }
    double GetChiZero() const { return fChiZero; }
    double GetChiZeroError() const { return fChiZeroError; }
    double GetRp() const { return fRp; }
    double GetChi0TZeroCorrelation() const { return fChi0TZeroCorrelation; }
    double GetRpChi0Correlation() const { return fRpChi0Correlation; }
    double GetRpTZeroCorrelation() const { return fRpTZeroCorrelation; }
    double GetRpError() const { return fRpError; }
    double GetTimeFitChiSquare() const { return fTimeFitChiSquare; }
    unsigned int GetTimeFitNDof() const { return fTimeFitNDof; }

    /** The correlations between northing, easting, theta, phi and TCore
      * as coming from the axis/core fit.
      * theta/phi are in the core coordinate system */
    double GetNorthEastCorrelation() const { return fNorthEastCorrelation; }
    double GetNorthThetaCorrelation() const { return fNorthThetaCorrelation; }
    double GetNorthPhiCorrelation() const { return fNorthPhiCorrelation; }
    double GetNorthTCoreCorrelation() const { return fNorthTCoreCorrelation; }
    double GetEastThetaCorrelation() const { return fEastThetaCorrelation; }
    double GetEastPhiCorrelation() const { return fEastPhiCorrelation; }
    double GetEastTCoreCorrelation() const { return fEastTCoreCorrelation; }
    double GetThetaPhiCorrelation() const { return fThetaPhiCorrelation; }
    double GetThetaTCoreCorrelation() const { return fThetaTCoreCorrelation; }
    double GetPhiTCoreCorrelation() const { return fPhiTCoreCorrelation; }
    double GetAxisFitChiSquare() const { return fAxisFitChiSquare; }
    unsigned int GetAxisFitNDof() const { return fAxisFitNDof; }

    /// Iterator over pixels used in reconstruction
    typedef boost::indirect_iterator<InternalPixelIterator,
                                     fevt::Pixel&> PixelIterator;

    /// Const iterator over pixels used in reconstruction 
    typedef boost::indirect_iterator<ConstInternalPixelIterator,
                                     const Pixel&> ConstPixelIterator;

    PixelIterator PulsedPixelsBegin()
    { return PixelIterator(fPulsedPixels.begin()); }
    PixelIterator PulsedPixelsEnd()
    { return PixelIterator(fPulsedPixels.end()); }
    ConstPixelIterator PulsedPixelsBegin() const
    { return ConstPixelIterator(fPulsedPixels.begin()); }
    ConstPixelIterator PulsedPixelsEnd() const
    { return ConstPixelIterator(fPulsedPixels.end()); }

    /// add a pixel to the list of pulsed pixels
    void AddPulsedPixel(fevt::Pixel& pixel)
    { fPulsedPixels.push_back(&pixel); }

    /// remove a pixel from the list of pulsed pixels
    PixelIterator RemovePulsedPixel(PixelIterator it)
    { return PixelIterator(fPulsedPixels.erase(it.base())); }

    /// Get number of pulsed pixels
    unsigned int GetNPulsedPixels() const { return fPulsedPixels.size(); }

    PixelIterator SDPPixelsBegin()
    { return PixelIterator(fSDPPixels.begin()); }
    ConstPixelIterator SDPPixelsBegin() const
    { return ConstPixelIterator(fSDPPixels.begin()); }
    PixelIterator SDPPixelsEnd()
    { return PixelIterator(fSDPPixels.end()); }
    ConstPixelIterator SDPPixelsEnd() const
    { return ConstPixelIterator(fSDPPixels.end()); }

    /// add a pixel to the list of SDP pixels
    void AddSDPPixel(fevt::Pixel& pixel) { fSDPPixels.push_back(&pixel); }

    /// Remove a pixel from the list of SDP pixels
    /** @note: the right way to remove in a loop (STL) is
      * @code
      * for (EyeRecData::PixelIterator it = eyerecdata.SDPPixelsBegin();
      *      it != eyerecdata.SDPPixlesEnd(); )
      *   if (condition)
      *     it = eyerecdata.RemoveSDPPixel(it);
      *   else
      *     ++it;
      * @endcode */
    PixelIterator RemoveSDPPixel(PixelIterator it)
    { return PixelIterator(fSDPPixels.erase(it.base())); }

    /// Get number of SDP pixels
    unsigned int GetNSDPPixels() const { return fSDPPixels.size(); }
    
    PixelIterator TimeFitPixelsBegin()
    { return PixelIterator(fTimeFitPixels.begin()); }
    ConstPixelIterator TimeFitPixelsBegin() const
    { return ConstPixelIterator(fTimeFitPixels.begin()); }
    PixelIterator TimeFitPixelsEnd()
    { return PixelIterator(fTimeFitPixels.end()); }
    ConstPixelIterator TimeFitPixelsEnd() const
    { return ConstPixelIterator(fTimeFitPixels.end()); }
    
    /// add a pixel to the list of Time Fit pixels
    void AddTimeFitPixel(fevt::Pixel& pixel) { fTimeFitPixels.push_back(&pixel); }

    /// Remove a pixel from the list of Time Fit pixels
    PixelIterator RemoveTimeFitPixel(PixelIterator it)
    { return PixelIterator(fTimeFitPixels.erase(it.base())); }

    /// Get number of Time Fit pixels
    unsigned int GetNTimeFitPixels() const { return fTimeFitPixels.size(); }
    
    typedef std::vector<CoordinateData>::iterator CoordinateIterator;  
    typedef std::vector<CoordinateData>::const_iterator ConstCoordinateIterator;
    
    ConstCoordinateIterator CoordinatesBegin() const
    { return fCoordinateData.begin(); }

    ConstCoordinateIterator CoordinatesEnd() const
    { return fCoordinateData.end(); }

    CoordinateIterator CoordinatesBegin()
    { return fCoordinateData.begin(); }

    CoordinateIterator CoordinatesEnd()
    { return fCoordinateData.end(); }

    /// add a point to the list of coordinates
    void AddCoordinate(const fevt::CoordinateData& coor)
    { fCoordinateData.push_back(coor); }

    /// remove a point to the list of coordinates  
    CoordinateIterator RemoveCoordinate(CoordinateIterator it)
    { return fCoordinateData.erase(it); }

    unsigned int GetNCoordinates() const
    { return fCoordinateData.size(); }

    void ClearCoordinates() { fCoordinateData.clear(); }

    void SetEyeId(const unsigned int id) { fEyeId = id; }

    void SetZeta(const double zeta) { fZeta = zeta; }

    void SetSDP(const utl::AxialVector& vec) { fSDP = vec; }

    void SetSDPThetaError(const double sdpThetaError)
    { fSDPThetaError = sdpThetaError; }

    void SetSDPPhiError(const double sdpPhiError)
    { fSDPPhiError = sdpPhiError; }

    void SetSDPCorrThetaPhi (double sdpCorrThetaPhi)
    { fSDPThetaPhiCorrelation=sdpCorrThetaPhi; }

    void SetSDPFitChiSquare(const double sdpChi2, const unsigned int ndof)
    { fSDPFitChiSquare = sdpChi2; fSDPFitNDof = ndof; }

    void SetTimeFitChiSquare(const double tfitChi2, const unsigned int ndof)
    { fTimeFitChiSquare = tfitChi2; fTimeFitNDof = ndof; }

    void SetTZero(const double tzero, const double error)
    { fTZero = tzero; fTZeroError = error; }
    void SetChiZero(const double chiZero, const double error)
    { fChiZero = chiZero; fChiZeroError = error; }
    void SetRp(const double rp, const double error)
    {  fRp = rp; fRpError = error; }
    void  SetTimeFitCorrelations(double rRpT0, double rRpChi0, double rChi0T0)
    {
      fRpTZeroCorrelation=rRpT0;
      fRpChi0Correlation=rRpChi0;
      fChi0TZeroCorrelation=rChi0T0;
    }

    /** Set the correlations for nothing, easting, theta, phi and TCore
      * coming from the axis/core fit.
      * Theta/Phi are in the core coordinate system */
    void SetAxisFitCorrelations(double northEast,
                                double northTheta,
                                double northPhi,
                                double northTCore,
                                double eastTheta,
                                double eastPhi,
                                double eastTCore,
                                double thetaPhi,
                                double thetaTCore,
                                double phiTCore)
    {
      fNorthEastCorrelation = northEast;
      fNorthThetaCorrelation = northTheta;
      fNorthPhiCorrelation = northPhi;
      fNorthTCoreCorrelation = northTCore;
      fEastThetaCorrelation = eastTheta;
      fEastPhiCorrelation = eastPhi;
      fEastTCoreCorrelation = eastTCore;
      fThetaPhiCorrelation = thetaPhi;
      fThetaTCoreCorrelation = thetaTCore;
      fPhiTCoreCorrelation = phiTCore;
    }
    void SetAxisFitChiSquare(const double globalChi2, const unsigned int ndof)
    { fAxisFitChiSquare = globalChi2; fAxisFitNDof = ndof; }

    /// Light flux at diaphragm, photons/m^2 versus time in ns
    /** By default , the light profile corresponding to
     *  total signal is returned
     *  Use GetLightFlux (FdConstants::LightSource source)
     *  to get other sources , if added with MakeLightFlux(source)
     *  /example GetLightFlux(FdConstants::eCerenkov)*/
    utl::TabulatedFunctionErrors& GetLightFlux(const FdConstants::LightSource source = fevt::FdConstants::eTotal)
    { return fLightProfile->GetTabulatedFunctionErrors(source); }
    const utl::TabulatedFunctionErrors& GetLightFlux(const FdConstants::LightSource source = fevt::FdConstants::eTotal) const
    { return fLightProfile->GetTabulatedFunctionErrors(source); }

    /**   Add a Light Profile for source of Type FdConstants::LightSource
     *   /example MakeLightFlux(Telescope::eDirect); */
    void MakeLightFlux(const FdConstants::LightSource source = FdConstants::eTotal)
    { fLightProfile->AddTabulatedFunctionErrors(source); }

    /// Check that light profile for source /par source is present 	     
    bool HasLightFlux(const FdConstants::LightSource source = FdConstants::eTotal) const
    { return fLightProfile->HasLabel(source); }

    /// An iterator over the components of the Light Flux
    typedef utl::MultiTabulatedFunctionErrors::Iterator MultiLightFluxIterator;
    typedef utl::MultiTabulatedFunctionErrors::ConstIterator ConstMultiLightFluxIterator;

    /// Begin of the Light flux, returns a LabeledTabulatedFunctionErrors
    MultiLightFluxIterator LightFluxesBegin()
    { return fLightProfile->TabulatedFunctionsErrorsBegin(); }
    ConstMultiLightFluxIterator LightFluxesBegin() const
    { return fLightProfile->TabulatedFunctionsErrorsBegin(); }

    /// End of the Light flux, returns a LabeledTabulatedFunctionErrors
    MultiLightFluxIterator LightFluxesEnd()
    { return fLightProfile->TabulatedFunctionsErrorsEnd(); }
    ConstMultiLightFluxIterator LightFluxesEnd() const
    { return fLightProfile->TabulatedFunctionsErrorsEnd(); }

    /// Start Time of the photons trace
    utl::TimeStamp GetPhotonsStartTime() const { return fTraceStartTime; }

    void SetPhotonsStartTime(const utl::TimeStamp& ts)
    { fTraceStartTime = ts; }

    /// Reconstructed shower info for this eye
    evt::ShowerFRecData& GetFRecShower()
    { return *fShowerData; }
    const evt::ShowerFRecData& GetFRecShower() const
    { return *fShowerData; }
    /// Allocate reconstructed shower info
    void MakeFRecShower();
    /// Check if reconstructed shower info has been allocated
    bool HasFRecShower() const { return fShowerData; }

  private:
    EyeRecData(const unsigned int eyeId);
    ~EyeRecData();

    unsigned int fEyeId;

    //utl::Plane fSDP;       // Plane not yet implemented

    double fZeta;

    utl::AxialVector fSDP;
    double fSDPThetaError;
    double fSDPPhiError;
    double fSDPThetaPhiCorrelation;
    double fSDPFitChiSquare;
    unsigned int fSDPFitNDof;

    double fTZero;
    double fTZeroError;
    double fChiZero;
    double fChiZeroError;
    double fRp;
    double fRpError;
    double fRpTZeroCorrelation;
    double fRpChi0Correlation;
    double fChi0TZeroCorrelation;
    double fTimeFitChiSquare;
    unsigned int fTimeFitNDof;

    double fNorthEastCorrelation;
    double fNorthThetaCorrelation;
    double fNorthPhiCorrelation;
    double fNorthTCoreCorrelation;
    double fEastThetaCorrelation;
    double fEastPhiCorrelation;
    double fEastTCoreCorrelation;
    double fThetaPhiCorrelation;
    double fThetaTCoreCorrelation;
    double fPhiTCoreCorrelation;
    double fAxisFitChiSquare;
    unsigned int fAxisFitNDof;

    /// pixels with Fluorescence Pulse
    /** \note This class does not own the pointer to the pixels */
    InternalPixelContainer fPulsedPixels;

    /// pixels used for SDP
    /** \note This class does not own the pointer to the pixels */
    InternalPixelContainer fSDPPixels;

    /// pixels used for Time Fit
    /** \note This class does not own the pointer to the pixels */
    InternalPixelContainer fTimeFitPixels;

    std::vector<CoordinateData> fCoordinateData;

    utl::InitializedShadowPtr<utl::MultiTabulatedFunctionErrors> fLightProfile;
    utl::TimeStamp fTraceStartTime;

    utl::LameShadowPtr<evt::ShowerFRecData> fShowerData;

    friend class Eye;
    friend class utl::ShadowPtr<EyeRecData>;

  };

}


#endif

// Configure (x)emacs for this file ...
// Local Variables:
// mode: c++
// compile-command: "make -C .. FEvent/EyeRecData.o  -k"
// End: