package classes;

import java.awt.Point;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
import java.util.LinkedList;
import java.util.ListIterator;

import com.google.gson.annotations.Expose;
import interfaces.GraphEditable;
import interfaces.LocalMode;
import ui.controller.SingletonControl;
import ui.view.IndexTranslator;
import ui.view.UnitGraph;

/**
 * The class HolonSwitch represents a Switch, which can be turned on and off.
 * 
 * @author Gruppe14
 *
 */
public class HolonSwitch extends AbstractCanvasObject implements LocalMode, GraphEditable {

	/**
	 * The class HolonSwitch represents an Object in the system, that has the
	 * capacity of manipulate the electricity flow. The switch can be manage
	 * automatically through a graph or direct manually.
	 *
	 */
	/*
	 * manual state True, if this wire is working (capable of carrying
	 * electricity), else false
	 */
	@Expose
	boolean manualActive;

	/*
	 * active state True, if this wire is working (capable of carrying
	 * electricity), else false
	 */
	@Expose
	private boolean autoActive;

	/*
	 * true if switch has to be used manually
	 */
	@Expose
	boolean manualMode;
	
	@Expose
	int localPeriod;
	
	@Expose
	boolean localPeriodActive;

	/*
	 * Energy at each point of the graph with 50 predefined points. At the
	 * beginning, it starts with all values at energy
	 */
	boolean[] activeAt;
	// Points on the UnitGraph
	LinkedList<Point2D.Double> graphPoints = new LinkedList<>();

	/**
	 * Create a new HolonSwitch with the default name ("Switch"), a default
	 * value of automatic handle and active status.
	 * 
	 * @param objName
	 *            String
	 */
	public HolonSwitch(String objName) {
		super(objName);
		setUseLocalPeriod(false);
		activeAt=new boolean[LocalMode.STANDARD_GRAPH_ACCURACY];
		setManualState(true);
		setAutoState(true);
		setManualMode(false);
		setGraphPoints(new LinkedList<Point2D.Double>());
		initGraphPoints();
		sampleGraph();
	}

	/**
	 * Create a copy of an existing HolonSwitch.
	 * 
	 * @param obj
	 *            the Object to copy
	 */
	public HolonSwitch(AbstractCanvasObject obj) {
		super(obj);
		System.out.println("SwitchCopy?");
		HolonSwitch copyObj = (HolonSwitch)obj;
		setLocalPeriod(copyObj.getLocalPeriod());
		setUseLocalPeriod(copyObj.isUsingLocalPeriod());
		activeAt=new boolean[LocalMode.STANDARD_GRAPH_ACCURACY];
		super.setName(obj.getName());
		setManualState(copyObj.getManualState());
		setAutoState(true);
		setGraphPoints(new LinkedList<Point2D.Double>());
		for (Point2D.Double p : copyObj.getGraphPoints()) {
			this.graphPoints.add(new Point2D.Double(p.getX(),p.getY()));
		}
		sampleGraph();
		setManualMode(copyObj.getManualMode());
	}

	/**
	 * Calculates the state of the Switch.
	 */
	public void switchState() {
		if (!manualMode) {
			setManualMode(true);
		}
		if (this.manualActive == true) {
			setImage("/Images/switch-off.png");
		} else {
			setImage("/Images/switch-on.png");
		}
		this.manualActive = !manualActive;

	}

	public static String getSwitchClosedImage(){
		return "/Images/switch-on.png";
	}
	
	public static String getSwitchOpenImage(){
		return "/Images/switch-off.png";
	}
	/**
	 * Getter for the status of the Switch at a given timestep.
	 * 
	 * @param timeStep state at given iteration.
	 * @return state value (true if closed/active, false if open)
	 */
	public boolean getState(int timeStep) {
		if (manualMode) {
			return this.manualActive;
		} else {
			return activeAt[IndexTranslator.getEffectiveIndex(this, timeStep)];
		}
	}


	/**
	 * Change the state of the Switch to manual.
	 * 
	 * @param state the State
	 */
	public void setManualState(boolean state) {
		this.manualActive = state;
		setImage();
	}

	/**
	 * Set the state of the Switch to automatic.
	 * 
	 * @param state the State
	 */
	public void setAutoState(boolean state) {
		this.autoActive = state;
		setImage();
	}

	/**
	 * Set Image of the Switch.
	 */
	private void setImage() {
		if (manualMode) {
			if (!this.manualActive) {
				setImage("/Images/switch-off.png");
			} else {
				setImage("/Images/switch-on.png");
			}
		} else {
			if (!this.autoActive) {
				setImage("/Images/switch-off.png");
			} else {
				setImage("/Images/switch-on.png");
			}
		}
	}

	/**
	 * For automatic use only (through the graph).
	 * 
	 * @return the Graph Points
	 */
	public LinkedList<Point2D.Double> getGraphPoints() {
		return graphPoints;
	}

	/**
	 * Set the values of the switch in the graph (auto. mode only).
	 * 
	 * @param linkedList the Graph points
	 */
	public void setGraphPoints(LinkedList<Double> linkedList) {
		this.graphPoints = linkedList;
	}
	/**
	 * Initialize the Graph as a closed Switch.
	 */
	private void initGraphPoints()
	{
		graphPoints.clear();
		graphPoints.add(new Point2D.Double(0.0, 1.0));
		graphPoints.add(new Point2D.Double(1.0, 1.0));
	}
	/**
	 * Returns the ManualState.
	 * 
	 * @return boolean Manual State
	 */
	public boolean getManualState() {
		return this.manualActive;
	}

	/**
	 * Returns the autoActive.
	 * 
	 * @return boolean autoActive
	 */
	public boolean getAutoActive() {
		return autoActive;
	}
	
	/**
	 * Set the overall value of the Switch (manual mode).
	 * 
	 * @param mode
	 *            the mode (boolean)
	 */
	public void setManualMode(boolean mode) {
		manualMode = mode;
	}

	/**
	 * Get manualmode state.
	 * 
	 * @return boolean manual mode state
	 */
	public boolean getManualMode() {
		return manualMode;
	}

	//interfaces.GraphEditable
	@Override
	public Graphtype getGraphType() {
		return Graphtype.boolGraph;
	}

	@Override
	public LinkedList<Double> getStateGraph() {
		return graphPoints;
	}
	

	@Override
	public void reset() {
		initGraphPoints();
		sampleGraph();
	}
	@Override
	public void sampleGraph() {
		activeAt = sampleGraph(100);
	}
	/**
	 * Generate out of the Graph Points a array of boolean that represent the Curve("on or off"-Graph) at each sample position. The Values are in the Range [0,1].
	 * @param sampleLength amount of samplePositions. The positions are equidistant on the Range[0,1].
	 * @return the boolean array of samplepoints.
	 */
	private boolean[] sampleGraph(int sampleLength)
	{
		ListIterator<Point2D.Double> iter = this.graphPoints.listIterator();
		Point.Double before = iter.next();
		Point.Double after = iter.next();
		boolean [] activeTriggerPos = new boolean[sampleLength];	
		for(int i = 0; i<sampleLength ; i++)
		{
			double graphX = (double)i / (double) (sampleLength - 1); //from 0.0 to 1.0
			if(graphX > after.x)
			{
				before = after;
				after = iter.next();
			}		
			activeTriggerPos[i] = (before.getY() >= 0.5);
		}
		return activeTriggerPos;
	}
	
	//interfaces.LocalMode
	@Override
	public void setLocalPeriod(int period) {
		localPeriod=period;
	}
	
	@Override
	public int getLocalPeriod() {
		return localPeriod;
	}
	
	@Override
	public boolean isUsingLocalPeriod() {
		return localPeriodActive;
	}
	
	@Override
	public void setUseLocalPeriod(boolean state) {
		this.localPeriodActive=state;
	}
	
	@Override
	public HolonSwitch makeCopy(){
		return new HolonSwitch(this);
	}
}