import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;

public class Backend implements BackendInterface {

    private GraphADT<String, Double> graph = new DijkstraGraph<String, Double>();
    private List<String> locations = new ArrayList<>();

    public Backend(GraphADT<String,Double> graph) { }

    /**
     * Loads graph data from a dot file.
     *
     * @param filename the path to a dot file to read graph data from
     * @throws IOException if there was a problem reading in the specified file
     */
    @Override
    public void loadGraphData(String filename) throws IOException {
        try {
            File file = new File(filename);
            Scanner scanner = new Scanner(file);
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine().trim();
                if (line.contains("->")) {
                    String[] parts = line.split("->");
                    String startLocation = parts[0].trim();
                    String Start = startLocation.replace("\"", "").trim();
                    String endLocation = parts[1].substring(0, parts[1].indexOf("[")).trim();
                    String End = endLocation.replace("\"", "").trim();
                    String weightStr = "";
                    if (parts[1].contains("seconds=")) {
                        int start = parts[1].indexOf("seconds=") + 8;
                        weightStr = parts[1].substring(start, parts[1].indexOf("]", start)).trim();
                    }
                    double weight = 0.0;
                    try {
                        weight = Double.parseDouble(weightStr);
                    } catch (NumberFormatException e) {
                        continue;
                    }
                    if (graph.insertNode(Start)) {
                        if (!locations.contains(Start)) {
                            locations.add(Start);
                        }
                    }
                    if (graph.insertNode(End)) {
                        if (!locations.contains(End)) {
                            locations.add(End);
                        }
                    }
                    graph.insertEdge(Start, End, weight);
                }
            }
        } catch (IOException e) {
            throw new IOException();
        }
    }

    /**
     * Returns a list of all locations (nodes) available on the backend's graph.
     *
     * @return list of all location names
     */
    @Override
    public List<String> getListOfAllLocations() {
        return locations;
    }

    /**
     * Return the sequence of locations along the shortest path from startLocation to endLocation, or
     * en empty list if no such path exists.
     *
     * @param startLocation the start location of the path
     * @param endLocation   the end location of the path
     * @return a list with the nodes along the shortest path from startLocation to endLocation, or
     * an empty list if no such path exists
     */
    @Override
    public List<String> findShortestPath(String startLocation, String endLocation) {
        try {
            return graph.shortestPathData(startLocation, endLocation);
        } catch (NoSuchElementException e) {
            return new ArrayList<>();
        }
    }

    /**
     * Return the walking times in seconds between each two nodes on the shortest path from startLocation
     * to endLocation, or an empty list of no such path exists.
     *
     * @param startLocation the start location of the path
     * @param endLocation   the end location of the path
     * @return a list with the walking times in seconds between two nodes along the shortest path from
     * startLocation to endLocation, or an empty list if no such path exists
     */
    @Override
    public List<Double> getTravelTimesOnPath(String startLocation, String endLocation) {
        List<String> path = graph.shortestPathData(startLocation, endLocation);
        List<Double> times = new ArrayList<>();
        try {
            for (int i = 0; i < path.size() - 1; i++) {
                times.add(graph.getEdge(path.get(i), path.get(i + 1)));
            }
        } catch (NoSuchElementException e) {
            return new ArrayList<>();
        }
        return times;
    }

    /**
     * Return the most distant location from startLocation that is reachable in the graph.
     *
     * @param startLocation the location to find the most distant location for
     * @return the location that is most distant (has the longest overall walking time)
     * @throws NoSuchElementException if startLocation does not exist
     */
    @Override
    public String getMostDistantLocation(String startLocation) throws NoSuchElementException {
        Double travelTime = 0.0;
        String mostDistantLocation = "";
        List<String> allLocations = getListOfAllLocations();
        for (String location : allLocations) {
            try {
                Double time = graph.shortestPathCost(startLocation, location);
                if (time > travelTime) {
                    travelTime = time;
                    mostDistantLocation = location;
                }
            } catch (Exception e) {
                continue;
            }
        }
        return mostDistantLocation;
    }

}
