#pragma once

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*                  This file is part of the program and library             */
/*         SCIP --- Solving Constraint Integer Programs                      */
/*                                                                           */
/*    Copyright (C) 2002-2016 Konrad-Zuse-Zentrum                            */
/*                            fuer Informationstechnik Berlin                */
/*                                                                           */
/*  SCIP is distributed under the terms of the ZIB Academic License.         */
/*                                                                           */
/*  You should have received a copy of the ZIB Academic License.             */
/*  along with SCIP; see the file COPYING. If not email to scip@zib.de.      */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "objscip/objscip.h"
#include "scip/pub_var.h"
#include "Customer.h"
#include "Variable.h"
#include "Label.h"
#include "BranchingConstraints.h"


#include <iostream>
#include <vector>
#include <list>
#include <ctime>           // contains clock()
#include <iomanip>

using namespace std;
using namespace scip;



/** pricer class */
class Pricer : public ObjPricer
{
public:

	/** Constructs the pricer object with the data needed */
	Pricer(
		SCIP* scip,								/**< SCIP pointer */
		const char* _name,								/**< name of pricer */
		int										_numNodes,							/**< number of nodes */
		double									_minSpeed,							/**< minmial speed */
		double									_maxSpeed,							/**< maximal speed */
		vector<Customer>& _customerList,						/**< set of all customer*/
		double									_timeHorizon,						/**< time horizon */
		vector<vector<bool>>& _edgeSet,							/**< edges of the graph */
		vector< vector<bool>>& _minSpeedMatrix,					/**< matrix, where enty m_{i,j} is 1 if job j can suceed i at lowest speed and completing at lower tw */
		vector<SCIP_CONS*>& _customerConstraints,				/**< vector of customer constraints */
		SCIP_CONS* _robotConstraint,					/**< maximal number of machines */
		//		vector<Variable>& _variables,							/**< added variables during solution process */
		double									_eps,								/**< computational accuracy */
		int										_addedVarsInAnIteration,			/**< maximal number of variables that are added in each iteration */
		vector<vector<ArcBranchingConstraints>>& _upperArcBranchingConstraints,	/**< includes all upper arc branching constraints of the b and b tree */
		vector<vector<ArcBranchingConstraints>>& _lowerArcBranchingConstarints,	/**< includes all lower arc branching constraints of the b and b tree */
		vector<BranchingConstraints>& _branchingConstraintsAtANode,
		int _edgeLimit,
		double _edgesToKeep,
		int _vehicleCapacity,
		long _startTimeOptimization,
		double _timeLimit,
		vector<vector<bool>>& _orgEdgeSet,
		vector<vector<int>>& _outNeighbors
	);
	/** Destructs the pricer object. */
	virtual ~Pricer();

	/** initialization method of variable pricer (called after problem was transformed) */
	virtual SCIP_DECL_PRICERINIT(scip_init);

	/** reduced cost pricing method of variable pricer for feasible LPs */
	virtual SCIP_DECL_PRICERREDCOST(scip_redcost);

	/** farkas pricing method of variable pricer for infeasible LPs */
	virtual SCIP_DECL_PRICERFARKAS(scip_farkas);

	/** perform pricing */
	SCIP_RETCODE pricing(
		SCIP* scip,               /**< SCIP data structure */
		bool               isfarkas            /**< whether we perform Farkas pricing */
	);

	/** add tour variable to problem */
	SCIP_RETCODE addVariable(
		SCIP* scip,				/**< SCIP data structure	*/
		Label* label,				/**< Label of with lowest cost */
		BranchingConstraints* branchingConsAtNode
	);
	// Method calculates the objective value


	/** returns negative reduced cost tour (uses restricted shortest path dynamic programming algorithm) */
	void forwardLabeling(
		vector<SCIP_Real>& redCost,
		SCIP_Real dualValR,
		vector<vector<Label>>* table,
		vector<vector<bool>>& edges,
		BranchingConstraints* branchingCons,
		bool isfarkas,
		vector<Label>& kVariables,
		bool rootNode,
		bool heuristicPricing);

	/** add variable to list*/
	void addVariable(Variable var)
	{
		variables.push_back(var);
	}
	bool checkMemoryUsage(double limitPercent);

	// returns list of all contained labels
	inline vector<Variable>* getVariables()
	{
		return &variables;
	}

	inline vector<BranchingConstraints>* getBranchingConstraintsAtANode()
	{
		return &branchingConstraintsAtANode;
	}

	inline void addBranchingConstraintsAtANode(BranchingConstraints* bC)
	{
		branchingConstraintsAtANode.push_back(*bC);
	}

	/** return number of nodes */
	inline int getNumNodes() const
	{
		return numNodes;
	}

	/** return node constraint */
	inline SCIP_CONS* getCustomerCon(int node)
	{
		return customerConstraints[node];
	}

	inline vector<vector<ArcBranchingConstraints>>* getUpperArcBranchingCons()
	{
		return &upperArcBranchingConstraints;
	}

	inline vector<vector<ArcBranchingConstraints>>* getLowerArcBranchingCons()
	{
		return &lowerArcBranchingConstarints;
	}

	inline void setUpperArcBranchingCon(ArcBranchingConstraints con)
	{
		assert(upperArcBranchingConstraints[con.getIdStartCustomer()][con.getIdEndCustomer()].getIdStartCustomer() == -1);
		upperArcBranchingConstraints[con.getIdStartCustomer()][con.getIdEndCustomer()] = con;
	}

	inline void setLowerArcBranchingCon(ArcBranchingConstraints con)
	{
		assert(lowerArcBranchingConstarints[con.getIdStartCustomer()][con.getIdEndCustomer()].getIdStartCustomer() == -1);
		lowerArcBranchingConstarints[con.getIdStartCustomer()][con.getIdEndCustomer()] = con;
	}
	/** return if edge exists*/
	inline bool edgeContained(int i, int j)
	{
		return edgeSet[i][j];
	}
protected:

	/** return a copy of a job*/
	inline Customer getCopyOfCustomer(int i) {
		return customerList[i];
	}


	// return customer j of complete customer list
	inline Customer* getCustomer(int j)
	{
		return &customerList[j];
	}

	// return customer j of complete customer list
	inline vector<Customer>* getCustomerList()
	{
		return &customerList;
	}

	/**  return customer j of forward label*/


	/** return time horizon*/
	inline const double getTimeHorizon() const
	{
		return timeHorizon;
	}
	/** return service time */
	inline int getServiceTime(int j)
	{
		return customerList[j].getServiceTime();
	}

	/** return robot constraint */
	inline SCIP_CONS* getRobotCon()
	{
		return robotConstraint;
	}
	/** return if edge exists*/
	inline vector<vector<bool>>* getEdgeSet()
	{
		return &edgeSet;
	}

	inline double getEps()
	{
		return eps;
	}

	inline double getMinSpeed()
	{
		return minSpeed;
	}

	inline double getMaxSpeed()
	{
		return maxSpeed;
	}

	inline bool getMinSpeedPossible(int i, int j) {
		return minSpeedMatrix[i][j];
	}

	inline vector<vector<bool>>* getMinSpeedMatrix()
	{
		return &minSpeedMatrix;
	}

	inline int getVarsToBeAddedInAnIteration()
	{
		return addedVarsInAnIteration;
	}
	inline int getEdgeLimit()
	{
		return edgeLimit;
	}
	inline double getEdgesToKeep()
	{
		return edgesToKeep;
	}
	inline int getVehicleCapacity()
	{
		return vehicleCapacity;
	}
	inline bool timeLimitReached()
	{
		if ((clock() - startTimeOptimization) / 1000 > timeLimit)
		{
			return true;
		}
		else
		{
			return false;
		}

	}

	inline vector<vector<bool>>& getOrgEdgeSet()
	{
		return orgEdgeSet;
	}


	const char* name;							/**< name of pricer */
	const int								numNodes;						/**< number of nodes */
	const double							minSpeed;						/**< minmial speed */
	const double							maxSpeed;						/**< maximal speed */
	vector<Customer>						customerList;					/**< set of all customer*/
	const double							timeHorizon;					/**< time horizon */
	vector<vector<bool>>					edgeSet;						/**< matrix of edges */
	vector<vector<bool>>					minSpeedMatrix;					/**< matrix, where entry m_{i,j} is 1 if customer j can succeed customer i at lowest speed and completing at lower tw */
	vector<SCIP_CONS*>						customerConstraints;			/**< vector of node constraints */
	SCIP_CONS* robotConstraint;				/**< maximal number of machines */
	vector<Variable>						variables;						/**< added variables during solution process */
	double									eps;							/**< computational accuracy */
	int										addedVarsInAnIteration;			/** < number of variables that should be added in each iteration */
	vector<BranchingConstraints>& branchingConstraintsAtANode;
	vector<vector<ArcBranchingConstraints>>& upperArcBranchingConstraints;	/**< includes all upper arc branching constraints of the b and b tree */
	vector<vector<ArcBranchingConstraints>>& lowerArcBranchingConstarints;	/**< includes all lower arc branching constraints of the b and b tree */
	int	edgeLimit;
	double edgesToKeep;
	int vehicleCapacity;
	long startTimeOptimization;
	double timeLimit;
	vector<vector<bool>> orgEdgeSet;
	bool y_dominanceRule;
	vector<vector<int>> outNeighbors;
	int maskWords;
	std::vector<std::vector<uint64_t>> orgEdgeMask;
	int pricerCounter;
};

