/**
 * @file parameters.hh
 * @author Christoph Langenbruch, Renata Kopecna
 * @date 2009-03-18
 *
 */
#ifndef PARAMETERS_H
#define PARAMETERS_H

#include <string>
#include <vector>

namespace fcnc {

class parameters;

/// The parameter class stores the different attributes of a parameter used in the analysis, e.g. the name, start value and the parameter range.
class parameter {
    friend class parameters;
    friend class feldman_cousins;
private:
    ///blind this parameter
    bool blind;
    ///blinding string
    std::string blinding_string;
    ///blinding delta, never look at this
    double blinding_delta;
    ///blinding range
    double blinding_scale;
    ///This name is supplied as the parameter name to minuit. Please give every parameter a different name.
    std::string name;
    ///The parameter description is used by the plotter for plot captions and axis titles.
    std::string description;
    ///The start value of the parameter. After every step in the toy study, the parameter is reset to this value.
    double start_value;
    ///The current value of the parameter
    double value;
    ///The minimum possible value for the parameter
    double min;
    ///The maximum possible value for the parameter
    double max;
    ///The initial step size used by minuit
    double step_size;
    ///The error of the fitted result
    double error;
    ///asymmetric errors if a minos error analysis has been performed
    double error_up;
    ///asymmetric errors if a minos error analysis has been performed
    double error_down;
    ///the index of this parameter in the params array of the parameter set
    int index;
    ///should the parameter float without limits?
    bool unlimited;
    ///this has been the error of a previous measurement
    double previous_error_low;
    double previous_error_high;
    ///previous measurement, this can be different from the start value, e.g. when running a toy study
    double previous_measurement;
    ///do we want to vary the parameter so that it is compatible with an earlier measurement
    bool gaussian_constraint;
    bool do_minos;
    ///pointer to parent parameterset this parameter was added to!
    parameters* parent;
public:
    ///
    void set_parent(parameters* p) {parent = p;};
    //correlations
    std::vector<double> correlations;
    ///public set routines
    void set_name(std::string n) {name = n;};
    void set_min(double m) {min = m;};
    void set_max(double m) {max = m;};
    void set_step_size(double s) {step_size = s;};
    void set_value(double v) {value = v;};
    void set_start_value(double v) {start_value = v;};
    //void set_true_value(double v) {true_value = v;};
    void set_description(std::string d) {description = d;};
    void set_error(double e) {error = e;};
    void set_error_down(double e) {error_down = e;};
    void set_error_up(double e) {error_up = e;};
    void set_unlimited(bool u) {unlimited = u;};
    void set_index(int idx) {index = idx;};
    void set_previous_measurement(double m) {previous_measurement = m;};
    void set_previous_error_low(double e) {previous_error_low = e;};
    void set_previous_error_high(double e) {previous_error_high = e;};
    void set_values_n_errors(double v, double e);
    void reset_correlations();
    void set_correlation(double corr, unsigned int idx);
    void set_correlations(std::vector<double> corrs, unsigned int first, unsigned int last, double scalefactor = 1.0);

    ///set blinding string
    void set_blinding_string(std::string s);
    void set_minos(bool minos) { do_minos = minos; };
    bool get_minos() { return do_minos; };
    void set_blinding(bool b, double b_scale, bool is_angle=false, std::string b_string="DefaultBlinder");

    ///set all values which make sense on initialisation
    void init(double v, double min, double max, double stepsize)
    {
        set_value(v);
        start_value = v;
        set_min(min);
        set_max(max);
        set_step_size(stepsize);
        set_error(0.0);
        set_error_up(0.0);
        set_error_down(0.0);
        set_unlimited(false);
        set_minos(false);
        gaussian_constraint = false;
        previous_measurement = v;
        correlations.clear();
    };

    void init_fixed(double v){
        init(v, v-0.01, v+0.01, 0.0);
        //This eaquals to the previous init with limits of v-0.01, v+0.01
        //Not sure if there is a < or <= further in the code, so let's play it safe
    }
    void init(double v, double min, double max, double stepsize, double previous_measurement_error, bool constrain)
    {
        init(v, min, max, stepsize);
        previous_error_low = previous_measurement_error;
        previous_error_high = previous_measurement_error;
        gaussian_constraint = constrain;
    }
    void init(double v, double min, double max, double stepsize, double previous_measurement_error_low, double previous_measurement_error_high, bool constrain)
    {
        init(v, min, max, stepsize);
        previous_error_low = previous_measurement_error_low;
        previous_error_high = previous_measurement_error_high;
        gaussian_constraint = constrain;
    }
    void init_unlimited( double v, double min, double max, double stepsize, bool nolimits)
    {
        init(v, min, max, stepsize);
        unlimited = nolimits;
    };

    ///public get routines
    std::string get_name() const {return name;};
    const char* get_mn_name() const {return get_name().c_str();};
    double get_min() const {return min;};
    double get_max() const {return max;};
    double get_blinding_delta() const {return blinding_delta;};
    double get_step_size() const {return step_size;};
    double get_value() const {return value;};
    double get_start_value() const {return start_value;};
    //double get_true_value() const {return true_value;};
    double get_previous_error() const {return 0.5*(previous_error_low+previous_error_high);};
    double get_previous_error_low() const {return previous_error_low;};
    double get_previous_error_high() const {return previous_error_high;};
    double get_previous_measurement() const {return previous_measurement;};
    std::string get_description() const {return description;};
    bool get_unlimited() const {return unlimited;};
    bool get_gaussian_constraint() const {return gaussian_constraint;};

    std::string get_root_name() const;
    ///public set routines
    void set_constant() {step_size = 0.0;};
    double operator() () const {return value;};
    double get_error() const {return error;};
    double get_error_up() const {return error_up;};
    double get_error_down() const {return error_down;};
    int get_index() const {return index;};
    void reset_start() {value = start_value; error = 0.0;};
    ///default constructor
    parameter(std::string n, std::string d):
        blind(false),
        blinding_string("DefaultBlinder"),
        blinding_delta(0.0),
        name(n),
        description(d),
        start_value(0.0),
        value(0.0),
        min(-1.0),
        max(+1.0),
        step_size(0.001),
        error(0.0),
        error_up(0.0),
        error_down(0.0),
        index(-1),
        unlimited(false),
        previous_error_low(0.0),
        previous_error_high(0.0),
        previous_measurement(0.0),
        gaussian_constraint(false)
    {};
    ///normal constructor
    parameter(std::string n, std::string d, double v,
              double minimum, double maximum, double stepsize = 0.0, bool nolimits=false):
        blind(false),
        blinding_string("DefaultBlinder"),
        blinding_delta(0.0),
        name(n),
        description(d),
        start_value(v),
        //    true_value(v),
        value(v),
        min(minimum),
        max(maximum),
        step_size(stepsize),
        error(0.0),
        error_up(0.0),
        error_down(0.0),
        index(-1),
        unlimited(nolimits),
        gaussian_constraint(false)
    {};
    bool is_blind() {
        return blind;
    };
};

///The parameters class stores a collection of parameters used in the analysis. Every analysis should implement its own set e.g. bs2jpsiphi_parameters 
class parameters
{
private:
    ///this vector holds the different parameters
    std::vector<parameter*> params;
protected:
    ///Add a certain parameter to the params vector.
    void add_parameter(parameter* param);
public:
    bool is_blind() const;
    virtual ~parameters() {};
    ///Method gives back he number of parameters
    unsigned int nparameters() const;
    ///Methos returns a pointer to parameter with index i
    parameter* get_parameter(unsigned int i) const;
    ///returns parameter with specific name
    parameter* get_parameter(std::string name) const;
    ///Resets all parameters to their start values
    void reset_parameters();
    ///Method that prints the parameter set, the current values, errors and the deviation from the start value in sigma
    void print_parameters(bool latex_output=true) const;
    ///fix all parameters
    void fix_parameters();
    ///set all parameters to current values! Careful with this, please.
    void take_current_as_start();
};

}

#endif
