#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].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;
				}
			}
		}
	}
}

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,
	bool withOutput,
	double timeLimit,
	string instance,
	string fileName
)
{

	// 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();

	int timeHorizon = 0;
	timeHorizon = data[0];
	double minSpeed = 1.0 * 1015 / 72;
	double maxSpeed = data[3];
	int shipCapacity = 1.0 * data[4];
	SCIP_Real   eps = 1e-6;

	vector<double> times(6);

	times[0] = edgesToKeep;
	times[1] = numOfVarsToBeAdded;
	times[5] = 0;

	// Contains all solutions
	vector<double> solutions;

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

	static const char* PRICER_NAME = "Maritime-Transportation-Pricer";

	// Vector of all variables in the model
	vector<Variable> vars;
	vector<Variable>* p_vars(&vars);

	// 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);
		}
	}
	vector<vector<bool>> speedOrder;
	// Calculate if job j can suceed i at lowest speed and completes at lower time window
	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 timebranchAndPrice = clock();

	// include VRP pricer
	Pricer* pricer = new Pricer(
		scip,								/**< SCIP pointer */
		PRICER_NAME,						/**< name of pricer */
		numCustomer,							/**< number of nodes */
		minSpeed,							/**< minmial speed */
		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,
		shipCapacity,
		timebranchAndPrice,
		timeLimit,
		orgEdgeSet,
		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));


	timebranchAndPrice = clock() - timebranchAndPrice;
	times[2] = (double)timebranchAndPrice;


	double obj = SCIPgetPrimalbound(scip);

	string info = "";

	if (-obj <= 1 && -obj >= 999999)
	{
		info = "infeasible";
	}

	times[3] = obj;

	if (SCIPgetStatus(scip) == 5)
	{
		times[4] = SCIPgetGap(scip) * 100;
		info = "time out";
	}
	else
	{
		times[4] = 0.0;
	}
	SCIPprintBestSol(scip, NULL, 0);

	/********************
	* Deinitialization *
	********************/

	SCIP_CALL(SCIPfree(&scip));

	BMScheckEmptyMemory();

	
	timeFile.appendTimeFile(
		fileName,
		times,
		numCustomer,
		numRobot,
		info, instance
	);

	return SCIP_OKAY;

}

//------------------------------------------------------------
SCIP_RETCODE execmain(int argc, char** argv)
{
	std::ofstream outfile;
	string instance = argv[1];

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

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

	double timeLimit = atof(argv[4]);

	// edges to keep %
	double edgesToKeep = 0.6;  

	// vars to be added %
	double portionOfCust = 0.5;

	bool withOutput = true;

	// percentage * number customers =  vars to be added after calculating a solutione to the pricing problem
	int varsToBeAdded = (int)(numberCustomer * (portionOfCust));
	if (varsToBeAdded == 0)
	{
		varsToBeAdded++;
	}
	int edgeLimit = numberCustomer / 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 timeFileStringStream;
	timeFileStringStream << "Branch-and-Price-Maritime-Transportation-results-date-" << datetime << ".txt";
	TimeFile timeFile(timeFileStringStream.str());

	vector<string> names = { " \t time\t objective \t gap %\t info" };
	if (!fileExists(timeFileStringStream.str())) {
		timeFile.initializeTimeFile(timeFileStringStream.str(), names);
	}

	timeFileStringStream.end;

	vector<Customer> customers(0);
	vector<vector<int>> distMatrix(numberCustomer + 1, vector<int>(numberCustomer + 1));
	vector<int> demands(numberCustomer);
	vector<int> data(5);


	// read instance
	Reader reader;
	reader.readInstance(instance, numberCustomer, data, distMatrix, customers);



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

 	mainProgram(customers, distMatrix, data, numberCustomer, numberVehicles,
		varsToBeAdded, timeFile, edgeLimit,
		edgesToKeep, withOutput,
		timeLimit, instance, timeFileStringStream.str());



	return SCIP_OKAY;
}

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

}
