#ifndef __TQ_GRIDSCANNER__
#define __TQ_GRIDSCANNER__
#include <vector>
#include "TString.h"
#include "TMVA/Timer.h"
#include "QFramework/TQGridScanPoint.h"
#include "QFramework/TQGridScanResults.h"
#include "QFramework/TQFolder.h"
#include "QFramework/TQSignificanceEvaluator.h"
#include "QFramework/TQTaggable.h"
class TQGridScanObservable;
class TQGridScanner : public TQTaggable, public TNamed {
public:
using ObsVec = std::vector<TQGridScanObservable*>;
using CutBounds = std::vector<std::pair<int, int>>;
using SplitCutVals = std::vector<double>;
using SplitCutBins = std::vector<int>;
using BoundDirection = TQGridScanBound::Direction;
TQGridScanner() {}
TQGridScanner(const TString& name, TQSignificanceEvaluator* evaluator);
TQGridScanner(const TString& name, TQSignificanceEvaluator* evaluator, TList* obsToScan);
~TQGridScanner();
bool addSignalHist(THnBase* hist);
bool addBkgHist(THnBase* hist);
TQGridScanObservable* getObs(const TString& obsName);
std::vector<TString> obsNamesToScan() {return m_obsNamesToScan;}
void addFOMDefinitions(const std::vector <TString>& definitions);
int prepare();
void run(std::vector<int> chunk = {});
TQGridScanResults* results() { return &m_results; }
void plotAndSaveAllSignificanceProfiles(int topNumber, const TString& options);
void plotAndSaveSignificanceProfile(BoundDirection direction, int i, int topNumber, const TString& options);
std::unique_ptr<TH1F> getSignificanceProfile(BoundDirection direction, int i, int topNumber);
void setVerbose(bool v = true) { m_verbose = v; }
std::vector<TQGridScanPoint>& points() { return m_points; }
const TString& nDimHistName() const { return m_nDimHistName; }
void extractInputHistogramProjections();
void dumpInputHistogramProjections(TQTaggable& tags);
bool hasSplitObs = false;
int getNumberOfPoints() { return m_nPoints; }
protected:
void init();
bool isAcceptedPoint(const TQGridScanPoint& point);
void scan(CutBounds::iterator obsValsIter, ObsVec::iterator obsIter, std::vector<int> chunk);
double splitSignif2(SplitCutBins::iterator obsBinsIter, ObsVec::iterator obsIter, bool isScanSplit);
void scanSplit(SplitCutBins::iterator obsBinsIter, ObsVec::iterator obsIter, std::vector<int> chunk);
double splitSignifHandleRecursion(
bool isFinalLevel,
bool isScanSplit,
SplitCutBins::iterator obsBinsIter,
ObsVec::iterator obsIter
);
bool updateHeartbeat();
void updateProgress();
bool evaluatePoint(std::vector<int> chunk);
std::vector<TString> m_obsNamesToScan;
std::map< TString, TQGridScanObservable*> m_observables;
std::vector< THnBase*> m_signalHists;
std::vector< THnBase*> m_bkgHists;
TQSignificanceEvaluator* m_evaluator = nullptr;
std::vector<TQGridScanPoint> m_points;
ObsVec m_normalObs;
ObsVec m_splitObs;
ObsVec m_splitScanObs;
CutBounds m_normalBounds;
SplitCutBins m_splitBins;
SplitCutBins m_splitScanBins;
TString m_heartBeatCommand;
unsigned long m_heartBeatInterval;
unsigned long m_heartbeat;
bool m_sorted = false;
bool m_verbose = false;
TQGridScanResults m_results;
TMVA::Timer m_runTimer;
int m_nPoints = 1;
int m_nPointsProcessed = 0;
TString m_nDimHistName;
ClassDefOverride(TQGridScanner,3)
};
#endif