package ui.controller;

import java.awt.Color;
import java.awt.Point;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import cpsAlgorithm.*;

import classes.Category;
import classes.CpsEdge;
import classes.CpsNode;
import classes.CpsUpperNode;
import classes.AbstractCpsObject;
import classes.HolonElement;
import classes.HolonObject;
import classes.HolonSwitch;
import interfaces.CategoryListener;
import ui.model.Model;
import ui.view.MyCanvas;

/**
 * The Class represents the controller in the model, controller view Pattern.
 * 
 * @author Gruppe14
 *
 */
public class Control {

	private Model model;

	private final ConsoleController consoleController;
	private final MultiPurposeController multiPurposeController;
	private final CategoryController categoryController;
	private final ObjectController objectController;
	private final CanvasController canvasController;
	private final GlobalController globalController;
	private final StoreController storeController;
	private final LoadController loadController;
	private final AutoSaveController autoSaveController;
	private SimulationManager simulationManager;
	private final StatsController statsController;
	private final NodeController nodeController;
	private String autoPath = "";

	/**
	 * Constructor.
	 * 
	 * @param model
	 *            the Model
	 */
	public Control(Model model) {
		this.model = model;

		this.multiPurposeController = new MultiPurposeController(model);
		this.categoryController = new CategoryController(model, multiPurposeController);
		this.objectController = new ObjectController(model, multiPurposeController);
		this.canvasController = new CanvasController(model, multiPurposeController);
		this.globalController = new GlobalController(model);
		this.storeController = new StoreController(model);
		this.loadController = new LoadController(model, categoryController, canvasController, objectController, multiPurposeController);
		this.simulationManager = new SimulationManager(model);
		this.autoSaveController = new AutoSaveController(model);
		this.consoleController = new ConsoleController(model);
		this.statsController = new StatsController(model);
		this.nodeController = new NodeController(model, canvasController, multiPurposeController);
		autoPath = System.getProperty("user.home") + "/HolonGUI/Autosave/";
		File dest = new File(autoPath);
		// deleteDirectory(dest);
		dest.mkdirs();
		try {
			autoSave();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Delete a Directory.
	 * 
	 * @param path
	 *            to delete
	 */
	public void deleteDirectory(File path) {
		if (path.exists()) {
			File[] files = path.listFiles();
			for (int i = 0; i < files.length; i++) {
				if (files[i].isDirectory()) {
					deleteDirectory(files[i]);
				} else {
					files[i].delete();
				}
			}
			path.delete();
		}
	}

	/* Operations for searching */

	/**
	 * Search for Object by ID.
	 * 
	 * @param id
	 *            the id of the Object
	 * @return the CpsObject
	 */
	public AbstractCpsObject searchByID(int id) {
		return multiPurposeController.searchByID(id);
	}

	/**
	 * Search for Object by ID in upperNode
	 * 
	 * @param id
	 *            the id of the Object
	 * @return the CpsObject
	 */
	public AbstractCpsObject searchByIDUpperNode(int id, CpsUpperNode upperNode) {
		return multiPurposeController.searchByIDUpperNode(id, upperNode);
	}

	/**
	 * Search for Object in a Category.
	 * 
	 * @param category
	 *            name of the Category
	 * @param object
	 *            Name of the Object
	 * @return The Object
	 */
	public AbstractCpsObject searchCategoryObject(String category, String object) {
		return multiPurposeController.searchCatObj(multiPurposeController.searchCat(category), object);
	}

	/**
	 * search for category.
	 * 
	 * @param cat
	 *            name of the Category
	 * @return the Category
	 */
	public Category searchCategory(String cat) {
		return multiPurposeController.searchCat(cat);
	}

	/* Operations for Categories and Objects */

	/**
	 * init default category and objects.
	 */
	public void resetCategorys() {
		categoryController.initCategories();
	}

	/**
	 * Adds New Category into Model.
	 * 
	 * @param cat
	 *            name of the new Category
	 */
	public void addCategory(String cat) {
		categoryController.addNewCategory(cat);
	}

	/**
	 * Add new Holon Object to a Category.
	 * 
	 * @param cat
	 *            Category
	 * @param obj
	 *            New Object Name
	 * @param ele
	 *            Array of Elements
	 * @param img
	 *            the image Path
	 */
	public void addObject(Category cat, String obj, ArrayList<HolonElement> ele, String img) {
		categoryController.addNewHolonObject(cat, obj, ele, img);
	}

	/**
	 * Add new Holon Transformer to a Category.
	 * 
	 * @param cat
	 *            Category
	 * @param obj
	 *            New Object Name
	 */
	public void addTransformer(Category cat, String obj) {
		categoryController.addNewHolonTransformer(cat, obj, "/Images/transformer-1.png");
	}

	/**
	 * Add new Holon Switch to a Category.
	 * 
	 * @param cat
	 *            Category
	 * @param obj
	 *            New Object Name
	 */
	public void addSwitch(Category cat, String obj) {
		categoryController.addNewHolonSwitch(cat, obj, "/Images/switch-on.png");
	}

	/**
	 * delete a given Category.
	 * 
	 * @param cat
	 *            the Category
	 */
	public void deleteCategory(String cat) {
		categoryController.deleteCategory(cat);
	}

	/**
	 * Delete an Object from a Category.
	 * 
	 * @param cat
	 *            the Category
	 * @param obj
	 *            the Object
	 */
	public void delObjectCategory(String cat, String obj) {
		categoryController.deleteObject(cat, obj);
	}

	/**
	 * deletes a selectedObject.
	 * 
	 * @param obj
	 *            Cpsobject
	 */
	public void deleteSelectedObject(AbstractCpsObject obj) {
		objectController.deleteSelectedObject(obj);
	}

	/**
	 * add an Object to selectedObject.
	 * 
	 * @param obj
	 *            AbstractCpsobject
	 */
	public void addSelectedObject(AbstractCpsObject obj) {
		objectController.addSelectedObject(obj);
	}

	/* Operations for Canvas */

	/**
	 * Add an edge to the Canvas.
	 * 
	 * @param edge
	 *            the edge
	 */
	public void addEdgeOnCanvas(CpsEdge edge) {
		canvasController.addEdgeOnCanvas(edge);
		try {
			autoSave();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Removes an Edge from the Canvas.
	 * 
	 * @param edge
	 *            the edge to remove
	 */
	public void removeEdgesOnCanvas(CpsEdge edge) {
		canvasController.removeEdgesOnCanvas(edge);
		try {
			autoSave();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Set the selected Edge.
	 * 
	 * @param edge
	 *            that is selected
	 */
	public void setSelecteEdge(CpsEdge edge) {
		model.setSelectedEdge(edge);
	}

	/**
	 * Add a new Object.
	 * 
	 * @param object
	 *            the Object
	 */
	public void addObjectCanvas(AbstractCpsObject object) {
		canvasController.addNewObject(object);
		if (!(object instanceof CpsNode)) {
			try {
				autoSave();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * Returns the ID of the selected Object 0 = no Object is selected.
	 * 
	 * @param id
	 *            the ID of the selected Object
	 */
	public void setSelectedObjectID(int id) {
		objectController.setSelectedObjectID(id);
	}

	/**
	 * Deletes an CpsObject on the Canvas and its connections.
	 * 
	 * @param obj
	 *            AbstractCpsObject
	 */
	public void delCanvasObject(AbstractCpsObject obj) {
		canvasController.deleteObjectOnCanvas(obj);
		if (obj instanceof CpsUpperNode)
			canvasController.bfsNodeCleaner((CpsUpperNode) obj);
		try {
			autoSave();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/* Operations for Objects and Elements */

	/**
	 * Add a new Element into a Object on the Canvas.
	 * 
	 * @param id
	 *            the Object ID
	 * @param ele
	 *            the Name of the Element
	 * @param amount
	 *            the Amount
	 * @param energy
	 *            the Energy
	 */
	public void addElementCanvasObject(int id, String ele, int amount, float energy) {
		objectController.addNewElementIntoCanvasObject(id, ele, amount, energy);
	}

	/**
	 * Add a new Element into a Object in Category.
	 * 
	 * @param catName
	 *            the Category
	 * @param objName
	 *            the Object
	 * @param eleName
	 *            the Element Name
	 * @param amount
	 *            the amount
	 * @param energy
	 *            the Energy
	 */
	public void addElementCategoryObject(String catName, String objName, String eleName, int amount, float energy) {
		objectController.addNewElementIntoCategoryObject(catName, objName, eleName, amount, energy);
	}

	/**
	 * deletes a Element from a given Canvas Object.
	 * 
	 * @param id
	 *            the ID
	 * @param elementid
	 *            the Element ID
	 */
	public void deleteElementCanvas(int id, int elementid) {
		objectController.deleteElementInCanvas(id, elementid);
	}

	/**
	 * deletes a Element from a given Object.
	 * 
	 * @param obj
	 *            the Oject
	 * @param ele
	 *            the Element
	 */
	public void deleteElementCanvas(HolonObject obj, HolonElement ele) {
		objectController.deleteElement(obj, ele);
	}

	/**
	 * Sets the ClipboardObjects.
	 * 
	 * @param list
	 *            Array of Objects
	 */
	public void setClipboardObjects(ArrayList<AbstractCpsObject> list) {
		model.setClipboradObjects(list);
	}

	/* Global Operations */

	/**
	 * Add a SubNetColor.
	 * 
	 * @param c
	 *            the Color
	 */
	public void addSubNetColor(Color c) {
		globalController.addSubNetColor(c);
	}

	/**
	 * Returns SCALE.
	 * 
	 * @return SCALE
	 */
	public int getScale() {
		return globalController.getScale();
	}

	/**
	 * Returns SCALE Divided by 2.
	 * 
	 * @return SCALE Divided by 2
	 */
	public int getScaleDiv2() {
		return globalController.getScaleDiv2();
	}

	/**
	 * Changes the value of SCALE and SCALEDIV2.
	 * 
	 * @param s
	 *            Scale
	 */
	public void setScale(int s) {
		globalController.setScale(s);
	}

	/**
	 * sets the current Iteration.
	 * 
	 * @param curit
	 *            the current Iteration
	 */
	public void setCurIteration(int curit) {
		globalController.setCurIteration(curit);
	}

	/**
	 * Writes the current State of the Modelling into a JSON File which can be
	 * loaded.
	 * 
	 * @param path
	 *            the Path
	 * 
	 * @throws IOException
	 *             exception
	 */
	public void saveFile(String path) throws IOException {
		storeController.writeSaveFile(path);
	}

	/**
	 * Reads the the JSON File and load the state into the Model.
	 * 
	 * @param path
	 *            the Path
	 * @throws IOException
	 *             exception
	 */
	public void loadFile(String path) throws IOException {
		loadController.readJson(path);
	}

	/**
	 * Init the CategoryListener.
	 * 
	 * @param catLis
	 *            the CategoryListener
	 */
	public void initListener(CategoryListener catLis) {
		categoryController.addCategoryListener(catLis);
	}

	/**
	 * calculates the flow of the edges and the supply for objects for the
	 * current Timestep.
	 */
	public void calculateStateForCurrentTimeStep() {
		simulationManager.reset();
		simulationManager.calculateStateForTimeStep(model.getCurIteration());
	}

	/**
	 * calculates the flow of the edges and the supply for objects.
	 * 
	 * @param x
	 *            current Iteration
	 */
	public void calculateStateForTimeStep(int x) {
		simulationManager.reset();
		simulationManager.calculateStateForTimeStep(x);
	}

	/**
	 * Set the Canvas.
	 * 
	 * @param can
	 *            the Canvas
	 */
	public void setCanvas(MyCanvas can) {
		simulationManager.setCanvas(can);
	}

	/**
	 * make an autosave.
	 * 
	 * @throws IOException
	 *             Exception
	 */
	public void autoSave() throws IOException {
		autoSaveController.increaseAutoSaveNr();
		storeController.writeAutosaveFile(autoPath + autoSaveController.getAutoSaveNr());
		if (autoSaveController.allowed()) {
			new File(autoPath + (autoSaveController.getAutoSaveNr() - globalController.getNumbersOfSaves())).delete();
		}
	}

	/**
	 * Returns the undo save.
	 * 
	 * @return the undo save
	 */
	public String getUndoSave() {
		autoSaveController.decreaseAutoSaveNr();
		if (!new File(autoPath + (autoSaveController.getAutoSaveNr())).exists()) {
			autoSaveController.increaseAutoSaveNr();
		}
		return autoPath + (autoSaveController.getAutoSaveNr());
	}

	/**
	 * Returns the redo save.
	 * 
	 * @return the redo save
	 */
	public String getRedoSave() {
		autoSaveController.increaseAutoSaveNr();
		if (!new File(autoPath + (autoSaveController.getAutoSaveNr())).exists()) {
			autoSaveController.decreaseAutoSaveNr();
		}
		return autoPath + (autoSaveController.getAutoSaveNr());
	}

	/**
	 * Copy all Selected Objects.
	 */
	public void copyObjects() {
		canvasController.copyObjects();
	}

	/**
	 * Paste all Selected Objects.
	 * 
	 * @param point
	 *            the mouse Position
	 */
	public void pasteObjects(Point point) {
		canvasController.pasteObjects(point);
		try {
			autoSave();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Cut all Selected Objects.
	 */
	public void cutObjects() {
		canvasController.cutObjects();
		try {
			autoSave();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Getter for Model.
	 * 
	 * @return the Model
	 */
	public Model getModel() {
		return model;
	}

	/**
	 * get the Simulation Manager.
	 * 
	 * @return the Simulation Manager
	 */
	public SimulationManager getSimManager() {
		return simulationManager;
	}

	/**
	 * Getter for selected CpsObject.
	 *
	 * @param text
	 *            String the Text
	 * @param color
	 *            the color of the Text
	 * @param p
	 *            size of the Text
	 * @param bold
	 *            bold or not
	 * @param italic
	 *            italic or not
	 * @param nl
	 *            new line or not
	 * 
	 */
	public void addTextToConsole(String text, Color color, int p, boolean bold, boolean italic, boolean nl) {
		consoleController.addTextToConsole(text, color, p, bold, italic, nl);
	}

	/**
	 * Print Text on the console in black and font size 12.
	 *
	 * @param text
	 *            String the Text
	 */
	public void addTextToConsole(String text) {
		consoleController.addTextToConsole(text);
	}

	/**
	 * Clears the console.
	 */
	public void clearConsole() {
		consoleController.clearConsole();
	}

	/**
	 * Set the timerSpeed.
	 * 
	 * @param t
	 *            interval in ms
	 */
	public void setTimerSpeed(int t) {
		globalController.setTimerSpeed(t);
	}

	/**
	 * Set if its simulating or not.
	 * 
	 * @param b
	 *            isSimulation
	 */
	public void setIsSimulation(boolean b) {
		globalController.setIsSimulation(b);
	}

	/**
	 * Set the Canvas X Size.
	 * 
	 * @param canvasX
	 *            the cANVAS_X to set
	 */
	public void setCanvasX(int canvasX) {
		globalController.setCanvasX(canvasX);
	}

	/**
	 * Set the Canvas Y Size.
	 * 
	 * @param canvasY
	 *            the cANVAS_Y to set
	 */
	public void setCanvasY(int canvasY) {
		globalController.setCanvasY(canvasY);
	}

	public void setMaxCapacity(float cap) {
		globalController.setMaxCapacity(cap);
	}

	/**
	 * Set the Algorithm.
	 * 
	 * @param obj
	 *            the Algorithm
	 */
	public void setAlgorithm(Object obj) {
		multiPurposeController.setAlgorithm(obj);
	}

	/**
	 * Run the Algorithm.
	 */
	public void runAlgorithm(Model model, Control controller) {
		if (model.getAlgorithm() != null) {
			((CpsAlgorithm) model.getAlgorithm()).RunAlgorithm(model, controller);
		}
	}

	// ========================= MANAGING TRACKED OBJECTS ====================

	public void setTrackingObj(ArrayList<AbstractCpsObject> objArr) {
		statsController.setTrackingObj(objArr);
	}

	public ArrayList<AbstractCpsObject> getTrackingObj() {
		return statsController.getTrackingObj();
	}

	public void addTrackingObj(AbstractCpsObject obj) {
		statsController.addTrackingObj(obj);
	}

	public void removeTrackingObj(AbstractCpsObject obj) {
		statsController.removeTrackingObj(obj);
	}

	// ========================== MANAGING TRACKED OBJECTS END ================

	/**
	 * Controlling Nodes of Nodes
	 */

	public void addUpperNode(String nodeName, CpsUpperNode upperNode, ArrayList<AbstractCpsObject> toGroup) {
		nodeController.doUpperNode(nodeName, upperNode, toGroup);
	}

	public void delUpperNode(CpsUpperNode node, CpsUpperNode upperNode) {
		nodeController.undoUpperNode(node, upperNode);
	}

	public void addObjUpperNode(AbstractCpsObject object, CpsUpperNode upperNode) {
		nodeController.addObjectInUpperNode(object, upperNode);
	}

	public void delObjUpperNode(AbstractCpsObject object, CpsUpperNode upperNode) {
		nodeController.deleteObjectInUpperNode(object, upperNode);
		if (object instanceof CpsUpperNode)
			canvasController.bfsNodeCleaner((CpsUpperNode) object);
	}

	public void addEdgeUpperNode(CpsEdge edge, CpsUpperNode upperNode) {
		nodeController.addEdge(edge, upperNode);
	}

	public void delEdgeUpperNode(CpsEdge edge, CpsUpperNode upperNode) {
		nodeController.deleteEdge(edge, upperNode);
	}

	public void connectNodes(CpsEdge edge, CpsUpperNode upperNode) {
		nodeController.connectNodes(edge, upperNode);
	}

	public void disconnectNodes(CpsEdge edge, CpsUpperNode upperNode) {
		nodeController.disconnectNodes(edge, upperNode);
	}

	/**
	 * Get the number of HolonObjects in the given List
	 * 
	 * @param list
	 */
	public int getNumberHolonObjects(ArrayList<AbstractCpsObject> list) {
		return objectController.getNumberHolonObjects(list);
	}

	/**
	 * Get the number of HolonObjects with the given state in the given List
	 * 
	 * @param list
	 * @param state
	 */
	public int getNumberStateObjects(ArrayList<AbstractCpsObject> list, int state) {
		return objectController.getNumberStateObjects(list, state);
	}

	/**
	 * Changes the value of HolonBodySCALE
	 * 
	 * @param s
	 *            HolonBodyScale
	 */
	public void setHolonBodyScale(int s) {
		globalController.setHolonBodyScale(s);
	}

	/**
	 * Returns HolonBodySCALE.
	 * 
	 * @return SCALE
	 */
	public int getHolonBodyScale() {
		return globalController.getHolonBodyScale();
	}

	/**
	 * Sets the ID of the selected HolonBody
	 * 
	 * @param i
	 *            ID of the selected HolonBody
	 */
	public void addSelectedHolonBody(int i) {
		objectController.addSelectedHolonBody(i);
	}

}