import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jgrapht.DirectedGraph;
import org.jgrapht.graph.SimpleDirectedGraph;

/**
 * This class represents the implementation of the A* algorithm
 */

public abstract class PuzzleAStar {

	private static DirectedGraph<PuzzleMove, Integer> graph; // vertex:
	// PuzzleMove
	// edge: Integer (use an int value as a unique ID)
	private PuzzleMove startState; // the start state of the puzzle
	private PuzzleMove destinationState; // the destination state of the puzzle
	private PuzzleMove currentState; // the current explored puzzle state
	private List<PuzzleMove> movesToProcess; // the number of moves not taken yet
	//TODO remove
	private List<PuzzleMove> movesInGraph = new ArrayList<PuzzleMove>();
	
	private long startPuzzleTime; // Timing variables
	private long endPuzzleTime;

	/**
	 * init() Initialises the graph
	 */
	void init() {
		graph = new SimpleDirectedGraph<PuzzleMove, Integer>(Integer.class);
		//TODO create movesToProcess
		//TODO set currentstate = startstate
	}

	/**
	 * This method implements the A* algorithm for the puzzle
	 */
	public void findDestination() {
		boolean goOn = true;
		int iterator = 0;
		
		//TODO do this in init
		movesToProcess = new ArrayList<PuzzleMove>();

		//TODO remove
		if (startState.equals(destinationState)) {
			goOn = false;
		}

		//TODO move to init
		currentState = startState;
		//TODO dont do that
		movesToProcess.add(currentState);
		//TODO do instead:
		//m2p.addAll(curState.successorMoves())
		
		
		//TODO: while(!currentstate.equals(destistate))
		while (goOn) {
			
			//TODO: search 4 nextstep:
			//		PuzzleMove nextMove = null 
			//      for(i = 0...moves2procees.size()) do
			//		if(nextmove == null)
			//			nextmove = m2p.get(i)
			//		else
			//			if(nextmove.cost() > m2p.get(i).cost()
			//				nextmove = m2p.get(i)
			
			//TODO: check if nextmove != null if its null -> m2p is empty -> no way found
			
			//TODO: try 2 add curmove 2 graph
			// if(graph.addVertex(currentState))
			//    add edge
			//    curstate = nextmove; //go on to next
			//    m2p.addAll(curstate.successorMoves())
			
			//TODO: remove nextmove from m2p -> ALWAYS
			
			
			/*graph.addVertex(currentState);
			movesInGraph.add(currentState);
			movesToProcess.remove(currentState);
			
			currentState.setSuccessorlist();

			for (int i = 0; i < currentState.getSuccessorlist().size(); i++) {
				PuzzleMove actualSuccessor = currentState.getSuccessorlist().get(i);

				// Berechnet dd, ds und cost fuer den i-ten successor des currentState
				actualSuccessor.setDd(actualSuccessor.calculateDistance(this.destinationState));
				actualSuccessor.setDs(iterator);
				actualSuccessor.setCost(actualSuccessor.getDd() + actualSuccessor.getDs());

				// Added den aktuellen Successor zur liste movesToProcess
				if (!moveInGraph(actualSuccessor)) {
					movesToProcess.add(actualSuccessor);
				}
			}
			
			// Setzt den/einen der besten Successor(s) auf currentState
			currentState = getABestSuccessor(getLowestCost());

			System.out.println(iterator);
			
			
			// Abbruchbedingung: Wenn der neu gefundene currentState dem
			// destinationState gleicht soll
			// terminiert werden
			if (currentState.equals(this.destinationState)) {
				goOn = false;
			}
			iterator++;*/
		}
	}
	
	/**
	 * getLowestCost() Checkt ob ein PuzzleMove den gelichen State hat wie ein
	 * PuzzleMove in einer Liste von PuzzleMoves
	 * 
	 * @param list
	 * @return int (the lowest cost)
	 */
	public int getLowestCost() {
		int lowestCost = 0;

		for (int i = 0; i < movesToProcess.size(); i++) {
			if (i == 0) {
				lowestCost = movesToProcess.get(i).getCost();
			} else if (i > 0) {
				if (movesToProcess.get(i).getCost() < lowestCost) {
					lowestCost = movesToProcess.get(i).getCost();
				}
			}
		}
		return lowestCost;
	}
	

	/**
	 * moveInList() Ueberprueft ob ein der state eines PuzzleMoves einem in
	 * einer Liste mit PMs gleicht
	 */
	public boolean moveInList(List<PuzzleMove> list, PuzzleMove toCompare) {
		for (int i = 0; i < list.size(); i++) {
			if (toCompare.equals(list.get(i))) {
				System.out.println("deine kack mooddie");
				return true;
			}
		}
		return false;
	}

	/**
	 * moveInGraph() Ueberprueft ob ein move bereits einem im Graphen gleicht
	 */
	public boolean moveInGraph(PuzzleMove move) {
		Iterator<PuzzleMove> it = graph.vertexSet().iterator();
		while(it.hasNext()) {
			PuzzleMove p = it.next();
			
			if (move.equals(p)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * getABestSuccessor() returned einen PM der successors der mit lowestCost als Cost-Wert 
	 * sollte dieser noch nicht im Graphen hinzugefuegt worden sein
	 */
	public PuzzleMove getABestSuccessor(int lowestCost) {
			
		for (int i = 0; i < movesToProcess.size(); i++) {
			if (movesToProcess.get(i).getCost() == lowestCost && !moveInList(movesInGraph, movesToProcess.get(i))) {
				return movesToProcess.get(i);
			}
		}
		return null;
	}

	
	
	/**
	 * This method prints a summary of the solution
	 * 
	 * @param state
	 *            The final puzzle state (Destination)
	 */
	public void printSolutionSummary(PuzzleMove state) {
		// TODO: Add code if needed
		// TODO: Remove comments and add needed code
		// System.out.println("Time needed to find Destination (ms): " + //
		// TODO);
		// System.out.println("Current cost at final state (Destination): " +
		// state.getCost());
		// System.out.println("States processed so far: " + // TODO);
		// System.out.println("States identified for processing (not being processed): "
		// + // TODO);
	}

	/**
	 * This method prints the path to the solution
	 * 
	 * @param graph
	 */
	public void printPathToSolution(DirectedGraph<PuzzleMove, Integer> graph) {
		System.out
				.println("\nThe steps/path needed to reach the destination:\n");
		// TODO: Implement method
		// TODO: Include in each step the cost, ds and dd of the current state
		// (PuzzleMove)
		// e.g.:
		// Step 9: cost = 3, ds = 9, dd = 7
		// --------------------------------
		// 1 2 3
		// 4 5 7
		// 6 8 #
		//
	}

	/**
	 * The number of states identified for processing but not processed yet
	 * 
	 * @return the number of moves not taken yet
	 */
	int numberOfStatesToProcess() {
		// TODO: Implement this method
		return -1;
	}

	// TODO: Implement other necessary methods

	/**
	 * The number of states/puzzle moves processed so far
	 * 
	 * @return the number of edges
	 */
	int numberOfProcessedStates() {
		return this.graph.vertexSet().size();
	}

	/**
	 * @return the graph
	 */
	public DirectedGraph<PuzzleMove, Integer> getGraph() {
		return this.graph;
	}

	/**
	 * @return the currentState
	 */
	public PuzzleMove getCurrentState() {
		return this.currentState;
	}

	/**
	 * @param destinationState
	 *            the destinationState to set
	 */
	public void setDestinationState(PuzzleMove destinationState) {
		this.destinationState = destinationState;
	}

	/**
	 * @param startState
	 *            the startState to set
	 */
	public void setStartState(PuzzleMove startState) {
		this.startState = startState;
	}

	/**
	 * @return the startState
	 */
	public PuzzleMove getStartState() {
		return this.startState;
	}

	/**
	 * @return the destinationState
	 */
	public PuzzleMove getDestinationState() {
		return this.destinationState;
	}

	/**
	 * @return the movesToProcess
	 */
	public List<PuzzleMove> getMovesToProcess() {
		return this.movesToProcess;
	}

	public static void main(String[] args) throws IOException {

		PuzzleInputOutput pio = new PuzzleInputOutput("test_0.txt");
		PuzzleAStar pas = new PuzzleAStar() {
			@Override
			public void setStartState(PuzzleMove startState) {
				// TODO Auto-generated method stub
				super.setStartState(startState);
			}

			@Override
			public void setDestinationState(PuzzleMove destinationState) {
				// TODO Auto-generated method stub
				super.setDestinationState(destinationState);
			}

			@Override
			public PuzzleMove getStartState() {
				// TODO Auto-generated method stub
				return super.getStartState();
			}

			@Override
			public PuzzleMove getDestinationState() {
				// TODO Auto-generated method stub
				return super.getDestinationState();
			}

			@Override
			public void findDestination() {
				// TODO Auto-generated method stub
				super.findDestination();
			}
		};

		PuzzleMove start = new PuzzleMove();
		PuzzleMove end = new PuzzleMove();
		start.setState(pio.getPuzzleStart());
		end.setState(pio.getPuzzleDestination());

		pas.setDestinationState(end);
		pas.setStartState(start);
		pas.init();
		
		pas.findDestination();
		
	}
}