package cz.vutbr.feec.libraryClasses;

import ij.ImagePlus;
import ij.process.BinaryProcessor;
import ij.process.ByteProcessor;

import java.io.File;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Vector;

import cz.vutbr.feec.graph.*;
import cz.vutbr.feec.gui.ProgressBarRT;
import cz.vutbr.feec.newGui.MainGuiController;
import cz.vutbr.feec.project.GlobalParameters;
import cz.vutbr.feec.stockElements.AbstractStockCell;
import cz.vutbr.feec.stockElements.CoridorCell;
import cz.vutbr.feec.stockElements.Stock;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class GraphCreatingMethods {

	private static int CORIDOR = 0;
	private static Stock stock = Stock.getInstance();

	private GraphCreatingMethods() {
		// soukromy bezparametricky konstruktor
	}

	public static void createDijkstraAndRT() {
		
		
		Dijkstra d = new Dijkstra(stock.getGraph());
		stock.setDijkstra(d);
		// Faster search for paths using precomputed RoutingTable
		// vytvorim tabulku pre rychlejsie hladanie
		// nastavim ju ako atribut stocku - jedinecna pre aplikaciu, vzdy sa
		// zmeni pri zmene grafu

		System.out.println("creating routing table");

		
		  RoutingTable rt = new RoutingTable(stock.getGraph());
		  stock.setRoutingTable(rt);
		 
		  
		  
		// ukazka ulozeni tabulky na disk a ziskani tabulky z disku
		 //zoberem cestu k suboru, kde je ulozeny projekt
		
		// rt = RoutingTable.loadFromFile("file.rt");
		/*
		 * FixedPoints frame = new FixedPoints(g); frame.pack();
		 * frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		 * frame.setVisible(true);
		 */

	}

	public static void createGraph() {
		Vector<Path> paths = stock.getPaths();
		for (int i = 0; i < paths.size(); i++) {
			// uz je definovane v konstruktore - necham ale radsej zakomentovane
			/*
			 * nameFirst = String
			 * .valueOf(path.get(i).getFirstRouter().getxInField()) + "," +
			 * String.valueOf(path.get(i).getFirstRouter().getyInField());
			 * //pomenujem firstRouter
			 * 
			 * //path.get(i).getFirstRouter().setData(nameFirst);
			 */

			// paths.get(i).addNode(paths.get(i).getFirstRouter());
			if (paths.get(i).getSecondRouter() != null) {
				// meno je z konstruktoru
				/*
				 * nameSecond = String.valueOf(path.get(i).getSecondRouter()
				 * .getxInField()) + "," +
				 * String.valueOf(path.get(i).getSecondRouter() .getyInField());
				 * System.out.println("second router ok ");
				 * 
				 * 
				 * path.get(i).getSecondRouter().setData(nameSecond);
				 */
				// paths.get(i).addNode(paths.get(i).getSecondRouter());
				stock.getGraph().add(paths.get(i).getFirstRouter(),
						paths.get(i).getSecondRouter(), paths.get(i).getCost());
				System.out.println("pridane do grafu " + i);
			}
		}
		
	}

	public static void createPath(BinaryProcessor binp, Router r, int x, int y) {

		int routerx = r.getxInField();
		int routery = r.getyInField();
		// zaciatocny router prefarbim na inu farbu
		/*
		new ImagePlus("skuska", binp).show();
		*/
		binp.set(routerx, routery, 200);
		// prefarbim celu oblast okolo routra aby sa nehladali susedne cesty
		// ktore idu z routra
		// potrebujem si tu vytvorit zoznam buniek ktore su cesty a su okolo
		// routra
		LinkedList<AbstractStockCell> cellsAroundRouter = new LinkedList<>();
		// ten list by mohol stacit aj typu CoridorCell - mali by sa prehladavat
		// len tie
		for (int i = routerx - 1; i < routerx + 2; i++) {
			for (int j = routery - 1; j < routery + 2; j++) {
				if (binp.get(i, j) == CORIDOR) {
					cellsAroundRouter.add(stock.getFieldIndex(i, j));
					binp.set(i, j, 200);
				}
			}
		}
		// hladanie cesty pokial nenarazi na druhy router
		// generovanie len moznych poli, bez navratu
		// BFS
		LinkedList<AbstractStockCell> open = new LinkedList<>();
		// aj tu by malo stacit len CoridorCell
		// pridaj vychozi bod do open
		open.add(stock.getFieldIndex(x, y));

		// vytvorit objekt cestu + firstRouter+prva bunka
		Path path = new Path(stock.getGraph());
		path.addCellToPath(stock.getFieldIndex(x, y));
		// cestu pridat do skladu
		stock.getPaths().add(path);
		path.setFirstRouter(r);
		path.setCost(stock.getDelimiter() / GlobalParameters.meterPerSecond);

		while (!open.isEmpty()) {

			// pokial ma kam ist ta cesta, tak pridavat prvky + inkrementovat
			// metriku
			// nevracat sa ale k first routru
			x = open.getFirst().getX();
			y = open.getFirst().getY();
			System.out.println(open.getFirst().getX() + " "
					+ open.getFirst().getY());
			// vygenerovat vsetky stavy kam pokracovat
			// musi skocit minimalne o jednu bunku od routra
			// smer by som mohol mat z metody

			// detekovat prvy krok od routra
			// to je prvy krok, to znamena inak s tym narabat ako s ostatnymi,
			// aby sa nekrizili cesty u routru

			// vymazat z fronty a pridat do navstivenych

			open.remove(open.getFirst());

			// vygenerovat vsetky smery a pridat do open - mala by to byt len
			// jedna bunka na zaklade skeletonizace
			int counter = 0;
			AbstractStockCell tmp = null;
			// cyklus na vyhladanie aj uhlopriečnych susednych buniek
			for (int i = x - 1; i < x + 2; i++) {
				for (int j = y - 1; j < y + 2; j++) {
					counter++;

					try {
						// mozno netreba vytvarat closed mnozinu, iba staci tu
						// bunku prepisat
						// ak najdem cestu na susednej bunke
						if (binp.get(i, j) == CORIDOR) {
							tmp = stock.getFieldIndex(i, j);
							// path.AddCellToPath(s.getFieldIndex(i,
							// j));
							// inkrementujem o jedna
							// path.setMetric(path.getMetric() + 1);
							// pridaj do open
							open.add(stock.getFieldIndex(i, j));
							// zmen jej farbu
							// binp.set(i, j, 200);
							System.out.println("nasiel som cestu");
							// problem pri poslednom policku v cyklu, prida sa
							// do open
							// ale cyklus konci, takze z tohto ifu sa nedostane
							// na pridanie do
							// cesty a zafarbenie pola - zacyklenie
							// pridavam osetrenie resp. ak to najde v poslednom
							// kroku nech sa rovno prida
							// do cesty
							if (counter == 9) {
								path.addCellToPath(tmp);
								// inkrementujem o jedna
								path.setCost(path.getCost()
										+ (stock.getDelimiter() / GlobalParameters.meterPerSecond));

								// tu treba inkrementovat aj o delimiter

								// zmen jej farbu
								binp.set(tmp.getX(), tmp.getY(), 200);

							}
						}
						// pokial narazi na router, cesta konci a do cesty sa
						// prida secondRouter

						else if (binp.get(i, j) == 128) {
							path.setSecondRouter(stock.getRouter(i, j));
							// podmienka ci to nie je ten isty router je
							// vyriesena farbou
							// celu frontu vyprazdnit
							open.removeAll(open);
							// navrat z metody
							System.out.println("nasiel som router");
							// tu zase zafarbit router
							// zafarbit spravne celu oblast zpatky - na to
							// potrebujem prejst zoznam
							// ciest okolo routra a vyfarbit cesty
							// metricu vydelim hodnou MS
							path.setCost(path.getCost()
									/ GlobalParameters.meterPerSecond);
							
							for (int j2 = 0; j2 < cellsAroundRouter.size(); j2++) {
								binp.set(cellsAroundRouter.get(j2).getX(),
										cellsAroundRouter.get(j2).getY(),
										CORIDOR);
							}
							
							binp.set(routerx, routery, 128);
							return;
						}

						// pokial ani uhlopriecka nie je, tak mame cestu len
						// s jednym routrom
						else {
							// musi sa ceknut az nakonci po vsetkych moznostiach
							if (counter == 9 && open.isEmpty()) {
								open.removeAll(open);
								System.out.println("cesta s jednym routrom");
								// tu zase zafarbit router
								// binp.set(cesta.getFirst().getX(),
								// cesta.getFirst().getY(), 128);
								for (int j2 = 0; j2 < cellsAroundRouter.size(); j2++) {
									binp.set(cellsAroundRouter.get(j2).getX(),
											cellsAroundRouter.get(j2).getY(),
											CORIDOR);
								}
								binp.set(routerx, routery, 128);
								return;
								// podmienka ak by sa narazilo na router ale
								// predtym este bola najdena cesta - je iba v
								// open a v tmp, zatial neprefarbena

							} else if (counter == 9 && !open.isEmpty()) {
								path.addCellToPath(tmp);
								// inkrementujem o jedna
								path.setCost(path.getCost()
										+ (stock.getDelimiter()/GlobalParameters.meterPerSecond));
								// aj inkrementovat o delimiter kvoli realnej
								// vzdialenosti

								// zmen jej farbu
								binp.set(tmp.getX(), tmp.getY(), 200);

							}

						}
					} catch (Exception e) {
						System.out.println("exception v create paths");
					}
				}

			}

		}

	}

	// vracia cislo cesty podla bunky na ktoru sa kliklo
	public static int getPathNumberByClick(int x, int y) {
		// musim najst v ktorej ceste sa dana bunka nachadza
		Vector<Path> paths = stock.getPaths();
		for (int i = 0; i < paths.size(); i++) {
			if (paths.get(i).contains(stock.getFieldIndex(x, y))) {
				return i;
			}
		}
		return -1;

	}

	public static int getCellIndexInPath(int pathNumber, int x, int y) {

		Vector<AbstractStockCell> path = stock.getPaths().get(pathNumber)
				.getPath();
		int counter = 0;
		
		for (int j = 0; j < path.size(); j++) {
			if (path.get(j).getX() == x && path.get(j).getY() == y) {
				return counter;
			} else
				counter++;
		}
		//ak sa nenachadza
		if(counter == path.size()){
			return -1;
		}
		return counter;
		

	}

	public static int getMetricToFirstRouter(int counter) {
		return (counter + 1) * (stock.getDelimiter()/GlobalParameters.meterPerSecond);
	}

	public static int getMetricToSecondRouter(int pathNumber, int counter) {
		Vector<AbstractStockCell> path = stock.getPaths().get(pathNumber)
				.getPath();
		return (path.size() - counter) * (stock.getDelimiter()/GlobalParameters.meterPerSecond);

	}

	public static Path findPathBetweenRouters(Router r1, Router r2) {
		float factor = (float) 1.2;
		// factor = o kolik procent muze byt povolena delka cest oproti
		// nejkratsi?
		// Search for paths
		Dijkstra d = stock.getDijkstra();

		/*
		 * String start = String.valueOf(r1.getxInField()) + "," +
		 * String.valueOf(r1.getyInField()); String cil =
		 * String.valueOf(r2.getxInField()) + "," +
		 * String.valueOf(r2.getyInField());
		 */
		// LinkedList<Path> p = d.getPaths(r1.getData(), r2.getData(), factor);
		LinkedList<Path> p = d.getPaths(r1, r2, factor);
		System.out.println("Nejkratsi cesta " + p + "...");
		// vyparsovanie zo stringu
		String s = String.valueOf(p.get(0));

		System.out.println(s);
		// System.out.println("cesta pomocou routing table");
		// stock.getRoutingTable().getPaths()(r1, r2);
		System.out.println("cost = " + p.get(0).getCost());
		return p.get(0);

	}

	public static String findPath(int xStart, int yStart, int xDst, int yDst) {
		// start
		int startPathNumber = GraphCreatingMethods.getPathNumberByClick(xStart,
				yStart);
		int startIndexInPath = GraphCreatingMethods
				.getMetricToFirstRouter(GraphCreatingMethods
						.getCellIndexInPath(startPathNumber, xStart, yStart));
		int startMetricToFirst = GraphCreatingMethods
				.getMetricToFirstRouter(startIndexInPath);
		int startMetricToSecond = GraphCreatingMethods.getMetricToSecondRouter(
				startPathNumber, startIndexInPath);
		// ciel
		int dstPathNumber = GraphCreatingMethods.getPathNumberByClick(xDst,
				yDst);
		int dstIndexInPath = GraphCreatingMethods
				.getMetricToFirstRouter(GraphCreatingMethods
						.getCellIndexInPath(dstPathNumber, xDst, yDst));
		int dstMetricToFirst = GraphCreatingMethods
				.getMetricToFirstRouter(dstIndexInPath);
		int dstMetricToSecond = GraphCreatingMethods.getMetricToSecondRouter(
				dstPathNumber, dstIndexInPath);

		// potrebujem skusit vsetky kombinacie medzi routrami + metriky od danej
		// bunky k routru

		Router r1start = stock.getPaths().get(startPathNumber).getFirstRouter();
		Router r2start = stock.getPaths().get(startPathNumber)
				.getSecondRouter();

		Router r1dst = stock.getPaths().get(dstPathNumber).getFirstRouter();
		Router r2dst = stock.getPaths().get(dstPathNumber).getSecondRouter();
		// tato cesta musi byt vzdy, inak moze byt second router null
		Path path = GraphCreatingMethods.findPathBetweenRouters(r1start, r1dst);
		int cost = (int) (path.getCost() + startMetricToFirst + dstMetricToFirst);
		Path tmp = null;
		int tmpCost;

		if (r2dst != null) {
			tmp = GraphCreatingMethods.findPathBetweenRouters(r1start, r2dst);
			tmpCost = (int) (tmp.getCost() + startMetricToFirst + dstMetricToSecond);
			// ak je metrika cez druhy dst router mensia, tak sa ulozi ta cesta
			if (cost > tmpCost) {
				path = tmp;
				cost = tmpCost;
			}
		}
		// pozriet ci v prvej ceste je second router
		if (r2start != null) {
			tmp = GraphCreatingMethods.findPathBetweenRouters(r2start, r1dst);
			tmpCost = (int) (tmp.getCost() + startMetricToSecond + dstMetricToFirst);
			if (cost > tmpCost) {
				path = tmp;
				cost = tmpCost;
			}
			// ak second router u prvej cesty nie je nula, tak skusit este do
			// second routru u dst
			if (r2dst != null) {
				tmp = GraphCreatingMethods.findPathBetweenRouters(r2start,
						r2dst);
				tmpCost = (int) (tmp.getCost() + startMetricToSecond + dstMetricToSecond);
				if (cost > tmpCost) {
					path = tmp;
					cost = tmpCost;
				}
			}
		}
		System.out.println("cesta = " + path + " + cena k bunkam " + cost);
		return String.valueOf(path);
	}

	

	public static void optimalizePaths() {
		// musim kazdu jednu cestu skontrolovat ci je kompletna bez
		// uhlopriecnych buniek
		for (int i = 0; i < stock.getPaths().size(); i++) {
			pathControl(stock.getPaths().get(i));
		}

	}

	private static void pathControl(Path p) {
		Vector<AbstractStockCell> cells = p.getPath();
		// pre kazdu bunku pozriem nasledujucu a ci je priamo dosiahnutelna, ci
		// nie je na uhlopriecke
		int counter = 0; // kvoli pridanie vzdialenosti do cesty
		System.out.println("cost pred = " + p.getCost());
		for (int i = 0; i < cells.size() - 1; i++) {
			int xCurrent = cells.get(i).getX();
			int yCurrent = cells.get(i).getY();
			int xNext = cells.get(i + 1).getX();
			int yNext = cells.get(i + 1).getY();

			// staci aby bola aspon jedna suradnica rovnaka, bud x, alebo y
			if (xCurrent != xNext && yCurrent != yNext) {
				findMiddleCell(xCurrent, yCurrent, xNext, yNext, i, cells);
				System.out.println("chybajuca bunka");
				counter++;
			}

		}
		System.out.println();
		counter = counter * stock.getDelimiter()/GlobalParameters.meterPerSecond;
		p.setCost(p.getCost() + counter);
		System.out.println("cost po = " + p.getCost());
	}

	private static void findMiddleCell(int x1, int y1, int x2, int y2,
			int index, Vector<AbstractStockCell> cells) {
		// musim skusit bunky ktore si len coridor bunky (nie regal ani router)
		// a musi to pasovat medzi ne a musim to pridat do cesty na spravne
		// miesto - medzi tieto bunky
		index = index + 1; // musi ist medzi tie bunky a nie pred prvu
		// mensie x a mensi y inkrementujem a ziskam obidve moznosti

		// spravim si mnozinu buniek
		HashSet<CoridorCell> open = new HashSet<CoridorCell>();
		// z prveho vygenerujem vsetky mozne stavy a pridam
		for (int i = -1; i < 2; i = i + 2) {

			try {
				open.add((CoridorCell) stock.getFieldIndex(x1 + i, y1));

			} catch (Exception e) {
				// TODO: handle exception
				System.out.println("neda sa");

			}
			try {
				open.add((CoridorCell) stock.getFieldIndex(x1, y1 + i));

			} catch (Exception e) {
				// TODO: handle exception
				System.out.println("neda sa");

			}
		}
		
	for (int i = -1; i < 2; i = i + 2) {
			// staci ked natrafim na prvu zhodu tak pridavam na index bunku
			if (open.contains(stock.getFieldIndex(x2 + i, y2))) {
				cells.add(index, stock.getFieldIndex(x2 + i, y2));
				return;
			} else if (open.contains(stock.getFieldIndex(x2, y2 + i))) {
				cells.add(index, stock.getFieldIndex(x2, y2 + i));
				return;
			}

		}

	}
}
