import os
import os.path
import logging
from tempfile import NamedTemporaryFile
import sys

from PIL import Image, ImageDraw, ImageFont

class Config:

    def __init__(self, cache_timeout, max_cache_timeout, cals_file_list=".cals"):
        self.icons_dir = os.path.join(os.path.dirname(sys.argv[0]), "icons")
        self.fonts_dir = os.path.join(os.path.dirname(sys.argv[0]), "fonts")
        self.cache_timeout = cache_timeout
        self.max_cache_timeout = max_cache_timeout

        if os.path.exists('/usr/share/fonts/source-pro/'):
            self.fnt_R_path = "/usr/share/fonts/source-pro/SourceSansPro-Regular.otf"
            self.fnt_RI_path = "/usr/share/fonts/source-pro/SourceSansPro-It.otf"
            self.fnt_RB_path = "/usr/share/fonts/source-pro/SourceSansPro-Bold.otf"
        elif os.path.exists('/usr/share/fonts/source-sans/'):
            self.fnt_R_path = "/usr/share/fonts/source-sans/SourceSans3-Regular.otf"
            self.fnt_RI_path = "/usr/share/fonts/source-sans/SourceSans3-It.otf"
            self.fnt_RB_path = "/usr/share/fonts/source-sans/SourceSans3-Bold.otf"
        elif os.path.exists('/usr/share/fonts/libertine'):
            self.fnt_R_path = "/usr/share/fonts/libertine/LinBiolinum_Rah.ttf"
            self.fnt_RI_path = "/usr/share/fonts/libertine/LinBiolinum_RIah.ttf"
            self.fnt_RB_path = "/usr/share/fonts/libertine/LinBiolinum_RBah.ttf"
        else:
            self.fnt_R_path = "/usr/share/fonts/TTF/LinBiolinum_Rah.ttf"
            self.fnt_RI_path = "/usr/share/fonts/TTF/LinBiolinum_RIah.ttf"
            self.fnt_RB_path = "/usr/share/fonts/TTF/LinBiolinum_RBah.ttf"

        try:
            with open(cals_file_list) as f:
                self.cals = [c for c in f.read().split("\n") if len(c) > 0]
        except:
            print("No calendar found. Please create a file " + cals_file_list)
            self.cals = []

        self.css_overlay = NamedTemporaryFile()
        with open(self.css_overlay.name, 'w') as f:
            f.write('''
{{ id }} .bound {
    font-size: 0.9em;
}
{{ id }} .gauge-background {
    fill: white;
    stroke: black;
}
{{ id }} .gauge path {
    fill-opacity: 1 !important;
}
{{ id }} .series .line {
    stroke-width: 2 !important;
}
{{ id }} .x .guides .line {
    stroke-width: 0 !important;
}
{{ id }} .y .guides .line {
    stroke: #555;
    stroke-width: 0.7;
}
{{ id }} .y .guides .line.axis {
    stroke: #000;
    stroke-width: 1;
}
            ''')

        import pygal
        self.pygal_config = pygal.Config()
        self.pygal_config.css.append('file://' + self.css_overlay.name)

        from pygal.style import Style
        self.pygal_custom_style = Style(
            background='white',
            foreground='black',
            font_family='Source Code Pro',
            gauge_background="red",
            colors=('#000', '#777'))

        self.charts_opts = {'style': self.pygal_custom_style, 'show_legend': False, 'margin_right': 7, 'margin_left': 2, 'margin_bottom': 4, 'margin_top': 7}


class RuleModule:

    def __init__(self):
        self.coeff = 0.92

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

        draw.rectangle((int((1-self.coeff) * width), 0, int(self.coeff * width), height), fill="black")

        return image


class AlertsModule:

    def __init__(self, config, alerts=[], ignore_module=[]):
        self._config = config
        self.icon_size = 50
        self.alerts = alerts
        self.ignore_module = ignore_module

    def draw_alert(self, alert, width, image, draw, fnt_R, fnt_B, align, font_size):
        if "icon" in alert and alert["icon"] is not None:
            if callable(alert["icon"]):
                icon_img = alert["icon"](size=int(font_size*self.icon_size/16))
                image.paste(icon_img, (int(self.icon_size / 2 - font_size*self.icon_size/32), align))
            else:
                color_img = Image.new('RGB', (self.icon_size, self.icon_size), "#fff")
                icon_img = Image.open(os.path.join(self._config.icons_dir, alert["icon"])).resize((self.icon_size, self.icon_size))
                image.paste(color_img, (0, align - 5), icon_img)

        if "title" in alert:
            draw.text(
                ((self.icon_size if alert["icon"] is not None else 0) - 5, align + 12),
                alert["title"],
                fill="white", anchor="ls", font=fnt_B
            )
        if "subtitle" in alert and alert["subtitle"]:
                draw.text(
                        ((self.icon_size if alert["icon"] is not None else 0) + (fnt_B.getlength(alert["title"]) if "title" in alert else 0), align + 12),
                        alert["subtitle"],
                        fill="white", anchor="ls", font=fnt_R
                )

        if "title" in alert:
            align += fnt_B.getbbox(alert["title"])[3]

        align += display_longtext(
                draw,
                (self.icon_size - 5, align),
                alert["description"],
                fill="white", font=fnt_R,
                maxwidth=width-self.icon_size-5,
                maxlines=1 if "oneline" in alert and alert["oneline"] else 99,
        )

        return align + 7


    def draw_module(self, config, width, height, font_size=16):
        image = Image.new('RGBA', (width, height), "#000")
        draw = ImageDraw.Draw(image)
        align = 0

        more_alerts = 0
        if len(self.alerts) > 0:
            fnt_R = ImageFont.truetype(config.fnt_R_path, font_size)
            fnt_B = ImageFont.truetype(config.fnt_RB_path, font_size)

            align = 7
            for alert in self.alerts:
                args_module = []
                args_func = []
                if isinstance(alert, dict) and "module" in alert:
                    args_module = alert["args_module"] if "args_module" in alert else []
                    args_func = alert["args_func"] if "args_func" in alert else []
                    alert = alert["module"]

                if isinstance(alert, type):
                    if alert.__name__ in self.ignore_module:
                        logging.info("Skip module " + alert.__name__ + ", as requested by arguments")
                        continue

                    try:
                        for alert in alert(*args_module).gen_alerts(*args_func):
                            align = self.draw_alert(alert, width, image, draw, fnt_R, fnt_B, align, font_size)
                    except BaseException as e:
                        logging.exception(e)
                        align = self.draw_alert({
                            "title": "Impossible de générer les alertes de " + alert.__name__,
                            "description": type(e).__name__ + ": " + (e.message if hasattr(e, 'message') else str(e)),
                            "icon": "weather/wi-earthquake.png",
                        }, width, image, draw, fnt_R, fnt_B, align, font_size)
                else:
                    align = self.draw_alert(alert, width, image, draw, fnt_R, fnt_B, align, font_size)

                if align > height:
                    more_alerts += 1

        image = image.crop((0,0,width, min(align, height)))

        if more_alerts > 0 and font_size > 12:
            return self.draw_module(config, width, height, font_size-1)
        elif more_alerts > 0:
            draw = ImageDraw.Draw(image)
            draw.rectangle((0, height-font_size, width, height), fill="black")
            draw.text(
                (width - 3, height),
                ("%d alertes supplémentaires" if more_alerts > 1 else "%d alerte supplémentaire") % more_alerts,
                fill="white", anchor="rb", font=fnt_B
            )

        return image, more_alerts

def display_longtext(draw, pos, text, font, anchor="lt", maxwidth=9999, maxlines=999, **kwargs):
        x,y = pos
        lines = []

        mainlines = text.split("\n")
        for line in mainlines:
            words = line.split(" ")

            cur = ""
            while len(words) > 0:
                    added = (" " if len(cur) > 0 else "") + words[0]
                    width = font.getlength(cur + added)
                    if width < maxwidth:
                            words.pop(0)
                            cur += added
                    else:
                            lines.append(cur)
                            cur = words.pop(0)

            if len(cur) > 0:
                    lines.append(cur)

            if len(lines) >= maxlines:
                lines = lines[0:maxlines]
                lines[-1] += "…"
                break

        line_height = font.getbbox("test")[3]
        if anchor[1] == "m":
                y -= line_height * len(lines) / 2
        elif anchor[1] == "b":
                y -= line_height * len(lines)
        for line in lines:
                draw.text((x,y), line, font=font, anchor=anchor, **kwargs)
                y += line_height

        return line_height * len(lines)
