/*
 * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
 *
 * This file is part of PeerfactSim.KOM.
 * 
 * PeerfactSim.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.topology;


import java.util.HashMap;

import de.tud.kom.p2psim.api.common.SimHost;
import de.tud.kom.p2psim.api.energy.ComponentType;
import de.tud.kom.p2psim.api.energy.EnergyModel;
import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.movement.SimEnergyConsumingLocationActuator;
import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
import de.tud.kom.p2psim.impl.energy.ActuatorEnergyComponent;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
import de.tudarmstadt.maki.simonstrator.api.component.overlay.OverlayComponent;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.battery.RechargableBattery;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationListener;
import de.tudarmstadt.maki.simonstrator.api.component.uavsupport.strategy.callbacks.ReachedLocationCallback;

/**
 * Extension to the default topology component gives access to movement control of the actuator. 
 * Also allows to do simple energy consumption calculation based on distances (with maximum speed).
 * 
 * @author Julian Zobel
 * @version 
 */
public class UAVTopologyComponent extends DefaultTopologyComponent implements SimEnergyConsumingLocationActuator {
	
	private ActuatorEnergyComponent actuatorEnergyComponent;	
	
	private RechargableBattery battery;
	
	private OverlayComponent uav;
	
	private HashMap<Location, ReachedLocationCallback> targetLocations;
	
	public UAVTopologyComponent(SimHost host, Topology topology,
			MovementModel movementModel, PlacementModel placementModel, boolean registerAsInformationProviderInSiS) {
		super(host, topology, movementModel, placementModel, registerAsInformationProviderInSiS);	
		
		targetLocations = new HashMap<>();
	}
	
	public void setUAVComponent(OverlayComponent uav) {
		this.uav = uav;
	}

	@Override
	public void initialize() {		
		super.initialize();
				
		try {
			actuatorEnergyComponent = getHost().getComponent(EnergyModel.class)
					.getComponent(ComponentType.ACTUATOR, ActuatorEnergyComponent.class);		
		} catch (ComponentNotAvailableException e) {
			System.err.println("No Acutator Energy Component was found!");
		}
		
		try {
			battery = getHost().getComponent(RechargableBattery.class);
				
		} catch (ComponentNotAvailableException e) {
			System.err.println("No Battery Component was found!");
		}
	}
	
	public OverlayComponent getOverlayComponent() {
		return uav;
	}

	public ActuatorEnergyComponent getActuatorEnergyComponent() {
		return actuatorEnergyComponent;
	}


	public void setActuatorEnergyComponent(ActuatorEnergyComponent actuatorEnergyComponent) {
		this.actuatorEnergyComponent = actuatorEnergyComponent;
	}


	@Override
	public boolean isAvailable() {
		return actuatorEnergyComponent.isAvailable();
	}


	@Override
	public boolean isActive() {
		return actuatorEnergyComponent.isOn();
	}


	@Override
	public boolean activate() {
		if(actuatorEnergyComponent.turnOn())
		{
			if(actuatorEnergyComponent.getCurrentState().equals(actuatorEnergyComponent.OFF)) 
			{
				actuatorEnergyComponent.doStateChange(actuatorEnergyComponent.HOVER);
				return true;
			}
		}		
		
		return false;
	}


	@Override
	public boolean deactivate() {
		if(!actuatorEnergyComponent.getCurrentState().equals(actuatorEnergyComponent.FLY)) 
		{
			actuatorEnergyComponent.doStateChange(actuatorEnergyComponent.OFF);
			return true;
		}
		
		return false;
	}


	@Override
	public boolean isMoving() {
		return actuatorEnergyComponent.getCurrentState().equals(actuatorEnergyComponent.FLY);
	}


	@Override
	public boolean setMoving() {
		if(actuatorEnergyComponent.isOn()) 
		{
			actuatorEnergyComponent.doStateChange(actuatorEnergyComponent.FLY);
			return true;
		}
		
		return false;
	}


	@Override
	public boolean holdPosition() {
		if(actuatorEnergyComponent.isOn()) 
		{
			actuatorEnergyComponent.doStateChange(actuatorEnergyComponent.HOVER);
			return true;
		}
		
		return false;		
	}

	@Override
	public double energyCalculation(double distance, double speed) {		
		double timeForDistance = Math.ceil(distance / speed);
		double energyNeeded = actuatorEnergyComponent.FLY.getEnergyConsumption() * timeForDistance ;		
		
		return energyNeeded;
	}
	
	@Override
	public double getChargePercent() {
		return battery.getCurrentPercentage();
	}

	@Override
	public boolean turnOff() {
		movementModel.removeTargetLocations(this);
		actuatorEnergyComponent.doStateChange(actuatorEnergyComponent.OFF);
		
		if(!actuatorEnergyComponent.isOn()) 
			return true;
		
		return false;
	}

	@Override
	public double energyCalculationToLocation(Location location) {
		return energyCalculation(calculateFlightDistanceToPoint(location), getMaxMovementSpeed());
	}

	@Override
	public double calculateFlightDistanceToPoint(Location location) {
		return this.getRealPosition().distanceTo(location);
	}

	@Override
	public boolean reachableDestination(Location location, double energylevelAtDestionation) {
		double energyNeeded = energyCalculationToLocation(location);
		double neededCapacity = battery.energyPercentageOfBattery(energyNeeded);
		
		// if the energy level would be below the threshold after moving there, this will not be seen as a reachable destination.
		if(battery.getCurrentPercentage() - neededCapacity < energylevelAtDestionation) 
			return false;
		
		return true;
	}

	@Override
	public void land() {		
		this.deactivate();	
	}

	@Override
	public void moveToLocation(Location location, ReachedLocationCallback cb) {	
		this.setTargetLocation(location.clone());	
		targetLocations.put(location.clone(), cb);
	}

	@Override
	public void moveToLocation(Location location, double speed,
			ReachedLocationCallback cb) {
		this.setMovementSpeed(speed);
		this.moveToLocation(location, cb);		
	}

	@Override
	public Location getCurrentLocation() {
		return getRealPosition();
	}

	@Override
	public RechargableBattery getBattery() {
		return battery;
	}

	@Override
	public void reachedLocation(Location location) {	
		ReachedLocationCallback cb = targetLocations.get(location);		
		targetLocations.remove(location);		
		cb.reachedLocation();	
	}
	
}
