// Lee los archivos de data/mapas y arma el grafo.
#ifndef MAP_FILE_H
#define MAP_FILE_H

#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

#include "graph.h"

struct MapEdge {
    int u;
    int v;
    int meters;
};

struct MapPoint {
    double latitude;
    double longitude;
};

// Lo que dice el archivo: V nodos con sus coordenadas, y E tramos de calle, en el orden del archivo.
struct MapFile {
    int node_count = 0;
    std::vector<MapPoint> points;
    std::vector<MapEdge> edges;
};

// Sale del programa si no puede leer el archivo completo.
inline MapFile read_map_file(const std::string& file_name) {
    std::ifstream file(file_name);
    MapFile map;
    std::size_t edge_count = 0;
    if (!(file >> map.node_count >> edge_count)) {
        std::cerr << "no se pudo leer " << file_name << "\n";
        std::exit(1);
    }
    map.points.resize(map.node_count);
    for (MapPoint& point : map.points) {
        file >> point.latitude >> point.longitude;
    }
    map.edges.resize(edge_count);
    for (MapEdge& edge : map.edges) {
        file >> edge.u >> edge.v >> edge.meters;
    }
    if (!file) {
        std::cerr << "archivo incompleto: " << file_name << "\n";
        std::exit(1);
    }
    return map;
}

inline Graph graph_of(const MapFile& map) {
    Graph graph(map.node_count);
    for (const MapEdge& edge : map.edges) {
        graph.add_edge(edge.u, edge.v, edge.meters);
    }
    return graph;
}

// Un archivo *-calles.tsv: por cada nombre de calle, un nodo de esa calle.
struct Street {
    int node;
    std::string name;
};

inline std::vector<Street> read_streets(const std::string& file_name) {
    std::ifstream file(file_name);
    if (!file) {
        std::cerr << "no se pudo leer " << file_name << "\n";
        std::exit(1);
    }
    std::vector<Street> streets;
    int node = 0;
    std::string name;
    while (file >> node) {
        file.get();  // el tabulador
        std::getline(file, name);
        streets.push_back({node, name});
    }
    return streets;
}

// El nodo de una calle con ese nombre exacto. Sale del programa si no está.
inline int node_of_street(const std::vector<Street>& streets, const std::string& name) {
    for (const Street& street : streets) {
        if (street.name == name) {
            return street.node;
        }
    }
    std::cerr << "no hay ninguna calle con ese nombre: " << name << "\n";
    std::exit(1);
}

// Metros en línea recta sobre la esfera, con la fórmula de los mapas del curso. Sirve para
// revisar si una ruta es razonable: ninguna ruta puede ser más corta que esto.
inline double straight_meters(const MapPoint& a, const MapPoint& b) {
    const double to_radians = std::acos(-1.0) / 180.0;
    double half_latitude = (b.latitude - a.latitude) * to_radians / 2;
    double half_longitude = (b.longitude - a.longitude) * to_radians / 2;
    double h = std::sin(half_latitude) * std::sin(half_latitude) +
               std::cos(a.latitude * to_radians) * std::cos(b.latitude * to_radians) *
                   std::sin(half_longitude) * std::sin(half_longitude);
    return 2 * 6371008.8 * std::asin(std::sqrt(h));
}

#endif
