import warnings

import numpy as np

from folium.elements import JSCSSMixin
from folium.map import Layer
from folium.template import Template
from folium.utilities import (
    if_pandas_df_convert_to_numpy,
    none_max,
    none_min,
    remove_empty,
    validate_location,
)


class HeatMap(JSCSSMixin, Layer):
    """
    Create a Heatmap layer

    Parameters
    ----------
    data : list of points of the form [lat, lng] or [lat, lng, weight]
        The points you want to plot.
        You can also provide a numpy.array of shape (n,2) or (n,3).
    name : string, default None
        The name of the Layer, as it will appear in LayerControls.
    min_opacity  : default 1.
        The minimum opacity the heat will start at.
    max_zoom : default 18
        Zoom level where the points reach maximum intensity (as intensity
        scales with zoom), equals maxZoom of the map by default
    radius : int, default 25
        Radius of each "point" of the heatmap
    blur : int, default 15
        Amount of blur
    gradient : dict, default None
        Color gradient config. Defaults to
        {.4: "blue", .6: "cyan", .7: "lime", .8: "yellow", 1: "red"}
    overlay : bool, default True
        Adds the layer as an optional overlay (True) or the base layer (False).
    control : bool, default True
        Whether the Layer will be included in LayerControls.
    show: bool, default True
        Whether the layer will be shown on opening.
    """

    _template = Template("""
        {% macro script(this, kwargs) %}
            var {{ this.get_name() }} = L.heatLayer(
                {{ this.data|tojson }},
                {{ this.options|tojavascript }}
            );
        {% endmacro %}
        """)

    default_js = [
        (
            "leaflet-heat.js",
            "https://cdn.jsdelivr.net/gh/python-visualization/folium@main/folium/templates/leaflet_heat.min.js",
        ),
    ]

    def __init__(
        self,
        data,
        name=None,
        min_opacity=0.5,
        max_zoom=18,
        radius=25,
        blur=15,
        gradient=None,
        overlay=True,
        control=True,
        show=True,
        **kwargs,
    ):
        super().__init__(name=name, overlay=overlay, control=control, show=show)
        self._name = "HeatMap"
        data = if_pandas_df_convert_to_numpy(data)
        self.data = [
            [*validate_location(line[:2]), *line[2:]] for line in data  # noqa: E999
        ]
        if np.any(np.isnan(self.data)):
            raise ValueError("data may not contain NaNs.")
        if kwargs.pop("max_val", None):
            warnings.warn(
                "The `max_val` parameter is no longer necessary. "
                "The largest intensity is calculated automatically.",
                stacklevel=2,
            )
        self.options = remove_empty(
            min_opacity=min_opacity,
            max_zoom=max_zoom,
            radius=radius,
            blur=blur,
            gradient=gradient,
            **kwargs,
        )

    def _get_self_bounds(self):
        """
        Computes the bounds of the object itself (not including it's children)
        in the form [[lat_min, lon_min], [lat_max, lon_max]].

        """

        bounds = [[None, None], [None, None]]
        for point in self.data:
            bounds = [
                [
                    none_min(bounds[0][0], point[0]),
                    none_min(bounds[0][1], point[1]),
                ],
                [
                    none_max(bounds[1][0], point[0]),
                    none_max(bounds[1][1], point[1]),
                ],
            ]
        return bounds
