//_____________________________________________________________________________
///
/// \class TrackFilterBFCalibND
///
/// Filter that selects only tracks useful for calibrating ND 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 <cmath>

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

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


// The ID of class Line
static const std::string BFCALIBND_TRACK_FILTER = "BFCalibND";

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

TrackFilter* Create() { return new TrackFilterBFCalibND; }

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

///
/// ctor
///
TrackFilterBFCalibND::TrackFilterBFCalibND() :
    fNHitsInViewMin(5), 
    fEndPlaneMax(115), 
    fNPlanesMin(20),
    fEdgeDistanceMin(0.25),
    fOutline()
{
    TracerSA trace("TrackFilterBFCalibND::TrackFilterBFCalibND()");
}

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

///
/// Pass method checks if the track passes cuts used to select
/// good stopping tracks to claibrate ND B-field. If any of the
/// cuts fail this method "short circuits" and returns false
///
Bool_t TrackFilterBFCalibND::Pass(const TrackContext& trackContext) const
{
    TracerSA trace("TrackFilterBFCalibND::Pass(const TrackContext&)");
    
    // 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();
    
    // beam track cut
    if ( beg > end ) return kFALSE;
    
    // EndPlane cut
    if ( end > fEndPlaneMax ) return kFALSE;
    
    // track length cut
    if ( (end-beg+1) < fNPlanesMin ) return kFALSE;
                                                             
    // track is within partial coverage area in the ND
    // (over its whole length)
    if ( ! IsInCoverage(trackContext) ) return kFALSE;
 
    return kTRUE;   
}        
        
///
/// check if a given track goes through "partial coverage area"
/// in the ND and that in every plane distance to the edge is
/// greater than fEdgeDistanceMin
///
Bool_t TrackFilterBFCalibND::IsInCoverage(const TrackContext& trackContext) const
{
    TracerSA trace("TrackFilterBFCalibND::IsInCoverage(const TrackContext&)");
    
    Int_t beg = trackContext.GetBegPlane();
    Int_t end = trackContext.GetEndPlane();

    VldContext vldc = trackContext.GetVldContext();
    Detector::Detector_t detector = vldc.GetDetector();
         
    UgliGeomHandle ugh(vldc);
    
    Float_t x(0.), y(0.);
    Float_t u(0.), v(0.);
    Float_t distance(0.), xdistance(0.), ydistance(0.);
            
    for ( Int_t i = beg; i <= end; ++i) {
        PlexPlaneId planeId(detector, i);
        
        u = trackContext.GetU(i);
        v = trackContext.GetV(i);

        // skip test if u or v have not been set by the tracker/fitter
        // (their default values then are -99999) or if look obviously
        // wrong
        if ( fabs(u) > 10. || fabs(v) > 10. ) continue;
        
        ugh.uv2xy(u, v, x, y);
        // fail if track position is outside of "PartialCoverage" area
        if ( ! fOutline.IsInside(x, y, planeId.GetPlaneView(), 
                        PlaneCoverage::kNearPartial) ) return kFALSE;
        // fail if distance to edge is less than allowed
        fOutline.DistanceToEdge(x, y, planeId.GetPlaneView(), 
                                     planeId.GetPlaneCoverage(),
                                     distance, xdistance, ydistance);
        if ( distance < fEdgeDistanceMin ) return kFALSE;
    }
    
    return kTRUE;
}                   
    
///
/// read configuration parameters from AlgConfig
///
void TrackFilterBFCalibND::Config(const AlgConfig& ac)
{
    TracerSA trace("TrackFilterBFCalibND::Config(const AlgConfig&)");
    
    if ( ac.KeyExists("NHitsInViewMin") ) {
        fNHitsInViewMin = ac.GetInt("NHitsInViewMin");
    }
    
    if ( ac.KeyExists("FilterBFCalibNDEndPlaneMax") ) {
        fEndPlaneMax = ac.GetInt("FilterBFCalibNDEndPlaneMax");
    }
    
    if ( ac.KeyExists("FilterBFCalibNDNPlanesMin") ) {
        fNPlanesMin = ac.GetInt("FilterBFCalibNDNPlanesMin");
    }
    
    if ( ac.KeyExists("FilterBFCalibNDEdgeDistanceMin") ) {
        fEdgeDistanceMin = ac.GetDouble("FilterBFCalibNDEdgeDistanceMin");
    }
}

