/*
 * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
 *
 * This file is part of Simonstrator.KOM.
 * 
 * Simonstrator.KOM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * PeerfactSim.KOM 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PeerfactSim.KOM.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.vehicular.relevance;

import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;

import de.tudarmstadt.maki.simonstrator.api.Host;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.VehicleInformationComponent;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.information.VehicularPointInformation;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.relevance.EventRelevanceCalculationComponent;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.roadnetwork.RoadNetworkEdge;

public class PredictionBasedEventRelevanceCalculationComponent implements EventRelevanceCalculationComponent {
	private static final double ABORT_THRESHOLD = 0.001d;
	private Host _host;

	public PredictionBasedEventRelevanceCalculationComponent(Host pHost) {
		_host = pHost;
	}
	
	@Override
	public double calculateRelevance(VehicleInformationComponent pVehicle, VehicularPointInformation pInformation) {
		double relevance = 0;

		RoadNetworkEdge eventLocation = pInformation.getEdge();

		Queue<RoadNetworkPath> currentPaths = new PriorityQueue<>(new RoadNetworkPathComperator());

		RoadNetworkEdge currentVehiclePosition = pVehicle.getCurrentRoute().getStart();

		currentPaths.add(new RoadNetworkPath(currentVehiclePosition, 1, 0));

		while (!currentPaths.isEmpty()) {
			RoadNetworkPath roadNetworkPath = currentPaths.poll();
			
			if (roadNetworkPath.getProbability() < ABORT_THRESHOLD) {
				break;
			}

			List<RoadNetworkEdge> accessibleEdges = roadNetworkPath.getEdge().getAccessibleEdges();
			
			for (RoadNetworkEdge accessibleEdge : accessibleEdges) {
				double accessibleEdgeProbability = 1 / ((double) accessibleEdges.size());
				long travelTime = Time.SECOND;
				
				if (accessibleEdge == eventLocation) {
					long totalTravelTime = roadNetworkPath.getTravelTime();
					double totalProbability = roadNetworkPath.getProbability();
					
					double temporalRelevance = calculateTemporalRelevance(pVehicle,
							Time.getCurrentTime() + totalTravelTime, pInformation);

					relevance += totalProbability * temporalRelevance;
				} else {
					RoadNetworkPath newPath = roadNetworkPath.createPath(accessibleEdge, accessibleEdgeProbability,
							travelTime);
					currentPaths.add(newPath);
				}
			}
		}

		return relevance;
	}

	/**
	 * This method calculates the temporal relevance of an event for a vehicle
	 * at a certain point in time.
	 * 
	 * @param pVehicle
	 *            The vehicle for which the information is relevant.
	 * @param pTime
	 *            The time for which the relevance should be calculated.
	 * @param pInformation
	 *            The information for which the relevance is calculated.
	 * @return The temporal relevance of the event.
	 */
	private double calculateTemporalRelevance(VehicleInformationComponent pVehicle, double pTime,
			VehicularPointInformation pInformation) {
		return 1;
	}

	private class RoadNetworkPathComperator implements Comparator<RoadNetworkPath> {

		public RoadNetworkPathComperator() {
		}

		@Override
		public int compare(RoadNetworkPath pPath0, RoadNetworkPath pPath1) {
			return Double.compare(pPath0.getProbability(), pPath1.getProbability());
		}


	}

	private class RoadNetworkPath {
		private double probability;
		private long travelTime;
		private RoadNetworkEdge edge;

		public RoadNetworkPath(RoadNetworkEdge pEdge, double pProbability, long pTravelTime) {
			edge = pEdge;
			probability = pProbability;
			travelTime = pTravelTime;
		}

		public RoadNetworkEdge getEdge() {
			return edge;
		}

		public double getProbability() {
			return probability;
		}

		public long getTravelTime() {
			return travelTime;
		}

		public RoadNetworkPath createPath(RoadNetworkEdge pCurrentEdge,
				double pProbability, long pTravelTime) {
			return new RoadNetworkPath(pCurrentEdge, getProbability() * pProbability, getTravelTime() + pTravelTime);
		}
	}

	@Override
	public void initialize() {

	}

	@Override
	public void shutdown() {

	}

	@Override
	public Host getHost() {
		return _host;
	}
}
