from datetime import datetime, timezone, timedelta
import io
import itertools
import math
import os.path

from PIL import Image, ImageDraw, ImageFont

import pygal

from . import display_longtext
from .weather_api import WeatherAPI

def draw_format_infos(infos, width, height, fnt_R, fnt_B, label_margin, align_height=0, margin_bf_first=True, **kwargs):
        image = Image.new('RGBA', (int(width), int(height)))
        draw = ImageDraw.Draw(image)

        nb_infos = len(infos.keys())
        size = 0
        for k,v in infos.items():
            size += fnt_R.getlength(k)
            size += label_margin
            size += fnt_B.getlength(v)

        margin = (width - size) / nb_infos
        align = 0
        if margin_bf_first:
            align += margin / 2
        for k,v in infos.items():
            draw.text(
                (align, align_height),
                k,
                font=fnt_R, **kwargs
            )
            align += fnt_R.getlength(k) + label_margin
            draw.text(
                (align, align_height),
                v,
                font=fnt_B, **kwargs
            )
            align += fnt_B.getlength(v) + margin

        return image

def draw_format_array(infos, width, height, fnt_R, fnt_B, label_margin, align_height=0, margin_bf_first=True, **kwargs):
        image = Image.new('RGBA', (int(width), int(height)))
        draw = ImageDraw.Draw(image)

        nb_infos = len(infos.keys())
        size = 0
        title_hsize = 0
        text_hsize = 0
        for k,v in infos.items():
            _,_,ksize,ksizeH = fnt_R.getbbox(k)
            _,_,vsize,vsizeH = fnt_B.getbbox(v)
            if title_hsize < ksizeH:
                    title_hsize = ksizeH
            if text_hsize < vsizeH:
                    text_hsize = vsizeH
            size += max(ksize,vsize)
            size += label_margin

        margin = (width - size) / nb_infos
        align = 0
        if margin_bf_first:
            align += margin / 2
        for k,v in infos.items():
            size = max(fnt_R.getlength(k),fnt_B.getlength(v))
            draw.text(
                (align + (0 if "anchor" not in kwargs or kwargs["anchor"][0] != "m" else size/2), align_height-title_hsize/2),
                k,
                font=fnt_R, **kwargs
            )
            draw.text(
                (align + (0 if "anchor" not in kwargs or kwargs["anchor"][0] != "m" else size/2), align_height+text_hsize/2),
                v,
                font=fnt_B, **kwargs
            )
            align += size + margin

        return image

class WeatherToolbarModule:

    def __init__(self):
        self.label_margin = 3

    def draw_module(self, config, width, height):
        image = Image.new('RGB', (width, height), 'black')

        draw = ImageDraw.Draw(image)
        fnt_R = ImageFont.truetype(config.fnt_R_path, 16)
        fnt_B = ImageFont.truetype(config.fnt_RB_path, 20)

        weather = WeatherAPI().get_currently()

        infos = {
            "Vent": "%d km/h" % weather["wind_speed"],
            "Humidité": "%d%%" % (weather["humidity"]),
            "Indice UV": str(int(weather["uvi"])),
            "Pression": "%s hPa" % weather["pressure"],
        }

        txt = draw_format_infos(infos, width, height, fnt_R, fnt_B, self.label_margin, align_height=height/2, anchor="lm")
        image.paste(txt, (0,0), txt)

        return image

class WeatherJumboCurrentModule:

    def __init__(self):
        self.middle_align = 1/3

    def draw_module(self, config, width, height):
        image = Image.new('RGB', (width, height), '#fff')

        draw = ImageDraw.Draw(image)
        fnt_Big = ImageFont.truetype(config.fnt_RB_path, 33)
        fnt_R = ImageFont.truetype(config.fnt_R_path, 16)
        fnt_B = ImageFont.truetype(config.fnt_RB_path, 16)

        # current
        curweather = WeatherAPI().get_currently()

        icon = Image.open(os.path.join(config.icons_dir, "weather", WeatherAPI.get_icon(curweather["weather"][0]["id"], night=curweather["weather"][0]["icon"].endswith("n"), current=True))).resize((height, height))
        image.paste(icon, (int(width*self.middle_align - height), 0), icon)

        draw.text(
            (width*self.middle_align, height/3),
            "%d˚ %s." % (math.trunc(curweather["temp"]), curweather["weather"][0]["description"]),
            fill="black", anchor="ld", font=fnt_Big
        )

        thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 1))[0]

        infos = {
            "Ressentie": "%d˚" % curweather["feels_like"],
            "Minimale": "%d˚" % thisdayweather["temp"]["min"],
            "Maximale": "%d˚" % thisdayweather["temp"]["max"],
        }

        txt = draw_format_infos(infos, (1-self.middle_align) * width, 20, fnt_R, fnt_B, 5, margin_bf_first=False, fill="black", anchor="lt")
        image.paste(txt, (int(self.middle_align*width),int(height/2.6)), txt)

        # day
        fnt_Rig = ImageFont.truetype(config.fnt_R_path, 20)

        dayweather = list(itertools.islice(WeatherAPI().get_hourly(), 2))

        if dayweather[0]["weather"][0]["description"] != curweather["weather"][0]["description"]:
            description = dayweather[0]["weather"][0]["description"]
            description = description[0].upper() + description[1:]
            display_longtext(draw, (width*self.middle_align, height/1.28), description + " la prochaine heure.\n" + thisdayweather["summary"] + ".", fill="black", anchor="lm", font=fnt_Rig, maxwidth=(1-self.middle_align)*width)
        else:
            display_longtext(draw, (width*self.middle_align, height/1.28), thisdayweather["summary"] + ".", fill="black", anchor="lm", font=fnt_Rig, maxwidth=(1-self.middle_align)*width)

        return image

class WeatherMoonPhaseModule:

    def draw_module(self, config, width, height):
        image = Image.new('RGBA', (width, height), '#fff0')

        icon = Image.open(os.path.join(config.icons_dir, "weather", WeatherAPI().get_moon_icon())).resize((height, height))
        image.paste(icon, (0,0), icon)

        return image


class WeatherSunModule:

    def draw_module(self, config, width, height, start_align=0):
        image = Image.new('RGB', (width, height), '#fff')
        draw = ImageDraw.Draw(image)
        fnt_R = ImageFont.truetype(config.fnt_R_path, int(height*0.7))

        thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 1))[0]

        import time
        infos = {
            "sunrise": WeatherAPI().read_timestamp(thisdayweather["sunrise"]),
            "sunset": WeatherAPI().read_timestamp(thisdayweather["sunset"]),
        }

        align = start_align
        for icon, info in infos.items():
            if info == "":
                continue
            info = info.strftime("%H:%M:%S")
            icon = Image.open(os.path.join(config.icons_dir, "weather", "wi-" + icon + ".png")).resize((height, height))
            image.paste(icon, (int(align),0), icon)
            align += height + 2
            draw.text(
                (align, height / 2),
                str(info),
                fill="black", anchor="lm", font=fnt_R
            )
            align += fnt_R.getlength(str(info))
            align += 10

        return image


class WeatherRainModule:

    def draw_module(self, config, width, height):
        if datetime.now().hour >= 21:
                thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 2))[1]
        else:
                thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 1))[0]

        gauge = pygal.SolidGauge(config.pygal_config, half_pie=True, inner_radius=0.70, width=width, height=height*1.55, style=config.pygal_custom_style, show_legend=False, margin_top=-height*0.58, margin_left=1, margin_right=1)
        percent_formatter = lambda x: '{:.10g}%'.format(x)
        gauge.value_formatter = percent_formatter

        if thisdayweather["pop"] > 0:
                gauge.add('Pluie', [{'value': thisdayweather["pop"] * 100}])
                if "wind_gust" in thisdayweather and thisdayweather["wind_gust"] > 45:
                        icon_path = "wi-wind-beaufort-6.png"
                else:
                        icon_path = "wi-umbrella.png"
        elif thisdayweather["uvi"] > 4:
                gauge.add('Index UV', [{'value': thisdayweather["uvi"] * 10}])
                icon_path = "wi-hot.png"
        elif thisdayweather["wind_gust"] > 40:
                gauge.add('Vent', [{'value': thisdayweather["wind_gust"], 'color': '#999'}])
                icon_path = "wi-strong-wind.png"
        elif "air_quality" in thisdayweather:
                gauge.add("Qualité de l'air", [{'value': thisdayweather["air_quality"]["gb-defra-index"] * 10}])
                if thisdayweather["air_quality"]["gb-defra-index"] >= 5:
                        icon_path = "wi-smog.png"
                else:
                        icon_path = "wi-smoke.png"
        else:
                gauge.add('Vent', [{'value': thisdayweather["wind_speed"], 'color': '#999'}])
                icon_path = "wi-wind-beaufort-1.png"

        image = Image.open(io.BytesIO(gauge.render_to_png()))

        if icon_path:
                icon = Image.open(os.path.join(config.icons_dir, "weather", icon_path)).resize((int(height/1.25), int(height/1.25)))
                image.paste(icon, (int(width/2-height/2.5), int(height-height/1.25)), icon)

        return image

class WeatherTemperatureModule:

    def __init__(self):
        self.limit_futur = 30

    def draw_module(self, config, width, height):
        thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 2))
        if datetime.now().hour >= 19 and thisdayweather[1]["pop"] > thisdayweather[0]["pop"]:
                thisdayweather = thisdayweather[1]
        else:
                thisdayweather = thisdayweather[0]

        hours_weather = [h for h in WeatherAPI().get_hourly()]

        hourly_min = 0
        hourly_max = 0
        for h in hours_weather:
            if hourly_min > h["temp"]:
                hourly_min = h["temp"]
            if hourly_max < h["temp"]:
                hourly_max = h["temp"]

        line_chart = pygal.Line(config.pygal_config, interpolate='hermite', interpolation_parameters={'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': 1}, width=width+10, height=height, inverse_y_axis=False, x_label_rotation=45, range=(hourly_min, hourly_max), secondary_range=(0,100) if thisdayweather["pop"] > 0 else (0,10), **config.charts_opts)
        line_chart.value_formatter = lambda x: "%d" % x

        line_chart.x_labels = [WeatherAPI().read_timestamp(d["dt"]).strftime("%Hh") if WeatherAPI().read_timestamp(d["dt"]).hour % 2 == 0 else "" for d in hours_weather[:self.limit_futur]]

        line_chart.add('Températures', [d["temp"] for d in hours_weather[:self.limit_futur]], show_dots=False)

        if thisdayweather["pop"] > 0:
            line_chart.add('Précipitations', [d["pop"]*100 for d in hours_weather[:self.limit_futur]], secondary=True, show_dots=False, fill=True if hourly_min == 0 else False)
        else:
            line_chart.add('Index UV', [d["uvi"] for d in hours_weather[:self.limit_futur]], secondary=True, show_dots=False)

        img = Image.open(io.BytesIO(line_chart.render_to_png()))
        draw = ImageDraw.Draw(img)

        # Add EcoWatt signal
        #for ecowatt
        #draw.rectangle(
        #        (15 + day_size + int((day["temperatureLow"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["temperatureHigh"]-t_min)*t_scale),i*day_size + 14),
        #        fill="black")

        return img

class WeeklyWeatherModule:

    def __init__(self):
        self.first_day = 1
        self.limit_futur = 5

    def draw_module(self, config, width, height):
        image = Image.new('RGB', (width, height), 'white')
        draw = ImageDraw.Draw(image)
        fnt_R = ImageFont.truetype(config.fnt_R_path, 14)
        fnt_B = ImageFont.truetype(config.fnt_RB_path, 14)

        weekweather = [d for d in WeatherAPI().get_daily()]

        nbdays = len(weekweather[self.first_day:self.first_day+self.limit_futur])
        day_size = min(40, int(height / (nbdays + 1)))

        display_longtext(draw,
                (day_size + (width - day_size)/2, day_size / 2 + 5),
                weekweather["summary"] if "summary" in weekweather else "",
                fill="black", anchor="mm", font=fnt_B,
                maxwidth=width - day_size
        )

        temp = []
        for day in weekweather[max(0,self.first_day-1):self.first_day+self.limit_futur]:
                temp.append(day["temp"]["min"])
                temp.append(day["temp"]["max"])
        t_min = min(temp)
        t_max = max(temp)
        t_scale = (width - day_size - 30) / (t_max - t_min)

        i = 1
        for day in weekweather[self.first_day:self.first_day+self.limit_futur]:
            icon = Image.open(os.path.join(config.icons_dir, "weather", WeatherAPI.get_icon(day["weather"][0]["id"]))).resize((day_size, day_size))
            image.paste(icon, (0, i * day_size), icon)

            draw.text(
                (15 + 2 + day_size + int((day["temp"]["min"]-t_min)*t_scale), i*day_size + 4),
                "%d˚" % math.trunc(day["temp"]["min"]),
                fill="black", anchor="rt", font=fnt_R
            )
            summary_size = fnt_R.getlength(day["date"].strftime("%a") + " : " + day["weather"][0]["description"])
            draw.text(
                (day_size + (width - day_size - summary_size) / 2, (i + 1) * day_size - 6),
                day["date"].strftime("%a") + " : ",
                fill="#666", anchor="ls", font=fnt_R
            )
            draw.text(
                (day_size + (width - day_size + summary_size) / 2, (i + 1) * day_size - 6),
                day["weather"][0]["description"],
                fill="black", anchor="rs", font=fnt_R
            )
            draw.text(
                (day_size + int((day["temp"]["max"]-t_min)*t_scale) + 2, i * day_size + 4),
                "%d˚" % math.trunc(day["temp"]["max"]),
                fill="black", anchor="lt", font=fnt_R
            )

            try:
                    draw.rounded_rectangle(
                            (15 + day_size + int((day["temp"]["min"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["temp"]["max"]-t_min)*t_scale),i*day_size + 14),
                            radius=5, fill="black")
            except AttributeError:
                    draw.rectangle(
                            (15 + day_size + int((day["temp"]["min"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["temp"]["max"]-t_min)*t_scale),i*day_size + 14),
                            fill="black")
            i += 1


        return image

class WeatherAlerts:

    def gen_alerts(self):
        alerts = []
        lastStartTime = 0
        lastEndTime = 0

        for alert in WeatherAPI().get_alerts():
                icon = "weather/wi-gale-warning.png"
                startTime = WeatherAPI().read_timestamp(alert["start"])
                endTime = WeatherAPI().read_timestamp(alert["end"]-1)
                vendTime = WeatherAPI().read_timestamp(alert["end"])

                # Show alert timing if under a day
                if startTime.hour != vendTime.hour:
                        subtitle = startTime.strftime(("%x " if startTime.day != datetime.now().day else "") + "%X") + " - " + endTime.strftime(("%x " if startTime.day != endTime.day else "") + "%X")
                elif startTime.day != datetime.now().day:
                        subtitle = startTime.strftime("%x")
                else:
                        subtitle = ""

                if len(alerts) >= 1 and alerts[len(alerts)-1]["title"] == alert["event"]:
                        startTime = lastStartTime
                        if startTime.hour != vendTime.hour:
                                alerts[len(alerts)-1]["subtitle"] = startTime.strftime(("%x " if startTime.day != datetime.now().day else "") + "%X") + " - " + endTime.strftime(("%x " if startTime.day != endTime.day else "") + "%X")
                        elif startTime.day != datetime.now().day:
                                alerts[len(alerts)-1]["subtitle"] = startTime.strftime("%x")
                        continue
                lastStartTime = startTime
                lastEndTime = vendTime

                alerts.append({
                        "icon": icon,
                        "title": alert["event"],
                        "subtitle": subtitle,
                        "description": alert["description"],
                })

        return alerts


class WeatherAirQualityModule:

    def __init__(self):
        self.label_margin = 3

    def draw_module(self, config, width, height):
        image = Image.new('RGB', (width, height), 'white')

        draw = ImageDraw.Draw(image)
        fnt_R = ImageFont.truetype(config.fnt_R_path, 16)
        fnt_B = ImageFont.truetype(config.fnt_RB_path, 20)

        weather = WeatherAPI().get_currently()

        infos1 = {
            "CO": "%d μg/m³" % weather["air_quality"]["co"],
            "O3": "%d μg/m³" % (weather["air_quality"]["o3"]),
            "NO2": "%d μg/m³" % (weather["air_quality"]["no2"]),
        }
        infos2 = {
            "SO2": "%d μg/m³" % (weather["air_quality"]["so2"]),
            "PM2.5": "%d μg/m³" % (weather["air_quality"]["pm2_5"]),
            "PM10": "%d μg/m³" % (weather["air_quality"]["pm10"]),
        }

        txt = draw_format_array(infos1, width, height/2, fnt_R, fnt_B, self.label_margin, align_height=height/4, anchor="mm", fill="black")
        image.paste(txt, (0,0), txt)

        txt = draw_format_array(infos2, width, height/2, fnt_R, fnt_B, self.label_margin, align_height=height/4, anchor="mm", fill="black")
        image.paste(txt, (0,int(height/2)), txt)

        return image
