package algorithm.objectiveFunction;

import classes.HolonElement.Priority;
import model.DecoratedNetwork;
import model.DecoratedState;
import model.DecoratedHolonObject.HolonObjectState;
import classes.HolonObject;
import ui.controller.FlexManager.FlexState;
import ui.controller.FlexManager.FlexWrapper;

public class Evaluation {
	
	/**
	 * Calculate the Fitness(Penelty) Value for a state (alias the calculated Position).
	 * TODO: Make me better Rolf.
	 * @param state
	 * @return
	 */
	public static double getFitnessValueForState(DecoratedState state) {
		double fitness = 0.0;
		double nw_fitness =0.0;
		double object_fitness = 0.0;
		double flexFitness = 0.0;
		
		double sigma = 9;
		
		// calculate network_fitness
		for(DecoratedNetwork net : state.getNetworkList()) {
			float production = net.getSupplierList().stream().map(supplier -> supplier.getEnergyToSupplyNetwork()).reduce(0.0f, (a, b) -> a + b);
			float consumption = net.getConsumerList().stream().map(con -> con.getEnergyNeededFromNetwork()).reduce(0.0f, (a, b) -> a + b);
			nw_fitness += Math.abs((production - consumption)/100); //Energy is now everywhere positive
		}
		
		// calculate object_fitness
		for(DecoratedNetwork net : state.getNetworkList()) {

			object_fitness += net.getConsumerList().stream().map(con -> holonObjectSupplyPenaltyFunction(con.getSupplyBarPercentage()) /*+ inactiveHolonElementPenalty(con.getModel())*/).reduce(0.0, (a, b) -> (a + b));
			object_fitness += net.getConsumerList().stream().map(con -> StateToDouble(con.getState())).reduce(0.0, (a,b) -> (a+b));
			//object_fitness += net.getPassivNoEnergyList().stream().map(sup -> inactiveHolonElementPenalty(sup.getModel())).reduce(0.0, (a, b) -> (a + b));
			//object_fitness += net.getSupplierList().stream().map(sup -> inactiveHolonElementPenalty(sup.getModel())).reduce(0.0, (a, b) -> (a + b));
			//object_fitness += net.getConsumerSelfSuppliedList().stream().map(con -> inactiveHolonElementPenalty(con.getModel())).reduce(0.0, (a, b) -> (a + b));

		}
		// calculate flexibility fitness old cost flex
		/*for(FlexWrapper flexWrapper :state.getFlexManager().getAllFlexWrapperWithState(FlexState.IN_USE)) {
			flexFitness += flexWrapper.getFlex().cost / (double)flexWrapper.getFlex().getDuration();
		}*/
		for(FlexWrapper flexWrapper :state.getFlexManager().getAllFlexWrapperWithState(FlexState.IN_USE)) {
			flexFitness += Math.pow(sigma, (double)priorityToInt(flexWrapper.getFlex().getElement().getPriority())) - 1;
		}
		
		fitness = nw_fitness + object_fitness + flexFitness;
		return fitness;
	}

	
	
	
	
	private static int priorityToInt(Priority priority) {
		switch(priority) {
		case Essential:
			return 3;
		case High:
			return 2;
		case Medium:
			return 1;
		case Low:
		default:		
			return 0;
		}
	}





	/**
	 * Untouched:
	 * Function that returns the fitness depending on the number of elements deactivated in a single holon object
	 * @param obj Holon Object that contains Holon Elements
	 * @return fitness value for that object depending on the number of deactivated holon elements
	 */
	@SuppressWarnings("unused")
	private static double inactiveHolonElementPenalty(HolonObject obj) {
		float result = 0;
		int activeElements = obj.getNumberOfActiveElements();
		int maxElements = obj.getElements().size();
		
		//result = (float) Math.pow((maxElements -activeElements),2)*10;
		result = (float) Math.pow(5, 4* ( (float) maxElements - (float) activeElements)/ (float) maxElements) - 1;
		//System.out.console.println("max: " + maxElements + " active: " + activeElements + " results in penalty: " + result);
	return result;
		
	}
	/**
	 * Untouched:
	 * Calculates a penalty value based on the HOs current supply percentage
	 * @param supplyPercentage
	 * @return
	 */
	private static double holonObjectSupplyPenaltyFunction(float supplyPercentage) {
		double result = 0;
		/*if(supplyPercentage == 1)
			return result;
		else if(supplyPercentage < 1 && supplyPercentage >= 0.25) // undersupplied inbetween 25% and 100%
			result = (float) Math.pow(1/supplyPercentage, 2);
		else if (supplyPercentage < 0.25) //undersupplied with less than 25%
			result = (float) Math.pow(1/supplyPercentage,2);
		else if (supplyPercentage < 1.25)  //Oversupplied less than 25%
			result = (float) Math.pow(supplyPercentage,3) ;
		else result = (float) Math.pow(supplyPercentage,4); //Oversupplied more than 25%
		
		
		if(Float.isInfinite(result) || Float.isNaN(result))
			result = 1000;
	*/
		if(supplyPercentage <= 1.0) {
			result = Math.pow(5,(Math.abs((100 - (supplyPercentage*100)))/50 + 2)) - Math.pow(5, 2);
		}
		else {
			result = Math.pow(6,(Math.abs((100 - (supplyPercentage*100)))/50 + 2)) - Math.pow(6, 2);
		}
		if(result > 1000)System.out.println("supplyPenalty(" + supplyPercentage + ")=" + result);
		
		return result;
	}
	/**
	 * If you want to get in touch with a reliable state? Working function not in use currently.
	 * @param state
	 * @return
	 */
	private static double StateToDouble(HolonObjectState state) {
		switch (state) {
		case NOT_SUPPLIED:
			return 150.0;
		case NO_ENERGY:
			return 150.0;
		case OVER_SUPPLIED:
			return 100.0;
		case PARTIALLY_SUPPLIED:
			return 100.0;
		case PRODUCER:
			return 0;
		case SUPPLIED:
			return 0;
		default:
			return 0;
		}
	}
}
