/*******************************************************************************
 * Copyright (C) 2013  Stefan Schroeder
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either 
 * version 3.0 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package jsprit.instance.reader;


import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.VehicleRoutingProblem.Builder;
import jsprit.core.problem.job.Shipment;
import jsprit.core.problem.solution.route.activity.TimeWindow;
import jsprit.core.problem.vehicle.Vehicle;
import jsprit.core.problem.vehicle.VehicleImpl;
import jsprit.core.problem.vehicle.VehicleTypeImpl;
import jsprit.core.util.Coordinate;
import jsprit.core.util.Locations;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;


/**
 * test instances for the capacitated vrp with pickup and deliveries and time windows. 
 * instances are from li and lim and can be found at:
 * http://www.top.sintef.no/vrp/benchmarks.html
 * @author stefan schroeder
 *
 */


public class LiLimReader {
	
	static class MyLocations implements Locations{

		private Map<String,Coordinate> locations = new HashMap<String, Coordinate>();

		public void addLocation(String id, Coordinate coord){
			locations.put(id, coord);
		}

		@Override
		public Coordinate getCoord(String id) {
			return locations.get(id);
		}	
	}
	
	static class CustomerData{
		public Coordinate coord;
		public double start;
		public double end;
		public double serviceTime;
		
		public CustomerData(Coordinate coord, double start, double end, double serviceTime) {
			super();
			this.coord = coord;
			this.start = start;
			this.end = end;
			this.serviceTime = serviceTime;
		}
	}
	
	static class Relation{
		public String from;
		public String to;
		public int demand;
		public Relation(String from, String to, int demand) {
			super();
			this.from = from;
			this.to = to;
			this.demand = demand;
		}
		
	}
	
	private static Logger logger = LogManager.getLogger(LiLimReader.class);
	
	private VehicleRoutingProblem.Builder vrpBuilder;
	
	private int vehicleCapacity;
	
	private String depotId;
	
	private Map<String,CustomerData> customers;
	
	private Collection<Relation> relations;
	
	private double depotOpeningTime;

	private double depotClosingTime;

	private int fixCosts = 0;
	
	public LiLimReader(Builder vrpBuilder) {
		customers = new HashMap<String, LiLimReader.CustomerData>();
		relations = new ArrayList<LiLimReader.Relation>();
		this.vrpBuilder = vrpBuilder;
	}
	
	public LiLimReader(Builder builder, int fixCosts) {
		customers = new HashMap<String, LiLimReader.CustomerData>();
		relations = new ArrayList<LiLimReader.Relation>();
		this.vrpBuilder = builder;
		this.fixCosts = fixCosts;
	}

	public void read(String filename){
		readShipments(filename);
		buildShipments();
		VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, vehicleCapacity)
				.setCostPerDistance(1.0).setFixedCost(fixCosts).build();
		Vehicle vehicle = VehicleImpl.Builder.newInstance("vehicle")
				.setEarliestStart(depotOpeningTime).setLatestArrival(depotClosingTime)
				.setStartLocationCoordinate(customers.get(depotId).coord).setType(type).build();
		vrpBuilder.addVehicle(vehicle);
	}
	
	private void buildShipments() {
		Integer counter = 0;
		for(Relation rel : relations){
			counter++;
			String from = rel.from;
			String to = rel.to;
			int demand = rel.demand;
			Shipment s = Shipment.Builder.newInstance(counter.toString()).addSizeDimension(0, demand)
					.setPickupCoord(customers.get(from).coord).setPickupServiceTime(customers.get(from).serviceTime)
					.setPickupTimeWindow(TimeWindow.newInstance(customers.get(from).start, customers.get(from).end))
					.setDeliveryCoord(customers.get(to).coord).setDeliveryServiceTime(customers.get(to).serviceTime)
					.setDeliveryTimeWindow(TimeWindow.newInstance(customers.get(to).start, customers.get(to).end)).build();
			vrpBuilder.addJob(s);
		}
		
	}
	
	private BufferedReader getReader(String file) {
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(file));
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
			logger.error(e1);
			System.exit(1);
		}
		return reader;
	}

	private void readShipments(String file) {
		BufferedReader reader = getReader(file);
		String line = null;
		boolean firstLine = true;
		try {
			while((line = reader.readLine()) != null){
				line = line.replace("\r", "");
				line = line.trim();
				String[] tokens = line.split("\t");
				if(firstLine){
					int vehicleCapacity = getInt(tokens[1]);
					this.vehicleCapacity = vehicleCapacity;
					firstLine = false;
					continue;
				}
				else{
					String customerId = tokens[0];
					Coordinate coord = makeCoord(tokens[1], tokens[2]);
					int demand = getInt(tokens[3]);
					double startTimeWindow = getDouble(tokens[4]);
					double endTimeWindow = getDouble(tokens[5]);
					double serviceTime = getDouble(tokens[6]);
//					vrpBuilder.addLocation(customerId, coord);
					customers.put(customerId, new CustomerData(coord,startTimeWindow,endTimeWindow, serviceTime));
					if(customerId.equals("0")){
						depotId = customerId;
						depotOpeningTime = startTimeWindow;
						depotClosingTime = endTimeWindow;
					}
					if(demand > 0){
						relations.add(new Relation(customerId,tokens[8],demand));
					}
				}
			}
			reader.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	private Coordinate makeCoord(String xString, String yString) {
		double x = Double.parseDouble(xString);
		double y = Double.parseDouble(yString);
		return new Coordinate(x,y);
	}
	
	private double getDouble(String string) {
		return Double.parseDouble(string);
	}

	private int getInt(String string) {
		return Integer.parseInt(string);
	}


}
