# -*- coding: utf-8 -*-
""" Core """

import os
import time
import json
import copy
from dash import html
import dash_leaflet as dl
import numpy as np
from dateutil.relativedelta import relativedelta
from netCDF4 import Dataset as nc_file
from datetime import datetime
import logging

DIR_PATH = os.path.dirname(os.path.realpath(__file__))
COLORBARS = json.load(open(os.path.join(DIR_PATH, 'conf/colorbars.json')))
INFO_STYLE = json.load(open(os.path.join(DIR_PATH, 'conf/info_style.json')))
MAP_LAYERS = json.load(open(os.path.join(DIR_PATH, 'conf_ines_core/map_layers.json')))
DEBUG = True

class MapHandler:

    def __init__(self):
        
        # TODO: Add common variables from all figure handlers here

        self.info_style = INFO_STYLE

        return None

    def get_title(self, **kwargs):
        """ Return title from base title and elements """

        logging.debug('================= Retrieving figure title...')

        # Get selected and current datetime details
        rhour = self.rdatetime.strftime("%H")
        rday = self.rdatetime.strftime("%d")
        rmonth = self.rdatetime.strftime("%b")
        ryear = self.rdatetime.strftime("%Y")
        if hasattr(self, 'cdatetime'):
            shour = self.cdatetime.strftime("%H")
            sday = self.cdatetime.strftime("%d")
            smonth = self.cdatetime.strftime("%b")
            syear = self.cdatetime.strftime("%Y")

        # Get title elements that we want to show from base title
        # e.g. a dict with shour, sday, smonth, syear as keys
        title_elements = {}
        base_title_split = self.base_title.split("%(")
        for element in base_title_split[1:]:
            element_split = element.split(")")
            if len(element_split) > 1:
                title_element = element_split[0]
                if hasattr(self, title_element):
                    title_elements.update({title_element: getattr(self, title_element)})
                else:
                    title_elements.update({title_element: locals()[title_element]})

        # Create title from base and elements
        title = r'{}'.format(self.base_title % title_elements)

        return title
      
    def retrieve_cdatetime(self, tstep=0):
        """ Retrieve data from NetCDF file """

        tstep = int(tstep)
        time_attr = int(self.timesteps[tstep])

        # Calculate current datetime given the selected initial datetime and current timestep
        if self.what == 'days':
            cdatetime = self.rdatetime + relativedelta(days=time_attr)
        elif self.what == 'hours':
            cdatetime = self.rdatetime + relativedelta(hours=time_attr)
        elif self.what == 'minutes':
            cdatetime = self.rdatetime + relativedelta(minutes=time_attr)
        elif self.what == 'seconds':
            cdatetime = self.rdatetime + relativedelta(seconds=time_attr)

        return cdatetime

    def create_legend(self, colormap):
        
        logging.debug('================= Retrieving legend...')

        # Create categorical legend given the labels and its colors
        legend_items = []
        labels = colormap.keys()
        colors = colormap.values()

        for i, (label, color) in enumerate(zip(labels, colors)):
            legend_item = html.Div([html.Span(
                                        "",
                                        className="{}-legend-point".format(self.name),
                                        style={
                                            'backgroundColor': color,
                                            }
                                        ),
                                    html.Span(
                                        label,
                                        className="{}-legend-label".format(self.name),
                                        )
                                    ],
                                    style={ 'display': 'block' })
            legend_items.append(legend_item)

        legend = html.Div(legend_items, className="{}-legend".format(self.name))
        
        return legend

    def retrieve_info(self, name):
        
        logging.debug('================= Retrieving figure title box...')

        # Box width to match colorbar width
        if hasattr(self, 'colorbar'):
            if self.colorbar is not None:
                self.info_style['width'] = str(self.colorbar.width) + "px"

        # Create figure title box
        info = html.Div(
            children=self.fig_title,
            id="{}-info".format(name),
            className="info",
            style=self.info_style
        )

        return info

    def retrieve_colorbar(self, aspect=(1,1)):
        
        logging.debug('================= Retrieving colorbar...')
        
        # Create colorbar
        colorbar = dl.Colorbar(**self.colorbar_info)

        # Resize and place on top left corner
        colorbar.width = 320 - 15 * aspect[0]
        colorbar.style = {'overflow':'hidden', 'white-space':'nowrap'}
        if aspect == (3,4):
            colorbar.width = 320 - 38 * aspect[0]
        colorbar.height = 8

        return colorbar

    def get_time_details(self):

        input_file = nc_file(self.filepath)
        time_obj = input_file.variables['time']
        timesteps = time_obj[:]
        tim_units = time_obj.units.split()
        if len(tim_units) == 3:
            what, _, rdate = tim_units
            rtime = "00:00"
        elif len(tim_units) >= 4:
            what, _, rdate, rtime = tim_units[:4]
        if len(rtime) > 5:
            rtime = rtime[:5]
        rdatetime = datetime.strptime("{} {}".format(rdate, rtime), "%Y-%m-%d %H:%M")

        return timesteps, what, rdatetime

    def hour_to_step(self, hour):
        """ Convert hour to relative tstep """
        cdatetime = self.rdatetime.date() + relativedelta(hours=hour)

        for step in range(len(self.timesteps)):
            if self.retrieve_cdatetime(step) == cdatetime:
                return step

        return 0

    def get_center(self, center=None):
        """ Returns center of map """

        if center is None and hasattr(self, 'ylat'):
            center =  [
                    (self.ylat.max()-self.ylat.min())/2 + self.ylat.min() + (self.ylat.max()-self.ylat.min())/6,
                    (self.xlon.max()-self.xlon.min())/2 + self.xlon.min(),
            ]
        elif center is None:
            center = [ 30, 15 ]

        return center

    def retrieve_fig(self, aspect=(1,1), center=None, selected_tiles='carto-positron', 
                     zoom=None, tag='empty', index=None, layers=None):
        """ run plot """

        self.st_time = time.time()

        logging.debug('================= Retrieving figure...')
        
        logging.debug("ASPECT %s", aspect)
        if center is None:
            center = self.get_center(center)
        if zoom is None:
            zoom = 3.5 -(aspect[0]-aspect[0]*0.4)
        logging.debug("ZOOM %s", zoom)
        logging.debug("CENTER %s", center)
        
        # Get index and template names
        index = str(index)
        tag_template_tile = "{}-tile-layer"
        tag_template_map = "{}-map"

        if not isinstance(layers, list):
            layers = [layers]

        logging.debug("TAG %s", tag)
        logging.debug("INDEX %s", index)
        fig = dl.Map(children=[
            dl.TileLayer(
                id=dict(
                    tag=tag_template_tile.format(tag),
                    index=index
                ),
                url=MAP_LAYERS[selected_tiles]['url'],
                attribution=MAP_LAYERS[selected_tiles]['attribution']
            ),
            dl.FullscreenControl(
                position='topright',
            )] + layers,
            zoomSnap=0.1,
            zoom=zoom,
            wheelPxPerZoomLevel=120,
            wheelDebounceTime=80,
            center=center,
            id=dict(
                tag=tag_template_map.format(tag),
                index=index
                ),
            inertia=True,
            preferCanvas=True,
            animate=False,
            minZoom=2,
            #className="graph-with-slider",
        )

        logging.debug("*** FIGURE EXECUTION TIME: {} ***".format(str(time.time() - self.st_time)))
        
        return fig

class PointsFigureHandler(MapHandler):

    def __init__(self):

        super(PointsFigureHandler, self).__init__()

        if self.name in COLORBARS:
            self.colorbar_info = COLORBARS[self.name]
        
        # TODO: Add common variables from:
        # VisFigureHandler, EvaluationGroundFigureHandler and EvaluationStatisticsFigureHandler

        return None

    def retrieve_geojson(self, geojson_data, namespace):

        logging.debug('================= Retrieving geojson...')

        # Get points properties
        hideout = {"circleOptions": self.circle_options}
        if 'fillColor' not in hideout["circleOptions"].keys():
            hideout.update({'colorProp': "value",
                            'colorscale': self.colors})

        # Get geojson
        point_to_layer = namespace("pointToLayer")
        geojson = dl.GeoJSON(data=geojson_data,
                             options=dict(pointToLayer=point_to_layer,),
                             hideout=hideout)

        return geojson

    def get_tooltip(self, df, var_list):

        logging.debug('================= Retrieving tooltip...')

        data_dict = df.to_dict('records')
        for item in data_dict:
            logging.debug("------------- %s -----------------", item)
            tooltip = """<span class='popup-map-body'>"""
            tooltip_keys = [key for key in var_list if key in item.keys()]
            for key in tooltip_keys:
                # Check for nans, empty and false numbers
                if (item[key] in [False, '']) or (item[key] != item[key]):
                    continue
                if key == 'station':
                    tooltip += """<b>{0}</b><br>""".format(item[key])
                else:
                    tooltip += """{0}: <b>{1}</b><br>""".format(key.capitalize(), item[key])
            tooltip += """</span>"""
            item["tooltip"] = tooltip

        return data_dict


class ContourFigureHandler(MapHandler):

    def __init__(self):

        super(ContourFigureHandler, self).__init__()

        # TODO: Add common variables from old FigureHandler and ProbFigureHandler

        if self.name in COLORBARS:
            self.colorbar_info = COLORBARS[self.name]
        else:
            self.colorbar_info = COLORBARS['model']

        return None

    def retrieve_geojson(self, geojson_path, namespace, hideout):
        
        from dash_server import app

        if DEBUG: 
            print('================= Retrieving geojson...')
            
        # Get geojson
        geojson_url = app.get_asset_url(geojson_path)
        style_handle = namespace("styleHandle")
        geojson = dl.GeoJSON(url=geojson_url,
                             options=dict(style=style_handle),
                             hideout=hideout)

        logging.info("GEOJSON URL %s", geojson_url)

        return geojson


class ShapefileFigureHandler(MapHandler):

    def __init__(self):
        
        super(ShapefileFigureHandler, self).__init__()

        # TODO: Add variables from ForecastWasFigureHandler that could be reused in other figures

        return None

    def retrieve_geojson(self, geojson_path, namespace, hideout, hover_style):
        
        from dash_server import app

        logging.debug('================= Retrieving geojson...')
        
        # Get geojson
        geojson_url = app.get_asset_url(geojson_path)
        style_handle = namespace("styleHandle")
        geojson = dl.GeoJSON(url=geojson_url,
                             options=dict(style=style_handle),
                             hoverStyle=hover_style,
                             hideout=hideout)

        logging.info("GEOJSON URL %s", geojson_url)

        return geojson
