import math

#https://pypi.org/project/networkx/
import networkx as nx

from . import logger
from . import output_creator
from . import input_processing

# following haversine function is from:
# https://www.geeksforgeeks.org/dsa/haversine-formula-to-find-distance-between-two-points-on-a-sphere/

def haversine(lat1, lon1, lat2, lon2):
    # distance between latitudes
    # and longitude

    dLat = (lat2 - lat1) * math.pi / 180.0
    dLon = (lon2 - lon1) * math.pi / 180.0

    # convert to radians
    lat1 = lat1 * math.pi / 180.0
    lat2 = lat2 * math.pi / 180.0

    # apply formulae
    a = (pow(math.sin(dLat / 2), 2) +
         pow(math.sin(dLon / 2), 2) *
         math.cos(lat1) * math.cos(lat2))
    rad = 6371
    c = 2 * math.asin(math.sqrt(a))
    return rad * c


def graph_creator(route_stops, cost, co2):
    # create graph, using networkx
    route_graph = nx.Graph(directed=False)

    # populate graph with nodes, each node values being based on the stops nested dictionary
    for stop, values in route_stops.items():
        if values["priority"] == "low":
            priority_weight = 2.0
        elif values["priority"] == "medium":
            priority_weight = 1.0
        elif values["priority"] == "high":
            priority_weight = 0.1
        route_graph.add_node(stop + ", " + values["customer"], customer=values["customer"], latitude=float(values["latitude"]),
                             longitude=float(values["longitude"]), priority=priority_weight,
                             weight_kg=float(values["weight_kg"]))

    # create and assign edges between nodes, using above haversine function
    for node1, data1 in route_graph.nodes.items():
        for node2, data2 in route_graph.nodes.items():
            if node1 != node2:
                lat1 = data1["latitude"]
                lon1 = data1["longitude"]
                lat2 = data2["latitude"]
                lon2 = data2["longitude"]

                distance = haversine(lat1, lon1, lat2, lon2)

                # based on the priority at each node
                priority_weight = data1["priority"] * data2["priority"]
                weighted_distance = priority_weight * distance

                distance_with_cost = distance * cost
                weighted_distance_with_cost = priority_weight * distance_with_cost

                distance_with_co2 = distance * co2
                weighted_distance_with_co2 = priority_weight * distance_with_co2


                route_graph.add_edge(node1, node2, weighted_distance=weighted_distance, distance=distance, weighted_distance_with_cost = weighted_distance_with_cost, weighted_distance_with_co2 = weighted_distance_with_co2)

    #print(route_graph.nodes.data())
    print(route_graph.edges.data())
    return route_graph


@logger.timing_decorator
def route_creator(mode, criterion, inputted_depot):

    #parameters = [mode, criterion, inputted_depot]
    #logger.log_function_called(parameters)

    input_dictionary = input_processing.input_processor()

    route_stops = {'depot': {'customer': inputted_depot.get('customer'), 'latitude': inputted_depot.get('latitude'),
                             'longitude': inputted_depot.get('longitude'), 'priority': 'medium', 'weight_kg': '0'}}

    route_stops.update(input_dictionary)

    if mode == "car":
        speed = 50
        cost_per_km = 4
        co2_per_km = 120
    elif mode == "bicycle":
        speed = 15
        cost_per_km = 0
        co2_per_km = 0
    elif mode == "walking":
        speed = 5
        cost_per_km = 0
        co2_per_km = 0

    graph = graph_creator(route_stops, cost_per_km, co2_per_km)

    # https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.approximation.traveling_salesman.traveling_salesman_problem.html
    tsp = nx.approximation.greedy_tsp

    # TODO: get the damn tsp to actually consider the different weight

    # fastest total time
    if criterion == "1":
        route = tsp(graph, weight="weighted_distance")

        output_creator.outputter(mode, criterion, route, graph, speed, cost_per_km, co2_per_km)

    # lowest total cost
    elif criterion == "2":
        if mode == "car":
            route = tsp(graph, weight="weighted_distance_with_cost")
            output_creator.outputter(mode, criterion, route, graph, speed, cost_per_km, co2_per_km)
        else:
            route = tsp(graph, weight="weighted_distance")
            output_creator.outputter(mode, criterion, route, graph, speed, cost_per_km, co2_per_km)

    # lowest total co2
    elif criterion == "3":
        if mode == "car":
            route = tsp(graph, weight="weighted_distance_with_co2")
            output_creator.outputter(mode, criterion, route, graph,speed, cost_per_km, co2_per_km)
        else:
            route = tsp(graph, weight="weighted_distance")
            output_creator.outputter(mode, criterion, route, graph, speed, cost_per_km, co2_per_km)

#route_creator('car', '1', {'customer': 'near oslo', 'latitude': '60','longitude': '11'})