from scipy.interpolate import CubicSpline
import numpy as np
import matplotlib.pyplot as plt
import folium


class Interpolator:
    @staticmethod
    def CubicTrajectoryInterpolator(x, y, interpolate_factor, circular=False, show=False, generate_map=False, map_name='default_map.html'):

        if circular:
            if (x[0], y[0]) != (x[-1], y[-1]):
                x = np.append(x, x[0])  # Cerrar el ciclo en X
                y = np.append(y, y[0])  # Cerrar el ciclo en Y

            # Interpolación cúbica con condición de periodicidad (trayectoria circular)
            t = np.arange(len(x))  # Variable independiente, basada en los índices de los puntos
            cs_x = CubicSpline(t, x, bc_type='periodic')  # Interpolamos las coordenadas X con condición periódica
            cs_y = CubicSpline(t, y, bc_type='periodic')  # Interpolamos las coordenadas Y con condición periódica

            # Generamos nuevos puntos interpolados
            t_new = np.linspace(0, len(x) - 1, len(x) * interpolate_factor)  # Espacio uniforme
            x_new = cs_x(t_new)  # Nuevas coordenadas X
            y_new = cs_y(t_new)  # Nuevas coordenadas Y
        else:
            # Interpolación cúbica separada para X e Y
            cs_x = CubicSpline(np.arange(len(x)), x)  # Interpolamos las coordenadas X
            cs_y = CubicSpline(np.arange(len(y)), y)  # Interpolamos las coordenadas Y

            # Generamos nuevos puntos interpolados
            t_new = np.linspace(0, len(x) - 1, len(x) * interpolate_factor)  # Espacio uniforme
            x_new = cs_x(t_new)  # Nuevas coordenadas X
            y_new = cs_y(t_new)  # Nuevas coordenadas Y

        if show:
            if generate_map:
                # Crear un mapa centrado en el primer punto
                m = folium.Map(location=[y[0], x[0]], zoom_start=14)

                # Añadir puntos originales al mapa
                for lat, lon in zip(y, x):
                    folium.Marker([lat, lon], popup=f"({lat}, {lon})").add_to(m)

                # Crear una lista de coordenadas interpoladas [(lat, lon), (lat, lon), ...]
                interpolated_points = [(lat, lon) for lat, lon in zip(y_new, x_new)]

                # Dibujar la línea interpolada en el mapa
                folium.PolyLine(interpolated_points, color="blue", weight=2.5, opacity=1).add_to(m)
                m.save(map_name)
            else:
                plt.plot(x, y, 'o', label='Puntos originales')  # Graficar puntos originales
                plt.plot(x_new, y_new, '-', label='Trayectoria suavizada')  # Trayectoria suavizada
                plt.legend()
                plt.xlabel("Coordenada X")
                plt.ylabel("Coordenada Y")
                plt.title("Interpolación de trayectoria")
                plt.grid(True)
                plt.show()

        return x_new, y_new


if __name__ == '__main__':

    cities = {
        "Estella": (-2.0310612055588173, 42.67367031611737),
        "Muxia": (-9.217775273649409, 43.10582940687305),
        "Trujillo": (-5.8824527238350655, 39.45804526503004),
        "Granada": (-3.599776119363134, 37.17421011902858),
        "Madrid": (-3.672190678958218, 40.49627408029931)
    }

    longitudes = [coords[0] for coords in cities.values()]
    latitudes = [coords[1] for coords in cities.values()]

    # Interpolación spline cúbica de la trayectoria
    x_new, y_new = Interpolator.CubicTrajectoryInterpolator(
        longitudes,
        latitudes,
        interpolate_factor=2000,
        show=True,
        circular=True,
        generate_map=True)
