#ifndef __TQ_GRIDSCANOBSERVABLE__
#define __TQ_GRIDSCANOBSERVABLE__

// TODO: remove this once full C++11 is used
#include <climits>
#include <iostream>

#include "boost/optional.hpp"
#include "boost/variant.hpp"
#include "boost/variant/get.hpp"

#include "QFramework/TQGridScanBound.h"

#include "TAxis.h"
#include "TH1.h"

using std::vector;

// Warning: namespaces don't play well with ROOT; use ugly verbose names instead

// These hold the relevant information about observables when initializing the grid scan
// Not meant to be serialised
class TQGridScanObservable : public TObject {
public:
  // These can be used to ergonomically query variant types
  // "split" is a special type of bound where the cut is applied in both directions,
  //   then the significances in both regions are added in quadrature
  // "normal" is every other type of bound
  using RangeUser = std::tuple<double, double, int>;
  using Range = TQGridScanBound::Range;

  TQGridScanObservable() = default;
  // Observables hold vector of axes for cases where more than one region or variation is to be optimized!
  // This is needed to execute TQGridScanObservable::setAxesRange for all regions/variations!
  // Since the feature space must be consistent within the regions only the first entry
  // for the axes is considered for most of the range and bound settings
  TQGridScanObservable(vector<TString> names, vector<TAxis*> axes) :
    m_names(names),
    m_axes(axes)
  {}

  const TString& name() const { return m_names.at(0); }
  TAxis* axis() { return m_axes.at(0); } // only used with cl evaluator
  vector<TString> names() const { return m_names; }
  vector<TAxis*> axes() { return m_axes; } // only used with cl evaluator

  // Set the ranges for both all axes for all regions/variations
  void setAxesRange(int first, int last);

  boost::optional<boost::variant<TQGridScanBounds, TQGridScanBound>>* bounds() {
    return &m_bounds;
  }
  const boost::optional<boost::variant<TQGridScanBounds, TQGridScanBound>>& bounds() const {
    return m_bounds;
  }

  TQGridScanBounds* normalBounds();
  TQGridScanBound* splitBounds();

  void setRangeCutLower(RangeUser boundRangeUser);
  void setRangeCutUpper(RangeUser boundRangeUser);
  void setFixedCutLower(double cutVal);
  void setFixedCutUpper(double cutVal);
  void setSwitchCutLower(double cutVal);
  void setSwitchCutUpper(double cutVal);
  void setRangeCutSplit(RangeUser boundRangeUser);
  void setFixedCutSplit(double cutVal);

  void setRangeCutLower(double lower, double upper, int nSteps);
  void setRangeCutUpper(double lower, double upper, int nSteps);
  void setRangeCutSplit(double lower, double upper, int nSteps);

private:
  vector<TString> m_names; 
  vector<TAxis*> m_axes; 
  boost::optional<boost::variant<TQGridScanBounds, TQGridScanBound>> m_bounds;
  
public:
  ClassDefOverride(TQGridScanObservable, 1)
};

class TQHistParams {
public:
  TQHistParams() = default;
  TQHistParams(vector<TAxis*> axes) 
  {
    for (unsigned int i=0; i < axes.size(); i++) {
      if (i==0) {
        m_hists.push_back( TH1F(
                                axes[i]->GetName(),
                                axes[i]->GetTitle(),
                                axes[i]->GetNbins(),
                                axes[i]->GetXmin(),
                                axes[i]->GetXmax()
                                ) );
      } else { // to avoid memory leak (separated from above to keep GetName() in first axes entry)
        m_hists.push_back( TH1F(
                                axes[i]->GetName()+TString::Format("%d", i),
                                axes[i]->GetTitle(),
                                axes[i]->GetNbins(),
                                axes[i]->GetXmin(),
                                axes[i]->GetXmax()
                                ) );
      }
    }
  }
  TString name() const { return m_hists[0].GetName(); }
  TString title() const { return m_hists[0].GetTitle(); }
  int nBins() const { return m_hists[0].GetXaxis()->GetNbins(); }
  double min() const { return m_hists[0].GetXaxis()->GetXmin(); }
  double max() const { return m_hists[0].GetXaxis()->GetXmax(); }

  const TH1F& hist() const { return m_hists[0]; }
  vector<TH1F> hists() const { return m_hists; }
  const TAxis* axis() const { return m_hists[0].GetXaxis(); }

protected:
  vector<TH1F> m_hists;
};

// These hold the relevant information to plot grid scan results, used after reading grid scan results
// They can be serialised with ROOT
// It stores separate properties for the view range vs. the actual binning; as far as I know there's no
// ergonomic way to apply cuts to the ndim histograms at the outset, so we never bother to remove
// bins that will never be used.
class TQGridScanNormalObservable : public TObject, public TQHistParams {
public:

  TQGridScanNormalObservable(
      vector<TAxis*>  axes,
      TQGridScanBounds bounds
  ) :
    TQHistParams(axes),
    m_bounds(bounds)
  {}

  TQGridScanNormalObservable() = default;

  TQGridScanBounds* bounds() {
    return &m_bounds;
  }

  const TQGridScanBounds& bounds() const {
    return m_bounds;
  }

private:
  TQGridScanBounds m_bounds;

public:
  ClassDefOverride(TQGridScanNormalObservable, 1)
};

class TQGridScanSplitObservable : public TObject, public TQHistParams {
public:
  TQGridScanSplitObservable(
      vector<TAxis*>  axes,
      TQGridScanBound bound
  ) :
    TQHistParams(axes),
    m_bound(bound)
  {}

  TQGridScanSplitObservable() = default;

  TQGridScanBound* bound() {
    return &m_bound;
  }

  const TQGridScanBound& bound() const {
    return m_bound;
  }

private:
  TQGridScanBound m_bound;

public:
  ClassDefOverride(TQGridScanSplitObservable, 1)
};
#endif
 TQGridScanObservable.h:1
 TQGridScanObservable.h:2
 TQGridScanObservable.h:3
 TQGridScanObservable.h:4
 TQGridScanObservable.h:5
 TQGridScanObservable.h:6
 TQGridScanObservable.h:7
 TQGridScanObservable.h:8
 TQGridScanObservable.h:9
 TQGridScanObservable.h:10
 TQGridScanObservable.h:11
 TQGridScanObservable.h:12
 TQGridScanObservable.h:13
 TQGridScanObservable.h:14
 TQGridScanObservable.h:15
 TQGridScanObservable.h:16
 TQGridScanObservable.h:17
 TQGridScanObservable.h:18
 TQGridScanObservable.h:19
 TQGridScanObservable.h:20
 TQGridScanObservable.h:21
 TQGridScanObservable.h:22
 TQGridScanObservable.h:23
 TQGridScanObservable.h:24
 TQGridScanObservable.h:25
 TQGridScanObservable.h:26
 TQGridScanObservable.h:27
 TQGridScanObservable.h:28
 TQGridScanObservable.h:29
 TQGridScanObservable.h:30
 TQGridScanObservable.h:31
 TQGridScanObservable.h:32
 TQGridScanObservable.h:33
 TQGridScanObservable.h:34
 TQGridScanObservable.h:35
 TQGridScanObservable.h:36
 TQGridScanObservable.h:37
 TQGridScanObservable.h:38
 TQGridScanObservable.h:39
 TQGridScanObservable.h:40
 TQGridScanObservable.h:41
 TQGridScanObservable.h:42
 TQGridScanObservable.h:43
 TQGridScanObservable.h:44
 TQGridScanObservable.h:45
 TQGridScanObservable.h:46
 TQGridScanObservable.h:47
 TQGridScanObservable.h:48
 TQGridScanObservable.h:49
 TQGridScanObservable.h:50
 TQGridScanObservable.h:51
 TQGridScanObservable.h:52
 TQGridScanObservable.h:53
 TQGridScanObservable.h:54
 TQGridScanObservable.h:55
 TQGridScanObservable.h:56
 TQGridScanObservable.h:57
 TQGridScanObservable.h:58
 TQGridScanObservable.h:59
 TQGridScanObservable.h:60
 TQGridScanObservable.h:61
 TQGridScanObservable.h:62
 TQGridScanObservable.h:63
 TQGridScanObservable.h:64
 TQGridScanObservable.h:65
 TQGridScanObservable.h:66
 TQGridScanObservable.h:67
 TQGridScanObservable.h:68
 TQGridScanObservable.h:69
 TQGridScanObservable.h:70
 TQGridScanObservable.h:71
 TQGridScanObservable.h:72
 TQGridScanObservable.h:73
 TQGridScanObservable.h:74
 TQGridScanObservable.h:75
 TQGridScanObservable.h:76
 TQGridScanObservable.h:77
 TQGridScanObservable.h:78
 TQGridScanObservable.h:79
 TQGridScanObservable.h:80
 TQGridScanObservable.h:81
 TQGridScanObservable.h:82
 TQGridScanObservable.h:83
 TQGridScanObservable.h:84
 TQGridScanObservable.h:85
 TQGridScanObservable.h:86
 TQGridScanObservable.h:87
 TQGridScanObservable.h:88
 TQGridScanObservable.h:89
 TQGridScanObservable.h:90
 TQGridScanObservable.h:91
 TQGridScanObservable.h:92
 TQGridScanObservable.h:93
 TQGridScanObservable.h:94
 TQGridScanObservable.h:95
 TQGridScanObservable.h:96
 TQGridScanObservable.h:97
 TQGridScanObservable.h:98
 TQGridScanObservable.h:99
 TQGridScanObservable.h:100
 TQGridScanObservable.h:101
 TQGridScanObservable.h:102
 TQGridScanObservable.h:103
 TQGridScanObservable.h:104
 TQGridScanObservable.h:105
 TQGridScanObservable.h:106
 TQGridScanObservable.h:107
 TQGridScanObservable.h:108
 TQGridScanObservable.h:109
 TQGridScanObservable.h:110
 TQGridScanObservable.h:111
 TQGridScanObservable.h:112
 TQGridScanObservable.h:113
 TQGridScanObservable.h:114
 TQGridScanObservable.h:115
 TQGridScanObservable.h:116
 TQGridScanObservable.h:117
 TQGridScanObservable.h:118
 TQGridScanObservable.h:119
 TQGridScanObservable.h:120
 TQGridScanObservable.h:121
 TQGridScanObservable.h:122
 TQGridScanObservable.h:123
 TQGridScanObservable.h:124
 TQGridScanObservable.h:125
 TQGridScanObservable.h:126
 TQGridScanObservable.h:127
 TQGridScanObservable.h:128
 TQGridScanObservable.h:129
 TQGridScanObservable.h:130
 TQGridScanObservable.h:131
 TQGridScanObservable.h:132
 TQGridScanObservable.h:133
 TQGridScanObservable.h:134
 TQGridScanObservable.h:135
 TQGridScanObservable.h:136
 TQGridScanObservable.h:137
 TQGridScanObservable.h:138
 TQGridScanObservable.h:139
 TQGridScanObservable.h:140
 TQGridScanObservable.h:141
 TQGridScanObservable.h:142
 TQGridScanObservable.h:143
 TQGridScanObservable.h:144
 TQGridScanObservable.h:145
 TQGridScanObservable.h:146
 TQGridScanObservable.h:147
 TQGridScanObservable.h:148
 TQGridScanObservable.h:149
 TQGridScanObservable.h:150
 TQGridScanObservable.h:151
 TQGridScanObservable.h:152
 TQGridScanObservable.h:153
 TQGridScanObservable.h:154
 TQGridScanObservable.h:155
 TQGridScanObservable.h:156
 TQGridScanObservable.h:157
 TQGridScanObservable.h:158
 TQGridScanObservable.h:159
 TQGridScanObservable.h:160
 TQGridScanObservable.h:161
 TQGridScanObservable.h:162
 TQGridScanObservable.h:163
 TQGridScanObservable.h:164
 TQGridScanObservable.h:165
 TQGridScanObservable.h:166
 TQGridScanObservable.h:167
 TQGridScanObservable.h:168
 TQGridScanObservable.h:169
 TQGridScanObservable.h:170
 TQGridScanObservable.h:171
 TQGridScanObservable.h:172
 TQGridScanObservable.h:173
 TQGridScanObservable.h:174
 TQGridScanObservable.h:175
 TQGridScanObservable.h:176
 TQGridScanObservable.h:177
 TQGridScanObservable.h:178
 TQGridScanObservable.h:179
 TQGridScanObservable.h:180