package ui.controller;

import java.awt.Point;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import classes.Category;
import classes.CpsEdge;
import classes.CpsNode;
import classes.AbstractCpsObject;
import classes.HolonElement;
import classes.HolonObject;
import classes.HolonSwitch;
import classes.HolonTransformer;
import classes.IdCounter;
import ui.model.Model;

/**
 * Controller for the Loading.
 * 
 * @author Gruppe14
 */
public class LoadController {
	/**
	 * enum Mode.
	 */
	public enum Mode {
		ALL, CATEGORY, CANVAS
	}

	private Model model;
	private CategoryController cgC;
	private CanvasController cvsC;
	private ObjectController objC;
	private MultiPurposeController mpC;

	/**
	 * Constructor.
	 * 
	 * @param model
	 *            Model
	 * @param cg
	 *            CategoryController
	 * @param cvs
	 *            CanvasController
	 * @param obj
	 *            ObjectController
	 * @param mp
	 *            MultiPurposeController
	 */
	public LoadController(Model model, CategoryController cg, CanvasController cvs, ObjectController obj,
			MultiPurposeController mp) {
		this.model = model;
		this.cgC = cg;
		this.cvsC = cvs;
		this.objC = obj;
		this.mpC = mp;

	}

	/**
	 * Reads the the JSON File and load the state into the Model.
	 * 
	 * @param path
	 *            the Path
	 * @throws IOException
	 *             exception
	 */
	public void readJson(String path) throws IOException {
		JSONParser parser = new JSONParser();
		JSONObject json = new JSONObject();
		try {
			json = (JSONObject) parser.parse(new FileReader(path));
		} catch (ParseException ex) {
			// TODO Auto-generated catch block
			ex.printStackTrace();
		}

		ArrayList<String> obj = new ArrayList<>();
		ArrayList<String> ele = new ArrayList<>();
		ArrayList<String> edge = new ArrayList<>();
		ArrayList<String> gp = new ArrayList<>();

		Mode mode = Mode.valueOf(json.get("MODE").toString());

		if (mode.equals(Mode.ALL) || mode.equals(Mode.CATEGORY)) {
			model.setCgIdx(new HashMap<String, Integer>());
			model.setCategories(new ArrayList<Category>());
		}
		if (mode.equals(Mode.ALL) || mode.equals(Mode.CANVAS)) {
			model.setCvsObjIdx(new HashMap<Integer, Integer>());
			model.setObjectsOnCanvas(new ArrayList<AbstractCpsObject>());
			model.setEdgesOnCanvas(new ArrayList<CpsEdge>());
		}

		for (Object key : json.keySet()) {
			if (key.equals("CG"))
				readCategory((JSONArray) json.get(key));
			else if (key.equals("ID"))
				IdCounter.setCounter(Integer.parseInt(json.get(key.toString()).toString()));
			else if (key.toString().contains("CGO") || key.toString().contains("CVSO"))
				obj.add(key.toString());
			else if (key.toString().contains("CGE") || key.toString().contains("CVSE"))
				ele.add(key.toString());
			else if (key.toString().contains("EDGE"))
				edge.add(key.toString());
			else if (key.toString().contains("CGGP") || key.toString().contains("CVSGP"))
				gp.add(key.toString());

		}

		Collections.sort(obj);
		Collections.sort(ele);
		Collections.sort(edge);
		Collections.sort(gp);

		dispatch(obj, json);
		dispatch(ele, json);
		dispatch(edge, json);
		dispatch(gp, json);

	}

	/**
	 * dispatch the Keys into the right processing.
	 * 
	 * @param input
	 *            input
	 * @param json
	 *            Json
	 */
	public void dispatch(ArrayList<String> input, JSONObject json) {

		for (String str : input) {
			if (str.contains("CGO")) {
				readCategoryObject((JSONArray) json.get(str));
			} else if (str.contains("CVSO")) {
				readCanvasObject((JSONArray) json.get(str));
			} else if (str.contains("CGE") || str.contains("CVSE")) {
				readElement((JSONArray) json.get(str));
			} else if (str.contains("EDGE")) {
				readEdge((JSONArray) json.get(str));
			} else if (str.contains("CGGP") || str.contains("CVSGP")) {
				readElementGraph((JSONArray) json.get(str));
			}
		}

	}

	/**
	 * Read all Categories from the JSON file.
	 * 
	 * @param arr
	 *            JSONArray
	 */
	public void readCategory(JSONArray arr) {

		Iterator<Object> i = arr.iterator();

		while (i.hasNext()) {
			cgC.addNewCategory(next(i));
		}
	}

	/**
	 * Read all Objects in Category from the JSON File.
	 * 
	 * @param arr
	 *            JSON Array
	 */
	public void readCategoryObject(JSONArray arr) {
		Iterator<Object> i = arr.iterator();

		String type = next(i);

		if (type.equals("HolonObject")) {
			cgC.addNewHolonObject(mpC.searchCat(next(i)), next(i), new ArrayList<HolonElement>(), next(i));
		} else if (type.equals("HolonTransformer")) {
			cgC.addNewHolonTransformer(mpC.searchCat(next(i)), next(i), next(i));
		} else if (type.equals("HolonSwitch")) {
			cgC.addNewHolonSwitch(mpC.searchCat(next(i)), next(i), next(i));
		}

	}

	/**
	 * Read all Objects in Canvas from JSON File.
	 * 
	 * @param arr
	 *            JSON Array
	 */
	public void readCanvasObject(JSONArray arr) {
		Iterator<Object> i = arr.iterator();
		AbstractCpsObject cps = null;

		String type = next(i);

		if (type.equals("HolonObject")) {
			cps = new HolonObject(next(i));
		} else if (type.equals("HolonTransformer")) {
			cps = new HolonTransformer(next(i));
		} else if (type.equals("HolonSwitch")) {
			cps = new HolonSwitch(next(i));
		} else if (type.equals("CpsNode")) {
			cps = new CpsNode(next(i));
		}

		cps.setName(next(i));
		cps.setID(Integer.parseInt(next(i)));
		cps.setImage(next(i));
		cps.setPosition(Integer.parseInt(next(i)), Integer.parseInt(next(i)));

		cvsC.addNewObject(cps);
	}

	/**
	 * Read all Elements in Category and Canvas from JSON File.
	 * 
	 * @param arr
	 *            JSON Array
	 */
	public void readElement(JSONArray arr) {
		Iterator<Object> i = arr.iterator();

		String sav = next(i);
		String obj = next(i);

		HolonElement ele = new HolonElement(next(i), Integer.parseInt(next(i)), Float.parseFloat(next(i)));

		if (sav.equals("CVS")) {
			ele.setActive(convert(Integer.parseInt(next(i))));
			objC.addElementIntoCanvasObject((HolonObject) mpC.searchByID(Integer.parseInt(obj)), ele);
		} else
			objC.addElementIntoCategoryObject(sav, obj, ele);
	}

	/**
	 * Read.
	 * 
	 * @param arr
	 *            JSON Array
	 */
	public void readEdge(JSONArray arr) {
		Iterator<Object> i = arr.iterator();

		CpsEdge edge = new CpsEdge(mpC.searchByID(Integer.parseInt(next(i))),
				mpC.searchByID(Integer.parseInt(next(i))));
		edge.setCapacity(Float.parseFloat(next(i)));
		edge.setFlow(Float.parseFloat(next(i)));

		cvsC.addEdgeOnCanvas(edge);
	}

	/**
	 * Read the Element Graph.
	 * 
	 * @param arr
	 *            JSON Array
	 */
	public void readElementGraph(JSONArray arr) {
		Iterator<Object> i = arr.iterator();

		String sav = next(i);
		HolonElement ele;

		if (sav.equals("CVS")) {
			ele = mpC.searchEle((HolonObject) mpC.searchByID(Integer.parseInt(next(i))), next(i));
			while (i.hasNext())
				ele.getGraphPoints().add(new Point(Integer.parseInt(next(i)), Integer.parseInt(next(i))));
		} else {
			ele = mpC.searchEle((HolonObject) mpC.searchCatObj(mpC.searchCat(sav), next(i)), next(i));
			while (i.hasNext())
				ele.getGraphPoints().add(new Point(Integer.parseInt(next(i)), Integer.parseInt(next(i))));
		}

	}

	/**
	 * Get the Next Element from an Iterator.
	 * 
	 * @param i Iterator
	 * @return next Element from the Iterator
	 */
	public String next(Iterator<Object> i) {
		return i.next().toString();
	}

	/**
	 * converting saved Integers into Booleans.
	 * 
	 * @param x integer
	 * @return boolean
	 */
	public boolean convert(int x) {
		return x == 1 ? true : false;
	}

}
