#pragma once
#include "OptimizationParams.h"

//USEFUL CONSTANTS:

const double EPS_ZERO = pow(10, -9);
class EquationCalculator;

//USEFUL ENUMERATIONS
enum sol_corner {
	std_cor = 0,
	cor_A = 1,
	cor_h2 = 2,
	cor_h1 = 3,
	cor_A_h2 = 4,
	cor_A_h1 = 5,
	cor_h1_h2 = 6,
	cor_A_h1_h2 = 7,
	error_cor = 99
};

#pragma region Type Dafinitions 

typedef struct loop_state {
	int j;
	int k;
	int l;
	int m;
	int norm_switch;
	printLevel level;
} LoopState;

typedef struct econ_params {
	bool isSet = false;

	double eta_cp;
	double r;
	double beta;
	double amin;
	double tau_b;

	double mu_0;
	double mu_l1;
	double mu_l2;
	double mu_t1;
	double mu_t2;

	double mu_0tag;

	double psi_l1;
	double psi_l2;
	double rhou_l;

	double psi_t1;
	double psi_t2;
	double rhou_t;

	double mu_l1_tild;
	double mu_l2_tild;
	double mu_t1_tild;
	double mu_t2_tild;

	double chi;
	double taumu;
	double tau_gamma;
	int L_bar;
	int kttp1;

	double w1;
	double w2;
	double Aprev;

	PartwiseLinFunc * EUfunc;
	PartwiseLinFunc * EVfunc;
} EconParams;

typedef struct double_pair
{
	EquationCalculator * eqCalc;
	double x0;
	double x1;
	int status;
} DoublePair;

typedef struct double_trio
{
	EquationCalculator * eqCalc;
	double x0;
	double x1;
	double x2;
	int status;
} DoubleTrio;

typedef struct double_single
{
	EquationCalculator * eqCalc;
	double x0;
	int status;
} DoubleSingle;

typedef struct solution_set
{
	sol_corner CorIndex = sol_corner::std_cor;

	double LAM1 = 0;
	double LAM2 = 0;

	double L1 = 0;
	double T1 = 0;
	double L2 = 0;
	double T2 = 0;

	double H1 = 0;
	double H2 = 0;

	double Y = 0;
	double atY = 0;
	double mY = 0;

	double A = 0;
	double C = 0;

	double V = 0;
	double F = 0;
	double U = 0;

	double MUL;

	double EU = 0;
	double EV = 0;

	int status;
} SolutionSet;

typedef DoublePair(*FocDoubleOp)(DoublePair);
typedef DoubleTrio(*FocTrioOp)(DoubleTrio);
typedef DoubleSingle(*FocSingleOp)(DoubleSingle);

#pragma endregion

class EquationCalculator
{
private:
	void SetEconParams(OptimizationParams * oParams);

public:
	LoopState state;
	EconParams econParams;
	EquationCalculator();
	~EquationCalculator();

	void SetNormSwitch(int norm_switch);
	void SetLoopState(OptimizationParams * oParams, int j, int k, int l, int m);

	static double foc_int_terminal_nk(DoubleSingle LT1);
	static DoublePair foc_int(DoublePair LT);

	static DoublePair foc_int_std(DoublePair LT1);
	static DoublePair foc_cor_h1(DoublePair L2LAM1);
	static DoublePair foc_cor_h2(DoublePair L1LAM2);
	static DoubleTrio foc_cor_h1_h2(DoubleTrio L1L2A);
	static DoublePair foc_cor_A(DoublePair LT);
	static DoublePair foc_cor_A_h1(DoublePair L2LAM1);
	static DoublePair foc_cor_A_h2(DoublePair L1LAM2);
	static DoublePair foc_cor_A_h1_h2(DoublePair L1L2);

	static DoubleSingle foc_int_std_nk(DoubleSingle LT1);
	static DoubleSingle foc_cor_h1_nk(DoubleSingle L2LAM1);
	static DoubleSingle foc_cor_h2_nk(DoubleSingle L1LAM2);
	static DoubleSingle foc_cor_A_nk(DoubleSingle LT);
	static DoubleSingle foc_cor_A_h2_nk(DoubleSingle L1LAM2);
	static DoubleSingle foc_cor_A_h1_nk(DoubleSingle L2LAM1);
	static DoubleSingle foc_cor_h1_h2_nk(DoubleSingle L1L2A);

	int GetKttp();

	double calc_w1(OptimizationParams * oParams);
	double calc_w2(OptimizationParams * oParams);
	double calc_L2(double L1);
	double calc_T2(double T1);
	DoublePair calc_A(double L1, double T1, double L2, double T2);
	double calc_V(double L1, double T1, double L2, double T2, double Csol, double Asol);
	double calc_U_from_C(double C);

	SolutionSet CalcFullSolutionSetStd(DoublePair LT1);
	SolutionSet CalcFullSolutionSetA(DoublePair LT1);
	SolutionSet CalcFullSolutionSetH2(DoublePair L1LAM2);
	SolutionSet CalcFullSolutionSetH1(DoublePair L2LAM1);
	SolutionSet CalcFullSolutionSetA_H2(DoublePair L1LAM2);
	SolutionSet CalcFullSolutionSetA_H1(DoublePair L2LAM1);
	SolutionSet CalcFullSolutionSetH1_H2(DoubleTrio L1L2A);
	SolutionSet CalcFullSolutionSetA_H1_H2(DoublePair L1L2);
	SolutionSet CalcFullSolutionSetStd_nk(DoubleSingle LT1);
	SolutionSet CalcFullSolutionSetA_nk(DoubleSingle LT1);
	SolutionSet CalcFullSolutionSetH2_nk(DoubleSingle L1LAM2);
	SolutionSet CalcFullSolutionSetH1_nk(DoubleSingle L2LAM1);
	SolutionSet CalcFullSolutionSetA_H2_nk(DoubleSingle L1LAM2);
	SolutionSet CalcFullSolutionSetA_H1_nk(DoubleSingle L2LAM1);
	SolutionSet CalcFullSolutionSetH1_H2_nk(DoubleSingle L1L2A);
	SolutionSet CalcFullSolutionSetA_H1_H2_nk();

};
