#ifndef DataFT_H
#define DataFT_H
/*************************************************************************
                          DataFT.h  -  description
  Class encapsulates the track measurement - coordinates measured in each
  plane; used by the least squares track fitter.
                             -------------------
    begin                : Thu Jun 26 2003
    author               : Sergei Avvakumov
    email                : avva@dunk
 *****************************************************************************/

#include <vector>
#include <map>
#include <algorithm>

#include "TVectorD.h"
#include "TMatrixD.h"

#include "Algorithm/AlgConfig.h"
#include "RecoBase/CandStripHandle.h"
#include "Swimmer/SwimSwimmer.h"

#include "GeometryHelper.h"
#include "ConstFT.h"
#include "PlaneData.h"
#include "TrackContext.h"

//class CandStripHandle;

class DataFT {

public:

    typedef Int_t                       Count_t;
    typedef UShort_t                    Mask_t;
    typedef std::vector<Mask_t>         ViewMask_t;

private:

    typedef std::vector<PlaneData>      PlaneDataVec_t;

    typedef PlaneDataVec_t::iterator                PlaneDataItr;
    typedef PlaneDataVec_t::const_iterator          PlaneDataCItr;
    typedef PlaneDataVec_t::const_reverse_iterator  PlaneDataCRItr;

    typedef PlaneData::Data_t (DataFT::*FGETTER)(Int_t) const;
    typedef void  (DataFT::*FSETTER)(Int_t, PlaneData::Data_t);
    typedef bool  (DataFT::*BGETTER)(Int_t) const;

    typedef const CandStripHandle CSH;
    typedef CSH* CSHPtr;
    typedef std::vector<CSHPtr>::iterator CSHItr;

    typedef std::multimap<Int_t, Reco::Strip_t> StripMap;
    typedef StripMap::iterator StripMapItr;


public:
   //DataFT();
   DataFT(const AlgConfig& ac, const TrackContext& tc);
   
   ~DataFT();

   Bool_t       Init(const TrackContext& );
   Bool_t       Fill(const TrackContext& );


   PlaneData& at(PlaneDataVec_t::size_type index) {
#ifdef DEBUG
        return fPlanes.at(index);
#else
        return fPlanes[index];
#endif
    };

   const PlaneData& at(PlaneDataVec_t::size_type index) const {
#ifdef DEBUG
        return fPlanes.at(index);
#else
        return fPlanes[index];
#endif
    };

   // accessors
   Count_t        GetNPlanes() const { return fPlanes.size();};
   Count_t        GetNUPlanes() const ;
   Count_t        GetNVPlanes() const ;
   Count_t        GetNPlanesUsed() const { return fNPlanesUsed;};
   Count_t        GetNPlanesMin(Count_t nhitsperview) const ;

   PlaneData::Data_t      GetU(Count_t i) const {return at(i).u;};
   PlaneData::Data_t      GetSigmaU(Count_t i) const {return at(i).eu;};
   PlaneData::Data_t      GetV(Count_t i) const {return at(i).v;};
   PlaneData::Data_t      GetSigmaV(Count_t i) const {return at(i).ev;};

   PlaneData::Data_t      GetUMean() const;
   PlaneData::Data_t      GetVMean() const;

   Bool_t       IsHitU(Count_t i) const { return at(i).uhit; };
   Bool_t       IsHitV(Count_t i) const { return at(i).vhit; };
//    Bool_t       UHitUse(Count_t i) const { return at(i).uhituse; };
//    Bool_t       VHitUse(Count_t i) const { return at(i).vhituse; };

//    Bool_t       IsHitU(Count_t i) const { return fUHit[i]; };
//    Bool_t       IsHitV(Count_t i) const { return fVHit[i]; };

   Bool_t       UHitUse(Count_t i) const { return fUHitUse[i]; };
   Bool_t       VHitUse(Count_t i) const { return fVHitUse[i]; };

   const ViewMask_t& GetUHitUse() const { return fUHitUse; };
   const ViewMask_t& GetVHitUse() const { return fVHitUse; };
   
   void SetUHitUse(const ViewMask_t&  usehitu) {fUHitUse = usehitu;};
   void SetVHitUse(const ViewMask_t&  usehitv) {fVHitUse = usehitv;};
   
   Int_t        GetZdir() const { return fDir; };
   
   PlaneData::Data_t      GetZVtx() const { return fZVtx; };
   PlaneData::Data_t      GetZ(Count_t i) const { return at(i).z;};
   PlaneData::Data_t      GetdZSteel(Count_t i) const { return at(i).dz;};
   PlaneData::Data_t      GetX0(Count_t i) const { return at(i).x0;};

   Count_t        GetNUHits() const ;
   Count_t        GetNVHits() const ;
   Count_t        GetNHits() const ;
   Count_t        GetNUHitsUsed() const ;
   Count_t        GetNVHitsUsed() const ;
   Count_t        GetNHitsUsed()  const ;

   PlexPlaneId  GetBegPlaneId() const { return fPlanes.begin()->plane; };
   PlexPlaneId  GetEndPlaneId() const { return fPlanes.rbegin()->plane; };
   PlexPlaneId  GetPlaneId(Count_t i) const { return at(i).plane;};
   Count_t        GetPlane(Count_t i) const { return at(i).plane.GetPlane();};
   
   Count_t        GetBegHit() const ;
   Count_t        GetEndHit() const ;
   Count_t        GetBegHitUsed() const ;
   Count_t        GetEndHitUsed() const ;

   // setters
   void         SetNPlanesUsed(Count_t n);

   void         SetU(Count_t i, PlaneData::Data_t u) { at(i).u = u; };
   void         SetSigmaU(Count_t i, PlaneData::Data_t eu) { at(i).eu = eu; };
   void         SetV(Count_t i, PlaneData::Data_t v) { at(i).v = v; };
   void         SetSigmaV(Count_t i, PlaneData::Data_t ev) { at(i).ev = ev; };

   void         SetUHit(Count_t i) { at(i).uhit = 1; };
   void         SetVHit(Count_t i) { at(i).vhit = 1; };
   void         UnsetUHit(Count_t i) { at(i).uhit = 0; };
   void         UnsetVHit(Count_t i) { at(i).vhit = 0; };
//    void         SetUHitUse(Count_t i) { at(i).uhituse = 1; };
//    void         SetVHitUse(Count_t i) { at(i).vhituse = 1; };
//    void         UnsetUHitUse(Count_t i) { at(i).uhituse = 0; };
//    void         UnsetVHitUse(Count_t i) { at(i).vhituse = 0; };

//    void         SetUHit(Count_t i) { fUHit[i] = kTRUE; };
//    void         SetVHit(Count_t i) { fVHit[i] = kTRUE; };
//    void         UnsetUHit(Count_t i) { fUHit[i] = kFALSE; };
//    void         UnsetVHit(Count_t i) { fVHit[i] = kFALSE; };
   void         SetUHitUse(Count_t i) { fUHitUse[i] = kTRUE; };
   void         SetVHitUse(Count_t i) { fVHitUse[i] = kTRUE; };
   void         UnsetUHitUse(Count_t i) { fUHitUse[i] = kFALSE; };
   void         UnsetVHitUse(Count_t i) { fVHitUse[i] = kFALSE; };
   
   
   void         SetZ(Count_t i, PlaneData::Data_t z) { at(i).z = z; };
   void         SetdZSteel(Count_t i, PlaneData::Data_t dz) { at(i).dz = dz; };
   void         SetX0(Count_t i, PlaneData::Data_t x0) { at(i).x0 = x0; };
   void         SetPlane(Count_t i, PlexPlaneId plane) { at(i).plane = plane; };


   // from trackFT
   PlaneData::Data_t      GetUf(Count_t i) const { return at(i).uf; };
   PlaneData::Data_t      GetDudz(Count_t i) const { return at(i).dudz; };
   PlaneData::Data_t      GetVf(Count_t i) const { return at(i).vf; };
   PlaneData::Data_t      GetDvdz(Count_t i) const { return at(i).dvdz; };

   PlaneData::Data_t      GetUlin(Count_t i) const {return at(i).ulin;};
   PlaneData::Data_t      GetVlin(Count_t i) const {return at(i).vlin;};
   PlaneData::Data_t      GetDudzlin(Count_t i) const {return at(i).dudzlin;};
   PlaneData::Data_t      GetDvdzlin(Count_t i) const {return at(i).dvdzlin;};

   PlaneData::Data_t      GetInvP(Count_t i) const { return at(i).invp; };
   PlaneData::Data_t      GetP(Count_t i) const { return 1./GetInvP(i); };
   PlaneData::Data_t      GetS(Count_t i) const { return at(i).s; };
   PlaneData::Data_t      GetR(Count_t i) const { return at(i).r; };
   PlaneData::Data_t      GetCos(Count_t i) const { return at(i).cos;};

   Int_t        GetEMCharge() const;
   
   TVectorD     GetTrack() const { return fInitialTrack; };

   void         SetUf(Count_t i, PlaneData::Data_t uf) { at(i).uf = uf; };
   void         SetDudz(Count_t i, PlaneData::Data_t dudz) { at(i).dudz = dudz; };
   void         SetVf(Count_t i, PlaneData::Data_t vf) { at(i).vf = vf; };
   void         SetDvdz(Count_t i, PlaneData::Data_t dvdz) { at(i).dvdz = dvdz; };
   void         SetInvP(Count_t i, PlaneData::Data_t invp) { at(i).invp = invp; };
   void         SetS(Count_t i, PlaneData::Data_t s) { at(i).s = s; };
   void         SetR(Count_t i, PlaneData::Data_t r) { at(i).r = r; };
   void         SetCos(Count_t i, PlaneData::Data_t cos) { at(i).cos = cos;};

   void         SetUlin(Count_t i, PlaneData::Data_t ulin) { at(i).ulin = ulin; };
   void         SetVlin(Count_t i, PlaneData::Data_t vlin) { at(i).vlin = vlin; };
   void         SetDudzlin(Count_t i, PlaneData::Data_t dudzlin) 
                                             { at(i).dudzlin = dudzlin; };
   void         SetDvdzlin(Count_t i, PlaneData::Data_t dvdzlin) 
                                             { at(i).dvdzlin = dvdzlin; };

   void         SetInitial(const TVectorD& inittrack);
   
   Count_t        SetInitial(const TVectorD& inittrack, Count_t nplanesuse,
                                                    SwimSwimmer& swimmer);

   // methods
//    Bool_t       LinearFitEstimate(Count_t initp, TVectorD& inittrack) const;

   void         FillVectorC(TMatrixD& vC) const;

//    Bool_t       Filter(Count_t nHitsSegment, Count_t nPolynomParams,
//                                                         PlaneData::Data_t chi2cut);

   Bool_t       FillUArrays(Double_t zu[], Double_t u[], Int_t n);
   
   Bool_t       FillVArrays(Double_t zv[], Double_t v[], Int_t n);
   
   Bool_t       FillLinWithSpline();

//    Bool_t       FillLin(Count_t nHitsSegment, Count_t nPolynomParams);

   void         PrintData() const ;

   // methods from trackFT
   void         FillVectorCF(TMatrixD& vC) const;
   void         FillVectorRes(TMatrixD& vRes, PlaneData::Data_t cdata=1.,
                                                PlaneData::Data_t ctrack=-1.) const;

   Count_t        GetNPlanesPCut(PlaneData::Data_t pcut) const ;

   Count_t        SwimAsSwimmer(SwimSwimmer& swimmer);
   Count_t        SwimAsSwimmer(Count_t nplanes, SwimSwimmer& swimmer);

//    Count_t        SwimAsData(SwimSwimmer& swimmer);
//    Count_t        SwimAsData(Count_t nplanes, SwimSwimmer& swimmer);

   PlaneData::Data_t       ThetaMCS(Count_t i) const ;
   PlaneData::Data_t       T(Count_t i, Count_t n) const ;

   void         DumpTrack() const ;
   void         PrintMomenta() const;
   
   void         Reset();

    ///
    /// configure from AlgConfig 
    ///
    void        Config(const AlgConfig& );
    
    void        SetHitCoords(Count_t planeIndex, PlaneData::Data_t tpos, PlaneData::Data_t error);
    
    void        OneStripPlane(Count_t i, StripMapItr it, 
                                            const CandTrackHandle* cth);
    void        TwoStripPlane(Count_t i, StripMapItr lower, 
                            StripMapItr upper, const CandTrackHandle* cth);

private:

//    Count_t        FilterStep(Count_t nHitsSegment, Count_t nPolynomParams, 
//                         PlaneData::Data_t filterChi2Cut,
//                         FGETTER pfC, FGETTER pfSigmaC, BGETTER pfHit);
//                         
//    void         SetSegment(Count_t i, Count_t nHitsSegment, 
//                         std::map<Count_t, Count_t>& segment, BGETTER pfHit) const ;
//    
//    Count_t        PolynomialFit(Count_t n, Count_t zdir,
//                            FGETTER pfZ, FGETTER pfC, BGETTER pfChit, 
//                            Count_t npar, PlaneData::Data_t* a) const ;
//                            
//    void         SetSegmentLin(Count_t i, Count_t nHitsSegment, 
//                            std::map<Count_t, Count_t>& segment, BGETTER pfHit) const ;
//    
//    Bool_t       FillLinView(Count_t nHitsSegment, Count_t nPolynomParams,
//                            FGETTER pfC, BGETTER pfHit, 
//                            FSETTER pfCset, FSETTER pfdCdZset);


   ///
   /// number of planes used in the fit, starting from fBegPlane
   ///
   Short_t      fNPlanesUsed;              
   
   ///
   /// direction along Z axis possible values 1, -1
   ///
   Short_t      fDir;
                         
   ///
   /// 0 - u0, 1 - dudz0, 2 - v0, 3 - dvdz0, 4 - 1/p0
   ///
   TVectorD     fInitialTrack;             

   ///
   /// z position of the vertex (middle of a steel plane)
   ///
   PlaneData::Data_t      fZVtx;
   
   ///
   /// track and fit data
   ///
   PlaneDataVec_t fPlanes;
    
   ///
   /// "mask" vector of what U hits should be used in the fit
   ///
   ViewMask_t  fUHitUse;
    
   ///
   /// "mask" vector of what U hits should be used in the fit
   ///
   ViewMask_t  fVHitUse;
    
   ///
   /// GeometryHelper
   ///
   GeometryHelper fGeo;

   // configurable parameters

   ///
   /// minimum strip charge
   ///
   PlaneData::Data_t fMinStripCharge;
   
   ///
   /// calculate tpos of a hit accounting for its longitudinal position
   /// along the strip if this flag set (otherwise - just stip->GetTPos())
   ///
   Bool_t fSetLPos;
   
   ///
   /// min # of hit planes per view
   ///
   Int_t  fNHitsInViewMin;

   bool  fUseGeoSwimmer;

};

///
/// Functor to sort strips by plane number
///
struct ByPlane :  public std::binary_function<const CandStripHandle*,
                                                const CandStripHandle*, Bool_t>
{
    Bool_t operator()(const CandStripHandle* a, const CandStripHandle* b) {
        return (a->GetPlane() < b->GetPlane());
    }
};

#endif
