/*
 * 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.decision;

import de.tud.kom.p2psim.impl.topology.movement.VehicleMovementModel;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Host;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
import de.tudarmstadt.maki.simonstrator.api.component.core.MonitorComponent.AnalyzerNotAvailableException;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.environment.EnvironmentSensor;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.VehicleDecisionComponent;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.VehicleInformationComponent;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.caching.CachingComponent;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.information.RoadInformation;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.roadnetwork.RoadNetwork;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.roadnetwork.RoadNetworkEdge;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.roadnetwork.RoadNetworkRoute;
import de.tudarmstadt.maki.simonstrator.api.component.vehicular.roadnetwork.VehicleRouteAnalyzer;

public abstract class AbstractVehicleDecisionComponent implements VehicleDecisionComponent, EventHandler {

	private static final int EVENT_DECISION_MAKING_CYCLE = 0;

    private static final long EVENT_DECISION_MAKING_FREQUENCY = Time.SECOND * VehicleMovementModel.TIMESTEP_RATIO;

	private VehicleInformationComponent _vehicleInformation;
	private Host _host;
	private CachingComponent _cache;

    private boolean _routeAnalyzerInit = false;
    private VehicleRouteAnalyzer _routeAnalyzer = null;

    private EnvironmentSensor _sensors;

	public AbstractVehicleDecisionComponent(Host pHost) {
		_host = pHost;
	}

	@Override
	public void initialize() {
		try {
            _cache = getHost().getComponent(CachingComponent.class);
            _sensors = getHost().getComponent(EnvironmentSensor.class);
		} catch (ComponentNotAvailableException e) {
			throw new AssertionError(e);
		}
	}

	@Override
	public void shutdown() {
		// Nothing to do
	}

	public VehicleInformationComponent getVehicleInformation() {
		if (_vehicleInformation == null) {
			try {
				_vehicleInformation = getHost().getComponent(VehicleInformationComponent.class);
			} catch (ComponentNotAvailableException e) {
				throw new AssertionError(e);
			}
		}
		return _vehicleInformation;
	}

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

	@Override
	public void startDecisionMaking() {
		Event.scheduleWithDelay(EVENT_DECISION_MAKING_FREQUENCY / 3 * 2, this, null, EVENT_DECISION_MAKING_CYCLE);
	}

	@Override
	public final void eventOccurred(Object pContent, int pType) {
		switch (pType) {
		case EVENT_DECISION_MAKING_CYCLE:
			RoadNetworkRoute currentRoute = getVehicleInformation().getCurrentRoute();

			if (currentRoute != null) {
	            boolean changed = false;
			    if (currentRoute.getRoute().size() > 2) {
                    BenefitBasedRoute optimalRouteWithBenefit = getOptimalRoute(currentRoute,
                            _cache.getDecidedCacheEntries(RoadInformation.class), _sensors.getEnvironment());
			        if (optimalRouteWithBenefit != null && optimalRouteWithBenefit.getBenefit() > 0) {
			            RoadNetworkRoute optimalRoute = optimalRouteWithBenefit.getRoute();
			            if (optimalRouteWithBenefit.getRoute().getRoute().size() > 1) {
		                    if (hasRouteAnalyzer()) {
		                        getRouteAnalyzer().routeChanged(getHost().getId(), currentRoute, optimalRoute);
		                    }
		                    changed = true;
		                    getVehicleInformation().changeCurrentRoute(optimalRouteWithBenefit.getRoute());
			            }
			        }
			    }
			    if (!changed && hasRouteAnalyzer()) {
			        getRouteAnalyzer().routeUnchanged(getHost().getId(), currentRoute);
			    }
			}

			Event.scheduleWithDelay(EVENT_DECISION_MAKING_FREQUENCY, this, null, EVENT_DECISION_MAKING_CYCLE);
			break;
		default:
			break;
		}
	}

    public boolean hasRouteAnalyzer() {
        if (!_routeAnalyzerInit) {
            _routeAnalyzerInit = true;
            try {
                _routeAnalyzer = Monitor.get(VehicleRouteAnalyzer.class);
            } catch (AnalyzerNotAvailableException e) {
            }
        }

        return _routeAnalyzer != null;
    }

    public VehicleRouteAnalyzer getRouteAnalyzer() {
        return _routeAnalyzer;
    }
}