//_____________________________________________________________________________
///
/// \class TrackFilterBFCalibFD
///
/// Filter that selects only tracks useful for calibrating FD b-field.
/// Requires minimum number of planes, entire track goes through
/// "partial coverage" area and ends in the detector.
///
/// \author Sergei avva@fnal.gov
///

#include "Algorithm/AlgConfig.h"
#include "UgliGeometry/UgliGeomHandle.h"

#include "CandFitTrackSA/TracerSA.h"
#include "CandFitTrackSA/TrackFilterFactory.h"
#include "CandFitTrackSA/TrackFilterBFCalibFD.h"
#include "CandFitTrackSA/TrackContext.h"

#include <cmath>

// The ID of class Line
static const std::string BFCALIBFD_TRACK_FILTER = "BFCalibFD";

// Create an anonymous namespace
// to make the function invisible from other modules
namespace {

TrackFilter* Create() { return new TrackFilterBFCalibFD; }

// register block
bool registered = TrackFilterFactory::Instance().RegisterTrackFilter(
                                                    BFCALIBFD_TRACK_FILTER,
                                                            Create);
}  // namespace

///
/// ctor
///
TrackFilterBFCalibFD::TrackFilterBFCalibFD() :
    fNHitsInViewMin(4),
    fNPlanesMin(20),
    fZDistanceMin(0.5),
    fRadiusMin(0.5),
    fRadiusMax(3.5)
{
    TracerSA trace("TrackFilterBFCalibFD::TrackFilterBFCalibFD()");
}

///
/// dtor
///
TrackFilterBFCalibFD::~TrackFilterBFCalibFD()
{
    TracerSA trace("TrackFilterBFCalibFD::~TrackFilterBFCalibFD()");
}


///
/// read configuration parameters from AlgConfig
///
void TrackFilterBFCalibFD::Config(const AlgConfig& ac)
{
    TracerSA trace("TrackFilterBFCalibFD::Config(const AlgConfig&)");

    if ( ac.KeyExists("NHitsInViewMin") ) {
        fNHitsInViewMin = ac.GetInt("NHitsInViewMin");
    }

    if ( ac.KeyExists("FilterBFCalibFDNPlanesMin") &&
            ac.GetType("FilterBFCalibFDNPlanesMin") == typeid(Int_t) ) {
        fNPlanesMin = ac.GetInt("FilterBFCalibFDNPlanesMin");
    }

    if ( ac.KeyExists("FilterBFCalibFDZDistanceMin") &&
            ac.GetType("FilterBFCalibFDZDistanceMin") == typeid(Double_t) ) {
        fZDistanceMin = ac.GetDouble("FilterBFCalibFDZDistanceMin");
    }

    if ( ac.KeyExists("FilterBFCalibFDRadiusMin") &&
            ac.GetType("FilterBFCalibFDRadiusMin") == typeid(Double_t) ) {
        fRadiusMin = ac.GetDouble("FilterBFCalibFDRadiusMin");
    }

    if ( ac.KeyExists("FilterBFCalibFDRadiusMax") &&
            ac.GetType("FilterBFCalibFDRadiusMax") == typeid(Double_t) ) {
        fRadiusMax = ac.GetDouble("FilterBFCalibFDRadiusMax");
    }

}


///
/// Pass method checks if the track passes cuts used to select
/// good stopping tracks to claibrate FD B-field. If any of the
/// cuts fail this method "short circuits" and returns false
///
Bool_t TrackFilterBFCalibFD::Pass(const TrackContext& trackContext) const
{
    TracerSA trace("TrackFilterBFCalibFD::Pass(const TrackContext&)");


    // set Z extent
    // doing it every event makes filter slow, so
    // instead do it once and save in static variables
    static Bool_t   zExtentSet  = kFALSE;
    static Double_t zMinSM1     = 0.;
    static Double_t zMaxSM1     = 0.;
    static Double_t zMinSM2     = 0.;
    static Double_t zMaxSM2     = 0.;

    // due to some bizarro convention id of SM1 is "0"
    // and id of SM2 is "1"
    static const Int_t Sm1 = 0;
    static const Int_t Sm2 = 1;

    if ( ! zExtentSet ) {
        UgliGeomHandle ugh(trackContext.GetVldContext());

        ugh.GetZExtent(zMinSM1, zMaxSM1, Sm1);
        ugh.GetZExtent(zMinSM2, zMaxSM2, Sm2);

        zExtentSet = kTRUE;
    }

    // check if passes #hits cut in both views
    if ( trackContext.GetNTrackPlaneU() < fNHitsInViewMin ) return kFALSE;
    if ( trackContext.GetNTrackPlaneV() < fNHitsInViewMin ) return kFALSE;

    Int_t beg = trackContext.GetBegPlane();
    Int_t end = trackContext.GetEndPlane();

    // track length cut
    if ( abs(end-beg)+1 < fNPlanesMin ) return kFALSE;

    // get track end coord's
    const CandTrackHandle*  pcth = trackContext.GetTrackHandle();
    Double_t endu = pcth->GetEndU();
    Double_t endv = pcth->GetEndV();
    Double_t endz = pcth->GetEndZ();

    // endtrack position along Z axis should be at least
    // fMinZDistance from the closest supermodule boundary
    if ( endz<(zMinSM1+fZDistanceMin) ||
        ( endz>(zMaxSM1-fZDistanceMin) && endz<(zMinSM2+fZDistanceMin) ) ||
        endz>(zMaxSM2-fZDistanceMin) ) return kFALSE;

    // check that fMinRadius < radius < fMaxRadius
    Double_t radius = sqrt(endu*endu + endv*endv);
    if ( radius<fRadiusMin || radius>fRadiusMax ) return kFALSE;

    return kTRUE;
}

