package ui.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

import classes.HolonElement;
import classes.HolonObject;
import classes.HolonSwitch;
import classes.TrackedDataSet;
import ui.controller.Control;
import ui.model.Model;

public class StatisticGraph extends JPanel {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	// Maximum y Value
	double maximum = 0;

	// is the Simulation running?
	private boolean isSimRunning;

	// model and controller
	private Model model;
	private Control controller;

	// Graphics2D
	private Graphics2D g2;
	GeneralPath path = new GeneralPath();

	// Data
	public ArrayList<TrackedDataSet> objects = new ArrayList<>();

	/**
	 * Constructor.
	 * 
	 * @param model
	 *            the Model
	 * @param control
	 *            the Controller
	 */
	public StatisticGraph(final Model model, Control control) {
		this.controller = control;
		this.model = model;

		this.setBackground(Color.WHITE);
	}

	/**
	 * Paints all Components on the Canvas.
	 * 
	 * @param g
	 *            Graphics
	 * 
	 */
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		// Graphics2D init
		g2 = (Graphics2D) g;
		RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHints(rh);

		// Paint the Grid
		g2.setStroke(new BasicStroke(0));
		g2.setColor(Color.BLACK);
		for (int i = 0; i <= this.getWidth(); i += 10) {
			g2.drawLine(i, 0, i, this.getHeight());
		}
		for (int i = 0; i <= this.getHeight(); i += 5) {
			g2.drawLine(0, i, this.getWidth(), i);
		}

		isSimRunning = model.getIsSimulation();

		// if sim is on
		if (isSimRunning) {
			g2.setStroke(new BasicStroke(3));

			// Calculate the Maximum
			calcMaximum();

			// Calculate values for each set and add them
			addValues();

			// Create Paths and draw them
			for (TrackedDataSet set : objects) {
				path.reset();
				switch (set.getProperty()) {
				case TrackedDataSet.CONSUMPTION:
				case TrackedDataSet.PRODUCTION:
				case TrackedDataSet.ACTIVATED_ELEMENTS:
				case TrackedDataSet.TOTAL_PRODUCTION:
				case TrackedDataSet.TOTAL_CONSUMPTION:
					createPathFloats(set);
					break;
				case TrackedDataSet.ON_OFF:
					createPathBooleans(set);
					break;
				case TrackedDataSet.PERCENT_SUPPLIED:
				case TrackedDataSet.PERCENT_NOT_SUPPLIED:
				case TrackedDataSet.PERCENT_PARTIAL_SUPPLIED:
					//TODO Percentage Method
					break;
				default:
					break;
				}
				g2.setColor(set.getColor());
				g2.draw(path);

			}
		}

	}

	/**
	 * Add an Object to the Graph if the maximum has not reached yet.
	 * 
	 * @param obj
	 *            the Object to add
	 */
	public void addObject(TrackedDataSet set) {
		objects.add(set);
	}

	/**
	 * Removes an Object from the Graph.
	 * 
	 * @param id
	 *            the id of the Object to remove
	 */
	public void removeObject(int id) {
		objects.remove(id);
	}

	/**
	 * converts the number to fit the canvas.
	 * 
	 * @param d
	 *            the number to convert
	 * @return the converted number
	 */
	public double convertToCanvasY(float d) {
		return Math.abs((this.getHeight() - (d * (this.getHeight() / maximum))));
	}

	/**
	 * Does take into account which timestep is watched, calculates the max
	 * values.
	 * 
	 * @return the currentEnergy
	 */
	public float getEnergyAtCurrentTimeStep(HolonObject obj) {
		float temp = 0;
		for (HolonElement e : obj.getElements()) {
			if (e.getActive()) {
				temp = temp + e.getEnergyAt()[model.getCurIteration()];
			}
		}
		return temp;
	}

	/**
	 * Calculate the Max Value of the Graph
	 */
	public void calcMaximum() {
		maximum = 0;
		for (TrackedDataSet set : objects) {
			int val = 0;
			switch (set.getProperty()) {
			case TrackedDataSet.CONSUMPTION:
				for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) {
					if (h.getEnergy() < 0) {
						val += h.getEnergy() * h.getAmount();
					}
				}
				val *= -1;
				break;
			case TrackedDataSet.PRODUCTION:
				for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) {
					if (h.getEnergy() > 0) {
						val += h.getEnergy() * h.getAmount();
					}
				}

				break;
			case TrackedDataSet.ACTIVATED_ELEMENTS:
				for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) {
					val += h.getAmount();
				}
				break;
			case TrackedDataSet.ON_OFF:
				val = 1;
				break;
			case TrackedDataSet.TOTAL_PRODUCTION:
				//TODO
				break;
			case TrackedDataSet.TOTAL_CONSUMPTION:
				//TODO
				break;
			case TrackedDataSet.PERCENT_SUPPLIED:
			case TrackedDataSet.PERCENT_NOT_SUPPLIED:
			case TrackedDataSet.PERCENT_PARTIAL_SUPPLIED:
				val = 1;
				break;
			default:
				break;
			}
			if (val > maximum) {
				maximum = val;
			}
		}
		((StatisticGraphPanel) this.getParent()).setMaximumLabel(maximum);
	}

	/**
	 * Add the Current Values to each set
	 */
	private void addValues() {
		for (TrackedDataSet set : objects) {
			int val = 0;
			switch (set.getProperty()) {
			case TrackedDataSet.CONSUMPTION:
				for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) {
					if (h.getEnergy() < 0 && h.getActive()) {
						val += Math.abs(h.getEnergyAt()[model.getCurIteration()]) * h.getAmount();
					}
					set.setValAt(val, model.getCurIteration());
				}
				break;
			case TrackedDataSet.PRODUCTION:
				for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) {
					if (h.getEnergy() > 0 && h.getActive()) {
						val += Math.abs(h.getEnergyAt()[model.getCurIteration()]) * h.getAmount();
					}
					set.setValAt(val, model.getCurIteration());
				}

				break;
			case TrackedDataSet.ACTIVATED_ELEMENTS:
				for (HolonElement h : ((HolonObject) set.getCpsObject()).getElements()) {
					if (h.getActive()) {
						val += h.getAmount();
					}
					set.setValAt(val, model.getCurIteration());
				}
				break;
			case TrackedDataSet.ON_OFF:
				if (((HolonSwitch) set.getCpsObject()).getManualMode()) {
					if (((HolonSwitch) set.getCpsObject()).getActiveManual()) {
						set.setValAt(1, model.getCurIteration());
					} else {
						set.setValAt(0, model.getCurIteration());
					}
				} else {
					if (((HolonSwitch) set.getCpsObject()).getActiveAt()[model.getCurIteration()]) {
						set.setValAt(1, model.getCurIteration());
					} else {
						set.setValAt(0, model.getCurIteration());
					}
				}
				break;
			case TrackedDataSet.TOTAL_PRODUCTION:

				break;
			case TrackedDataSet.TOTAL_CONSUMPTION:

				break;
			case TrackedDataSet.PERCENT_SUPPLIED:

				break;
			case TrackedDataSet.PERCENT_NOT_SUPPLIED:

				break;
			case TrackedDataSet.PERCENT_PARTIAL_SUPPLIED:

				break;
			default:
				break;
			}
		}
	}

	/**
	 * create Path with floats
	 * 
	 * @param set
	 */
	private void createPathFloats(TrackedDataSet set) {
		boolean init = true;
		path.moveTo(0, 0);
		for (int i = 0; i < model.getCurIteration(); i++) {
			if (init && set.getValues()[i] != -1) {
				path.moveTo(i * this.getWidth() / model.getIterations() - 1, convertToCanvasY(set.getValues()[i]));
				init = false;
			}
			if (!init) {
				if (set.getValues()[i + 1] != -1) {
					path.lineTo((i + 1) * this.getWidth() / model.getIterations(),
							convertToCanvasY(set.getValues()[i + 1]));
				} else {
					break;
				}
			}

		}
	}

	/**
	 * create Path with booleans(0 and 1)
	 * 
	 * @param set
	 */
	private void createPathBooleans(TrackedDataSet set) {
		boolean init = true;
		for (int i = 0; i < model.getCurIteration(); i++) {
			if (init && set.getValues()[i] != -1) {
				path.moveTo(i * this.getWidth() / model.getIterations() - 1,
						convertToCanvasY((float) (set.getValues()[i] * maximum)));
				init = false;
			}
			if (!init) {
				if (set.getValues()[i + 1] != -1) {
					path.lineTo((i + 1) * this.getWidth() / model.getIterations(),
							convertToCanvasY((float) (set.getValues()[i + 1] * maximum)));
				} else {
					break;
				}
			}
		}
	}

}