#include <ilsolver/ilosolver.h>
#include <ildispat/ilodispatcher.h>


ILOSTLBEGIN

void Info(IloDispatcher dispatcher) 
{
  IloSolver solver = dispatcher.getSolver();
  solver.printInformation();
  //dispatcher.printInformation();
  solver.out() << "===============" << endl
               << "Cost         : " << dispatcher.getTotalCost() << endl 
               << "Number of vehicles used : " 
               << dispatcher.getNbOfVehiclesUsed() << endl
               << "Solution     : " << endl
              // << dispatcher << endl;
			   << IloTerse(dispatcher) << endl;
			   
}

void creatVehicle(IloModel model, IloInt nbOfTrucks, ifstream& fin, IloDimension2 length, IloDimension2 tardiness, IloDimension1 weight)
{
	IloEnv env = model.getEnv();

	IloNum capacity;		//¿ë·® ÀÔ·Â
	fin >> capacity;

	IloNum depotX, depotY, openTime, closeTime;		//µ¥Æ÷Á¤º¸ ÀÔ·Â
	fin >> depotX  >> depotY >> openTime >> closeTime;

	IloNode depot(env, depotX, depotY);	//µ¥Æ÷³ëµå »ý¼º

	for(int i=0; i<nbOfTrucks; i++)
	{
		IloVisit first(depot, "Depot");	//Ãâ¹ßµ¥Æ÷ºñÁöÆ® »ý¼º
		IloVisit last(depot, "Depot");		//µµÀÛµ¥Æ÷ºñÁöÆ® »ý¼º

		model.add(first.getCumulVar(length) >= openTime);//Å¸ÀÓÀ©µµ¿ì Á¦¾à			
		model.add(last.getCumulVar(length) <= closeTime);//Å¸ÀÓÀ©µµ¿ì Á¦¾à		

		model.add(first.getCumulVar(weight) == 0);//ÃÊ±â ¹æ¹®Áö Á¦¾à
		model.add(last.getCumulVar(weight) == 0);//¸¶Áö¸· ¹æ¹®Áö Á¦¾à

		char name[20];
		sprintf(name, "Vehicle %ld", i);//Â÷·®ÀÌ¸§ »ý¼º

		IloVehicle vhc(env, name);//Â÷·® »ý¼º

		vhc.setFirstVisit(first);//Â÷·® Ãâ¹ßÁö ¼³Á¤
		vhc.setLastVisit(last);//Â÷·® µµÂøÁö ¼³Á¤

		vhc.setCapacity(weight, capacity);//Â÷·® ¿ë·® ¼³Á¤

		vhc.setCost(length, 1);//½Ã°£¿¡ µû¸¥ ÄÚ½ºÆ® ºÎ°ú

		vhc.setCost(tardiness, 1);//Å¸µð´Ï½ºÄÚ½ºÆ® //ÀÌ ÄÚ½ºÆ®°¡ »ó´çÈ÷ ¸ðÈ£ÇÕ´Ï´Ù.
     
		model.add(vhc);//Â÷·® ¸ðµ¨¿¡ Ãß°¡
	}
}

void creatVisit(IloModel model, IloInt nbOfVisits, IloVisitArray visits, ifstream& fin, 
				IloDimension2 length, IloDimension2 tardiness, IloDimension1 weight)
{
	IloEnv env = model.getEnv();
	
	for(int i=0; i<nbOfVisits; i++)
	{
		IloInt id;											//¹æ¹®Áö Á¤º¸ ÀÔ·Â
		IloNum x, y, quantity, minTime, maxTime, dropTime;	//¹æ¹®Áö Á¤º¸ ÀÔ·Â
		fin >> id >> x >> y >> quantity >> minTime >> maxTime >> dropTime;	//¹æ¹®Áö Á¤º¸ ÀÔ·Â
	
		char name[10];
		sprintf(name, "%ld", id);//¹æ¹®Áö ÀÌ¸§ ¼³Á¤

		IloNode customer(env, x, y);//¹æ¹®Áö ³ëµå ¼³Á¤
		IloVisit visit(customer, name);//¹æ¹®Áö ¼³Á¤


		model.add(visit.getTransitVar(weight) == -quantity);//¹æ¹®Áö ¿ä±¸ ¼ö·® ¼³Á¤

		model.add(visit.getDelayVar(length) == dropTime);//¹æ¹®Áö ÀÛ¾÷ ½Ã°£ ¼³Á¤

		model.add(minTime <= visit.getCumulVar(length) <= maxTime);//¹æ¹®Áö Å¸ÀÓ À©µµ¿ì ¼³Á¤

		//IloNumVar tard(env, 0, IloInfinity, "tard");//ÀÌ º¯¼öÀÇ µµ¸ÞÀÎ Upper°¡ µ¥Æ÷ close timeÀÏ ÇÊ¿ä°¡ ¾ø½À´Ï´Ù.(Á¦¾à¿¡ ÀÇ°Å)
		//model.add(tard >= visit.getCumulVar(length) - maxTime);
		//model.add(tard >= minTime - visit.getCumulVar(length));
		//model.add(visit.getTransitVar(tardiness) == tard);
		
		model.add(visit.getTransitVar(tardiness) >= maxTime - visit.getCumulVar(length));//¼öÁ¤
		model.add(visit.getTransitVar(tardiness) >= visit.getCumulVar(length) - minTime);//¼öÁ¤	
		

		visit.setPenaltyCost(1);	//ÆÐ³ÎÆ¼ ÄÚ½ºÆ® ÀÔ·Â

		model.add(visit);//¸ðµ¨¿¡ Ãß°¡
		visits.add(visit);//¹æ¹®Áö ¾î·¹ÀÌ¿¡ Çö ¹æ¹®Áö Ãß°¡
	}

	
}

void creatInitRouting(IloModel model, IloSolver solver, IloDispatcher dispatcher, IloRoutingSolution solution,
						IloGoal instCost)
{
	cout << "ÃÊ±â °æ·Î »ý¼º. " << endl;

	IloEnv env = model.getEnv();


	for(IloVehicleIterator vehlt(model); vehlt.ok(); ++vehlt)
	{
		IloVehicle vehicle = *vehlt;
		for(int i=0; i<2; i++)//ÃÖ´ë 2È¸Â÷ °¡´É
		{	
			IloVisit visit(vehicle.getFirstVisit().getNode(), "depot");//µ¥Æ÷ ¹æ¹®Áö ¼³Á¤
			
			visit.setPenaltyCost(0);//¹æ¹®Áö ÆÐ³ÎÆ¼ ÄÚ½ºÆ® ÀÔ·Â
			
			model.add(visit);//Ãß°¡ÇÒ ¹æ¹®Áö ¸ðµ¨¿¡ ÀÔ·Â
			
			IloGoal insert = IloInsertVisit(env, visit, vehicle, solution, instCost);

			if(solver.solve(insert))
			{
				solution.add(visit);
				solution.store(solver);//°æ·Î°¡ È®Á¤µÇ¸é ¼Ö·ç¼Ç¿¡ ÀúÀå
				cout << "È¸Â÷ µ¥Æ÷¸¦ Ãß°¡ÇÕ´Ï´Ù. " << endl;
			}
			else
			{
				cout << visit.getNode() << "¸¦ »ðÀÔÇÒ ¼ö ¾ø½À´Ï´Ù. " << endl;
				model.remove(visit);//°æ·Î°¡ ½ÇÆÐÇÏ¸é ¸ðµ¨¿¡¼­ ¹æ¹®Áö Á¦°Å
			}
		}
	}
	
	cout << endl;

}


IloVisitArray OrderVisits(IloEnv env, IloVisitArray visits, IloDistance dist, IloDimension2 tardiness) 
{
  IloModel tspModel(env);

  IloInt nbOfVisits = visits.getSize();
  IloVisit first(visits[0].getNode(), "first");
  IloVisit last(visits[0].getNode(), "last");

  IloVehicle vehicle(first, last, "TSP");
  tspModel.add(vehicle);
  IloDimension2 dim(env, dist, IloFalse);
  vehicle.setCost(dim, 1.0);
  //test
  IloDimension2 tard(env, IloEuclidean, "tardiness");
  vehicle.setCost(tard, 1.0);
  tspModel.add(tard);
  //test end

  tspModel.add(dim);
  IloInt i;
  for (i = 1; i < nbOfVisits; i++)
    tspModel.add(visits[i]);
 
  IloSolver solver(tspModel);
  IloDispatcher dispatcher(solver);
  IloGoal instCost = IloDichotomize(env, dispatcher.getCostVar(), IloFalse);
  
  solver.out() << "Producing insertion order" << endl;
  solver.solve(IloNearestAdditionGenerate(env) && instCost);
  
  IloRoutingSolution rsolution(tspModel);
  rsolution.store(solver);
  IloNHood nhood = IloTwoOpt(env);
  IloMetaHeuristic improve = IloImprove(env);
  IloGoal move = IloSingleMove(env, rsolution, nhood, improve, instCost);
  
  while (solver.solve(move)) { }
 
  IloVisitArray orderedVisits(env, nbOfVisits);

  IloRoutingSolution::RouteIterator rit(rsolution, vehicle);
  ++rit;
  orderedVisits[0] = visits[0];//Ã³À½Àº µ¥Æ÷

  for (IloInt k = 1; k < nbOfVisits; ++k, ++rit)
    orderedVisits[k] = *rit;//µ¥Æ÷´Â ÀÌ¹Ì ÀÔ·ÂÇßÀ¸¹Ç·Î vehicle°¡ ¹æ¹®ÇÏ´Â ¹æ¹®Áö ÀÔ·Â

  nhood.end();
  solver.end();
  rsolution.end();

  
  return orderedVisits;//vehicleÂ÷·®ÀÌ ¹æ¹®ÇÏ´Â ¹æ¹®Áö¸¦ ¸®ÅÏ
}


IloVisit getVisitByOrderedVisits(IloVisit orderedVisit, IloVisitArray visits)
{
	
	for(int i=0; i<visits.getSize(); i++)
	{
		if(orderedVisit.getNode() == visits[i].getNode())
			return visits[i];
	}
	return NULL;

}

void creatNewRouting(IloModel model, IloSolver solver, IloDispatcher dispatcher, IloRoutingSolution solution,
						IloGoal instCost, IloVisitArray orderedVisits, IloVisitArray visits)
{
	cout << endl << "»õ·Î¿î °æ·Î »ý¼º" << endl;

	IloEnv env = model.getEnv();

	for(int i=0; i<orderedVisits.getSize(); i++)
	{
		IloVisit visit = getVisitByOrderedVisits(orderedVisits[i], visits);//»õ·Î¿î ¹æ¹®¼ø¼­´ë·Î ºñÁöÆ® »ý¼º
		
		visit.setPenaltyCost(500);//¹Ýµå½Ã ¹æ¹®ÇØ¾ß ÇÏ´Â ¹æ¹®ÁöÀÇ Æä³ÎÆ¼ÄÚ½ºÆ®
		model.add(visit);//ÇØ´ç ¹æ¹®Áö ¸ðµ¨¿¡ Ãß°¡
			
		IloGoal insert = IloInsertVisit(env, visit, solution, instCost);//»õ·Î¿î ¹æ¹®Áö¸¦ ÇÏ³ª¾¿ Ãß°¡ÇÏ´Â °ñ
		if(!solver.solve(insert))
		{
			cout << "»õ·Î¿î ¹æ¹®Áö¸¦ Ãß°¡ÇÒ ¼ö ¾ø½À´Ï´Ù. " << endl;
			//model.remove(visit);//Á¦°ÅÇÏ°Ô µÇ¸é ±âÁ¸ÀÇ ¹æ¹®Áö ±îÁö Á¦°ÅµË´Ï´Ù. Á¦°ÅÇÏ¸é ¾ÈµÊ
		}
		else
		{
			cout << "»õ·Î¿î ¹æ¹®Áö Ãß°¡ ¿Ï·á" << endl;
			//solution.add(visit);//±âÁ¸ÀÇ ¹æ¹®Áö¸¦ »ç¿ëÇÏ±â ¶§¹®¿¡ Áßº¹ Ãß°¡ÇÒ ÇÊ¿ä°¡ ¾øÀ½
			solution.store(solver);
		}
	}

	cout << endl;

}

void Info2(IloDispatcher dispatcher, const char* message) 
{	
	cout << message << "Cost : " << dispatcher.getTotalCost() << endl
        << "Unperformed visits : " << dispatcher.getNumberOfUnperformedVisits() << endl
        << "Number of vehicles used : " << dispatcher.getNbOfVehiclesUsed() << endl;
        
}

void ImproveWithGreedy(IloDispatcher dispatcher,
                        IloRoutingSolution solution,
                        IloNHood nhood,
                        int limitTime,
                        IloGoal subGoal) 
{
	IloEnv env = dispatcher.getEnv();
	IloSolver solver = dispatcher.getSolver();

	IloGoal move = IloSingleMove(env, solution, nhood, IloImprove(env), subGoal);
	IloGoal limitGoal = IloLimitSearch(env, move, IloTimeLimit(env, limitTime));

	
	for (int i=0; int(env.getTime()) <= limitTime; i++)
	{
		if (solver.solve(limitGoal))
		{					
			solution.store(solver);
		    if (i%20 == 0) Info2(dispatcher, "<Greedy> --- ");
		}
		else break;
	}

	IloGoal restoreSolution = IloRestoreSolution(env, solution);// && subGoal;
	solver.solve(restoreSolution);
}


int main()
{

	IloEnv env;
	try 
	{
		IloModel model(env);	//¸ðµ¨ °´Ã¼ »ý¼º

		IloDimension2 length(env, IloEuclidean, "Length");	//½Ã°£ µð¸àÀü
		model.add(length);

		IloDimension2 tardiness(env, IloEuclidean, "Tardiness");			//Å¸µð µð¸àÀü
		model.add(tardiness);

		IloDimension1 weight(env, "Weight");				//¹«°Ô µð¸àÀü
		model.add(weight);
		

		ifstream fin;
		fin.open("vrp5.dat");

		IloInt nbOfVisits;		//ºñÁöÆ® ¼ö ÀÔ·Â
		fin >> nbOfVisits;

		IloInt nbOfTrucks;		//Â÷·® ¼ö ÀÔ·Â
		fin >> nbOfTrucks;
			
   
		//===============================================
		//* Â÷·® »ý¼º								   //
		//**										   //
		//===============================================		
		creatVehicle(model, nbOfTrucks, fin, length, tardiness, weight);//Â÷·®»ý¼º



		//===============================================
		//* ¹æ¹®Áö(IloVisit) »ý¼º					   //
		//**										   //
		//===============================================
		IloVisitArray visits(env); //¹æ¹®Áö ¾î·¹ÀÌ »ý¼º		
		
		creatVisit(model, nbOfVisits, visits, fin, length, tardiness, weight);//¹æ¹®Áö »ý¼º
		
		fin.close();


		//===============================================
		//* ÃÊ±âÇØ »ý¼º								   //
		//**										   //
		//===============================================
		IloSolver solver(model);//¼Ö¹ö°´Ã¼ »ý¼º
		IloDispatcher dispatcher(solver);//µð½ºÆÐÃÄ °´Ã¼ »ý¼º
		IloRoutingSolution solution(model);//¶óÀÌÆÃ¼Ö·ç¼Ç °´Ã¼ »ý¼º

		IloGoal instCost = IloDichotomize(env, dispatcher.getCostVar(), IloFalse);//°ñ »ý¼º
		//IloGoal goal = IloNearestAdditionGenerate(env, instCost);//°ñ »ý¼º
					 //= IloInsertionGenerate(env);
				     //= IloSavingsGenerate(env) && instCost;

		creatInitRouting(model, solver, dispatcher, solution, instCost);//ÃÊ±âÇØ »ý¼º
		cout << "ÃÊ±â °æ·Î === µ¥Æ÷ °æ·Î¸¦ ³ªÅ¸³À´Ï´Ù. " << endl;
		cout << "===================================================" << endl;
		cout << IloTerse(dispatcher) << endl;
		cout << "===================================================" << endl << endl;
		
		//===============================================
		//* »õ·Î¿î °æ·Î »ý¼º						   //
		//**										   //
		//===============================================
		IloDistance distance = IloDistance(env, IloEuclidean);//°Å¸® °´Ã¼ »ý¼º
		IloVisitArray orderedVisits = OrderVisits(env, visits, distance, tardiness);//»õ·Î¿î ºñÅ¬ÀÌ ¹æ¹®ÇÏ´Â ¹æ¹®Áö 


		//===============================================
		//* »õ·Î¿î ¹æ¹®Áö Ãß°¡						   //
		//**										   //
		//===============================================
		creatNewRouting(model, solver, dispatcher, solution, instCost, orderedVisits, visits);//»õ·Î¿î ¹æ¹®Áö Ãß°¡ ÇÔ¼ö
		
			
		//===============================================
		//* ÈÞ¸®½ºÆ½ Àû¿ë							   //
		//**										   //
		//===============================================
		IloInt limitTime = 10;//ÈÞ¸®½ºÆ½Àû¿ë½Ã°£ /ÃÊ
		IloNHood nhood = 
			  IloMakePerformed(env)
			  + IloSwapPerform(env)
			+ IloRelocate(env)
			+ IloExchange(env)
			+ IloCross(env)
			+ IloOrOpt(env)
			+ IloTwoOpt(env)
			+ IloMakeUnperformed(env);

		cout << endl << "===============================================" << endl;
		cout << "ÈÞ¸®½ºÆ½À» Àû¿ëÇÕ´Ï´Ù." << endl << "ÇØ °³¼±ÀÌ ¾øÀ»°æ¿ì Áß´ÜµÉ ¼ö ÀÖ½À´Ï´Ù.  " << endl;
		cout << "===============================================" << endl;
		ImproveWithGreedy(dispatcher, solution, nhood, limitTime, instCost);//ÈÞ¸®½ºÆ½ Àû¿ë

		cout << endl << "===============================================" << endl;
		cout << "ÃÖÁ¾ º¸°í¼­¸¦ Ãâ·ÂÇÕ´Ï´Ù. " << endl;
		cout << "===============================================" << endl;
		Info(dispatcher); //ÃÖÁ¾ º¸°í¼­   
	

	} 
	catch(IloException& ex) 
	{
    cout << "Error: " << ex << endl;
	} 
	env.end();
	return 0;


}