/**
 * This class represents the final node in a network. The pulse procedure is overrided!
 * 
 * Ref.: Lozano, L. and Medaglia, A. L. (2013). 
 * On an exact method for the constrained shortest path problem. Computers & Operations Research. 40 (1):378-384.
 * DOI: http://dx.doi.org/10.1016/j.cor.2012.07.008 
 * 
 * 
 * @author L. Lozano & D. Duque
 * @affiliation Universidad de los Andes - Centro para la Optimización y Probabilidad Aplicada (COPA)
 * @url http://copa.uniandes.edu.co/
 * 
 */

package sampling;

import java.util.ArrayList;

import backwardSampling.AlgorithmHandler;
import backwardSampling.DataHandler;



public class FinalNode extends Node {
	
	// Node id
	private int id;
	public ArrayList<Integer> pathArcs = new ArrayList<Integer>();
	
	
	// SP stuff
	private Node bLeft;
	private Node bRigth;
	private EdgePulse reverseEdges;
	
	// Bounds
	int minDist;
	int maxTime;
	int minTime;
	int maxDist;
	
	private boolean inserted;
	
	int c=0;
	int d=0;
	public FinalNode(int i) {
		super(i);
		id = i;
		inserted = false;
		minDist = infinity;
		minTime = infinity;
		maxTime = 0;
		maxDist = 0;
		bLeft = this;
		bRigth = this;
	}

	
	public int  getID()
	{
		return id;
	}
	
	public void addReversedEdge(EdgePulse e)
	{
		if(reverseEdges!=null){
			reverseEdges.addNextCommonTailEdge(e);
		}else
			reverseEdges = e;
	}
	
	
	public EdgePulse findEdgeByTarget(Node target){
		if(reverseEdges!=null){
			reverseEdges.findEdgebyTarget(target);
		}
		return null;
	}
	public EdgePulse getReversedEdges() {
		if(reverseEdges!= null){
			return reverseEdges;
		}return new EdgePulse(1,1, this,this , -1);
	}
	
	public void setMinDist(int c){
		minDist = c;
	}
	
	public int getMinDist(){
		return minDist;
	}
	
	public void setMaxTime(int mt){
		maxTime = mt;
	}
	
	public int getMaxTime(){
		return maxTime;
	}
	
	public void setMinTime(int t){
		minTime = t;
	}
	
	public int getMinTime(){
		return minTime;
	}
	
	public void setMaxDist(int md){
		maxDist = md;
	}
	
	public int getMaxDist(){
		return maxDist;
	}
	
	
	/**
	 * Unlink a vertex from the bucket
	 * @return true, if the buckets gets empty
	 */
	public boolean unLinkVertexDist(){
		if(bRigth.getID() == id){
			bLeft=this;
			bRigth=this;
			return true;
		}else{
			bLeft.setRigthDist(bRigth);
			bRigth.setLeftDist(bLeft);
			bLeft = this;
			bRigth = this;
			return false;
		}
	}
	/**
	 * Insert a vertex in a bucket. 
	 * New vertex is inserted on the left of the bucket entrance 
	 * @param v vertex in progress to be inserted
	 */
	public void insertVertexDist(Node v) {
		v.setLeftDist(bLeft);
		v.setRigthDist(this);
		bLeft.setRigthDist(v);
		bLeft = v;
	}
	
	
	public void setLeftDist(Node v){
		bLeft= v;
	}
	public void setRigthDist(Node v){
		bRigth= v;
	}
	public Node getBLeftDist(){
		return bLeft;
	}
	public Node getBRigthDist(){
		return bRigth;
	}
	public void setInsertedDist(){
		inserted = true;
	}
	public boolean isInserteDist(){
		return inserted;
	}

	public void reset(){
		inserted = false;
		this.pathArcs.clear();
	}
	public void setBounds(int MT, int MD){
		maxDist = MD- minDist;
		maxTime = MT - minTime;
	}
		
	public void pulse(int PTime, int PDist,ArrayList<Integer> path, ArrayList<Integer> pathArcs) {
		// Add node id to path
		path.add(id);
		
		//System.out.println(path+" cost: "+PDist);
		//System.out.println(pathArcs+" Arcs cost: "+PDist);
		addSample(pathArcs,PDist);
			
		if(PDist<=PulseGraph.PrimalBound && PTime<=PulseGraph.TimeC){
			
			
			// Update the best solution known
			PulseGraph.realDist=PTime;
			PulseGraph.PrimalBound=PDist;
				
		}
		// Remove the node id to backtrack
		path.remove((path.size()-1));
		
		// Check time limit
		if((System.currentTimeMillis()-AlgorithmHandler.sampleTime)/1000 > AlgorithmHandler.sTimeLimit){
			//System.out.println("TIME LIMIT");
			AlgorithmHandler.continueSampling=false;
		}
		
	}


	public void addSample(ArrayList<Integer> pathArcs, int obj) {
	
		for (int i = 0; i < pathArcs.size(); i++) {
			DataHandler.arcCount[pathArcs.get(i)]++;
		}
		
		if(AlgorithmHandler.sample.size()==0){
			AlgorithmHandler.sample.add(copy(pathArcs));
			AlgorithmHandler.sampleObj.add(obj);
		}
		else{
				boolean inserted = false;
				for (int i = 0; i < AlgorithmHandler.sample.size(); i++) {
					if(obj<=AlgorithmHandler.sampleObj.get(i)){
						AlgorithmHandler.sample.add(i,copy(pathArcs));
						AlgorithmHandler.sampleObj.add(i,obj);
						i=AlgorithmHandler.sample.size()+1;
						inserted = true;
					}
				}
				if(inserted == false){
					AlgorithmHandler.sample.add(copy(pathArcs));
					AlgorithmHandler.sampleObj.add(obj);
				}
				
		}
				
	}


	private ArrayList<Integer> copy(ArrayList<Integer> pathArcs) {
		
		ArrayList<Integer> copy = new ArrayList<Integer>();
		copy.addAll(pathArcs);
		return copy;
	}


	public void pulse2(int PRealDist, int PDist,ArrayList<Integer> path, ArrayList<Integer> pathArcs, int[] x) {
		// Add node id to path
		path.add(id);
		
		//System.out.println(path+" cost: "+PDist);
		//System.out.println(pathArcs+" Arcs cost: "+PDist);
		
			
		if(PDist<=PulseGraph.PrimalBound){
			
			
			// Update the best solution known
			PulseGraph.realDist=PRealDist;
			PulseGraph.PrimalBound=PDist;
			this.pathArcs.clear();
			this.pathArcs.addAll(pathArcs);
			//addSample(pathArcs,PDist);
			
		}
		// Remove the node id to backtrack
		path.remove((path.size()-1));
	}

	
	
	
}
