#include "stdafx.h"


/* standard library includes */
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <list>
#include <sstream>
#include <chrono>
#include <iomanip>

/* scip includes */
#include "objscip/objscip.h"
#include "objscip/objscipdefplugins.h"

/* user defined includes */
#include "Variable.h"
#include "scip/cons_setppc.h"
#include "Branching.h"
#include "Reader.h"
#include "QuadRootsRevJ.h"
#include "TimeFile.h"
#include <stdlib.h> 
#include <ctime>           // contains clock()

#pragma warning(disable : 4996) //_CRT_SECURE_NO_WARNINGS

/* namespace usage */
using namespace std;
using namespace scip;

bool fileExists(const std::string& path) {
	std::ifstream in(path);
	return in.good();
}
 
void edges(vector<vector<bool>>& edgeSet, vector<Customer>* jobList, int numNodes, double maxSpeed, double eps) {

	for (int i = 0; i < numNodes; i++)
	{
		for (int j = 0; j < numNodes; j++)
		{
			if ((((*jobList)[i].getLowerTW() + (*jobList)[i].getServiceTime() + (*jobList)[i].getCDist(j) / maxSpeed) - eps >
				(*jobList)[j].getUpperTW()) && i != j)
			{
				edgeSet[i][j] = false;
			}
			else
			{
				edgeSet[i][j] = true;
			}
		}
	}

}

void minSpeedOrder(vector<Customer>* jobList, vector<vector<bool>>* matrixSpeedOrder,
	double minSpeed, int numNodes, double eps)
{

	(*matrixSpeedOrder).resize(numNodes);
	for (int i = 0; i < (*jobList).size(); i++)
	{
		(*matrixSpeedOrder)[i].resize(numNodes, 0);
		for (int j = 0; j < jobList->size(); j++)
		{
			if (i != j)
			{
				if ((*jobList)[i].getUpperTW() + (*jobList)[i].getServiceTime() + ((*jobList)[i].getCDist(j) / minSpeed) - eps <= ((*jobList)[j].getLowerTW()))
				{
					(*matrixSpeedOrder)[i][j] = true;
				}
				else
				{
					(*matrixSpeedOrder)[i][j] = false;
				}
			}
			else
			{
				if ((*jobList)[j].getDistFromDepot() / minSpeed - eps <= (*jobList)[j].getLowerTW())
				{
					(*matrixSpeedOrder)[i][j] = true;

				}
				else
				{
					(*matrixSpeedOrder)[i][j] = false;
				}
			}
		}
	}
}
void rounding(double eps, double* value)
{
	long long a = (long long)((*value) / eps);
	*value = a * eps;
}
double maximum(double a, double b) {
	if (a < b) {
		return b;
	}
	else
		return a;
}

SCIP_RETCODE mainProgram(
	vector<Customer>& customerList,
	vector<vector<int>> distMatrix,
	vector<int> data,
	int numCustomer,
	int numRobot,
	int numOfVarsToBeAdded,
	TimeFile timeFile,
	int edgeLimit,
	double edgesToKeep,
	string fileName,
	double timeLimit,
	bool withOutput,
	const string instance
)
{

	// Get current time
	auto now = std::chrono::system_clock::now();
	std::time_t now_time = std::chrono::system_clock::to_time_t(now);
	// Convert to local time
	std::tm local_tm = *std::localtime(&now_time);
	// Format into string
	std::ostringstream oss;
	oss << std::put_time(&local_tm, "%Y-%m-%d %H-%M");
	std::string datetime = oss.str();

	vector<string> times(3);
	bool solutionInfeas = true;

	int timeHorizon = 100000000;
	//timeHorizon = data[0];
	double minSpeed = 15.33035927;
	double minSpeedWage = 20.9711;

	//	minSpeed = data[2];
	//double maxSpeed = data[3];
	double maxSpeed = 25;
	double vehicleCapacity = data[3];
	double wage = 8 / 3600.0;

	int numRobotOriginal = numRobot;
	SCIP_Real   eps = 1e-5;


	times[0] = instance;

	ostringstream strsEdges;
	strsEdges << edgesToKeep;
	times[1] = strsEdges.str();

	ostringstream strsVars;
	strsVars << numOfVarsToBeAdded;
	times[2] = strsVars.str();



	// Contains all solutions	
	vector<double> solutions;

	/**********************
	* Setup problem data *
	**********************/

	static const char* PRICER_NAME = "Pollution-Routing-Prolbem-Pricer";

	const bool hOption = false;


	// Calculate edge set
	vector<vector<bool>> edgeSet(numCustomer, vector<bool>(numCustomer));
	edges(edgeSet, &customerList, numCustomer, maxSpeed, eps);

	// Calculate edge set
	vector<vector<bool>> orgEdgeSet(numCustomer, vector<bool>(numCustomer));
	edges(orgEdgeSet, &customerList, numCustomer, maxSpeed, eps);

	// after building edges:
	vector<vector<int>> outNeighbors(numCustomer);
	for (int i = 0; i < numCustomer; ++i) {
		for (int j = 0; j < numCustomer; ++j) {
			if (edgeSet[i][j]) 
				outNeighbors[i].push_back(j);
		}
	}

	// Calculate if job j can suceed i at lowest speed and completes at lower time window
	vector<vector<bool>> speedOrder;
	minSpeedOrder(&customerList, &speedOrder, minSpeed, numCustomer, eps);


	/**************
	* Setup SCIP *
	**************/
	SCIP* scip = NULL;
	// initialize SCIP environment 
	SCIP_CALL(SCIPcreate(&scip));

	if (withOutput) {
		ostringstream scipOutput;
		scipOutput << "scip_details" << "_C_" << numCustomer << "_V_" << numRobot << "_" << datetime << "_" << ".txt";
		SCIPsetMessagehdlrLogfile(scip, scipOutput.str().c_str());

	}
	else
	{
		SCIP_CALL(SCIPsetIntParam(scip, "display/verblevel", 0));
	}

	//	SCIPprintVersion(scip, NULL);
	SCIPinfoMessage(scip, NULL, "\n");

	// include default plugins 
	SCIP_CALL(SCIPincludeDefaultPlugins(scip));

	SCIP_CALL(SCIPsetRealParam(scip, "limits/time", timeLimit));

	// create empty problem 
	SCIP_CALL(SCIPcreateProb(scip, "ColGenRobot", 0, 0, 0, 0, 0, 0, 0));


	// add job inclusion constraints
	char con_name[255];
	vector<SCIP_CONS*> customerCons(numCustomer);
	for (int i = 0; i < numCustomer; ++i)
	{
		(void)SCIPsnprintf(con_name, 255, "S%d", i);
		// number of vars
		SCIP_CALL(SCIPcreateConsBasicSetpart(scip, &customerCons[i], con_name, 0, NULL));

		// declare constraint modifiable for adding variables during pricing 
		SCIP_CALL(SCIPsetConsModifiable(scip, customerCons[i], TRUE));
		SCIP_CALL(SCIPaddCons(scip, customerCons[i]));
	}

	// Create knapsack constraint for number of robots
	SCIP_CONS* robotCon;
	(void)SCIPsnprintf(con_name, 255, "R%d", 0);

	SCIP_CALL(SCIPcreateConsBasicLinear(scip, &robotCon, con_name, 0, 0, 0, numRobot, numRobot));
	SCIP_CALL(SCIPsetConsModifiable(scip, robotCon, TRUE));
	SCIP_CALL(SCIPaddCons(scip, robotCon));

	// vectors for branching constraints
	vector<vector<ArcBranchingConstraints>> upperArcBranchingConstraints(customerList.size(), vector<ArcBranchingConstraints>(customerList.size()));		/**< includes all upper arc branching constraints of the b and b tree */
	vector<vector<ArcBranchingConstraints>>	lowerArcBranchingConstarints(customerList.size(), vector<ArcBranchingConstraints>(customerList.size()));	/**< includes all lower arc branching constraints of the b and b tree */
	vector<BranchingConstraints>			branchingConstraintsAtANode;


	long startTimeOptimization = clock();


	// include VRP pricer
	Pricer* pricer = new Pricer(
		scip,								/**< SCIP pointer */
		PRICER_NAME,						/**< name of pricer */
		numCustomer,							/**< number of nodes */
		minSpeed,							/**< minmial speed */
		minSpeedWage,
		maxSpeed,							/**< maximal speed */
		customerList,						/**< set of all customer*/
		timeHorizon,						/**< time horizon */
		edgeSet,							/**< edges of the graph */
		speedOrder,					/**< matrix, where enty m_{i,j} is 1 if job j can suceed i at lowest speed and completing at lower tw */
		customerCons,				/**< vector of customer constraints */
		robotCon,							/**< maximal number of machines */
		eps,								/**< computational accuracy */
		numOfVarsToBeAdded,					/**< maximal number of variables that are added in each iteration */
		upperArcBranchingConstraints,		/**< includes all upper arc branching constraints of the b and b tree */
		lowerArcBranchingConstarints,	/**< includes all lower arc branching constraints of the b and b tree */
		branchingConstraintsAtANode,
		edgeLimit,
		edgesToKeep,
		vehicleCapacity,
		wage,
		startTimeOptimization,
		timeLimit,
		edgeSet,
		outNeighbors
	);

	Branching* bNA = new Branching(scip, pricer, eps);

	bNA->SCIPincludeBranchruleMyBranchRule(scip);

	SCIP_CALL(SCIPincludeObjPricer(scip, pricer, true));

	// activate pricer
	SCIP_CALL(SCIPactivatePricer(scip, SCIPfindPricer(scip, PRICER_NAME)));

	/*************
	*  Solve    *
	*************/

	SCIP_CALL(SCIPsolve(scip));

	startTimeOptimization = clock() - startTimeOptimization;

	ostringstream strsTime;
	strsTime << (double)startTimeOptimization;
	times.push_back(strsTime.str());
	
	string info = "";

	double obj = SCIPgetPrimalbound(scip);

	if (obj <= 1 && obj >= 999999)
	{
		times.push_back("infeas");
		info = "infeasible";
	}
	else
	{
		ostringstream strsObj;
		strsObj << obj;
		times.push_back(strsObj.str());
	}

	if (SCIPgetStatus(scip) == 5)
	{
		ostringstream strsGap;
		strsGap << (SCIPgetGap(scip) * 100);
		times.push_back(strsGap.str());
		info = "time out";
	}
	else
	{
		times.push_back("0.0");
	}
	SCIPprintBestSol(scip,NULL,0);


	ostringstream numRobotsStr;
	numRobotsStr << numRobot;
	times.push_back(numRobotsStr.str());
	
	/********************
	* Deinitialization *
	********************/
	SCIP_CALL(SCIPfree(&scip));

	BMScheckEmptyMemory();

	timeFile.appendTimeFile(
		fileName,
		times,
		numCustomer,
		numRobotOriginal,
		info);
	return SCIP_OKAY;
}

//------------------------------------------------------------
SCIP_RETCODE execmain(int argc, char** argv)
{
	std::ofstream outfile;

	string instance = argv[1];

	// number of customer
	int numberOfCustomer = atof(argv[2]);

	// number of vehicles
	int numberVehicles = atof(argv[3]);

	double timeLimit = atof(argv[4]);

	// percentage edges to keep in heuristic pricing
	double edgesToKeep = 0.6;

	bool withOutput = true;

	// percentage * number customers =  vars to be added after calculating a solutione to the pricing problem
	double varsToBeAdded = 0.5;
	varsToBeAdded = (int)(numberOfCustomer * (varsToBeAdded));
	if (varsToBeAdded == 0)
	{
		varsToBeAdded++;
	}
	int edgeLimit = numberOfCustomer / 2;

	//prepare output file
	// Get current time
	auto now = std::chrono::system_clock::now();
	std::time_t now_time = std::chrono::system_clock::to_time_t(now);
	// Convert to local time
	std::tm local_tm = *std::localtime(&now_time);
	// Format into string
	std::ostringstream oss;
	oss << std::put_time(&local_tm, "%Y-%m-%d");

	std::string datetime = oss.str();

	ostringstream strTimeFileName;
	strTimeFileName << "Branch-and-Price-PRP-results-date-" << datetime << ".txt";
	string fileName2 = strTimeFileName.str();
	cout << instance << " " << " " << numberOfCustomer << " " << " " << endl;

	TimeFile timeFile(fileName2);
	vector<string> names = { " time\t objective \t gap % \t info" };
	if (!fileExists(fileName2)) {
		timeFile.initializeTimeFile(timeFile.getTimeFileName(), names);
	}
	
	vector<Customer> customers(0);
	vector<vector<int>> distMatrix(numberOfCustomer + 1, vector<int>(numberOfCustomer + 1));
	vector<int> demands(numberOfCustomer);
	vector<int> data(4);
	
	// read instance
	Reader reader;
	reader.readInstance(instance, numberOfCustomer + 1, data, distMatrix, customers);

	cout << "----------------------------------------------------------------------" << endl;
	cout << "----------------------------------------------------------------------" << endl;
	cout << "- Start Branch-and-Price-Algorithm with " << numberOfCustomer << " customers and " << numberVehicles << " vehicles -" << endl;
	cout << "----------------------------------------------------------------------" << endl;
	cout << "----------------------------------------------------------------------" << endl;

	mainProgram(customers, distMatrix, data, numberOfCustomer, numberVehicles,
		varsToBeAdded, timeFile, edgeLimit, edgesToKeep,
		fileName2, timeLimit, withOutput, instance);


	cout << "completed";
	return SCIP_OKAY;

}
int main(int argc, char** argv)
{
	return execmain(argc, argv) != SCIP_OKAY ? 1 : 0;
}
