package ch.zhaw.ads;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

public class RouteServer implements CommandExecutor {
    Graph<DijkstraNode, Edge> graph;

    @Override
    public String execute(String command) {
        graph = this.readInput(command);
        StringBuffer result = new StringBuffer(100);
        for (Node node : graph.getNodes()) {
            result.append(node.getName() + "\n");
        }

        List<DijkstraNode> path = this.dijkstra("Winterthur", "Lugano");
        result.append("\n\nShortest path:\n");
        for (DijkstraNode node : path) {
            result.append(node.getName() + "\n");
        }
        return result.toString();
    }

    private Graph<DijkstraNode, Edge> readInput(String input) {
        int dist;
        String line, src, dest;
        String[] values;

        Scanner scanner = new Scanner(input);
        Graph<DijkstraNode, Edge> graph = new AdjListGraph<DijkstraNode, Edge>(DijkstraNode.class, Edge.class);
        while (scanner.hasNextLine()) {
            line = scanner.nextLine();

            try {
                values = line.split(" ");
                graph.addEdge(values[0], values[1], Integer.valueOf(values[2]));
                graph.addEdge(values[1], values[0], Integer.valueOf(values[2]));
            } catch (Throwable e) {
                System.err.println("Error.");
                continue;
            }
        }
        scanner.close();
        return graph;
    }

    private List<DijkstraNode> dijkstra(String from, String to) {
        for (DijkstraNode<Edge> node : graph.getNodes()) {
            node.setMark(false);
            node.setDist(Double.POSITIVE_INFINITY);
            node.setPrev(null);
        }

        Queue<DijkstraNode> pq = new PriorityQueue<DijkstraNode>();
        DijkstraNode<Edge> start = graph.findNode(from);
        DijkstraNode<Edge> goal = graph.findNode(to);
        start.setDist(0);
        pq.add(start);
        while (!pq.isEmpty()) {
            DijkstraNode<Edge> current = pq.poll();
            current.setMark(true);
            if (current.equals(goal)) {
                break;
            }
            for (Edge<DijkstraNode> edge : current.getEdges()) {
                DijkstraNode<Edge> sucessor = edge.getDest();
                if (!sucessor.getMark()) {
                    double dist = edge.getWeight() + current.getDist();
                    if (sucessor.getPrev() == null || dist < sucessor.getDist()) {
                        sucessor.setDist(dist);
                        sucessor.setPrev(current);
                        pq.add(sucessor);
                    }
                }
            }
        }

        List<DijkstraNode> path = new ArrayList<DijkstraNode>();
        DijkstraNode<Edge> current = goal;
        while (current.getPrev() != null) {
            System.out.println(current.getName());
            path.add(current);
            current = current.getPrev();
        }
        Collections.reverse(path);
        return path;
    }
}
