from PIL import Image, ImageFont, ImageDraw
import os
import os.path as path
import logging

from . import utils

FONT_DIR = utils.relative_path(__file__, "..", "fonts")
TEMPLATES_DIR = utils.relative_path(__file__, "..", "templates")

FONTS = {}

logger = logging.getLogger("img_factory")


def load_fonts():
    """
    TODO
    """
    for file in [f for f in os.listdir(FONT_DIR) if path.isfile(path.join(FONT_DIR, f))]:
        split = path.splitext(file)
        if split[-1] == ".ttf":
            try:
                FONTS[split[0]] = ImageFont.truetype(path.join(FONT_DIR, file))
                logger.info(f"Loaded font '{split[0]}'")
            except OSError:
                logger.error(f"Could not load font '{split[0]}'")


def make(template, texts, debug=False):
    """
    TODO

    :param (str) template:
    :param (list of Text) texts:
    :param (bool) debug:
    :rtype: PIL.Image.Image
    :return:
    """
    try:
        img = Image.open(path.join(TEMPLATES_DIR, template)).convert(mode='RGBA')
    except OSError as e:
        logger.error(f"Could not read template file '{template}': {e}")
        return None
    draw = ImageDraw.Draw(img)

    for text in texts:
        draw_text(draw, img, text, debug=debug)

    return img.convert(mode='RGB')


def draw_text(draw, img, text, debug=False):
    """
    TODO

    :param (PIL.ImageDraw.ImageDraw) draw: source image canvas
    :param (PIL.Image.Image) img: source image
    :param (Text) text:
    :param (bool) debug:
    """
    # TODO rotation
    # https://stackoverflow.com/questions/245447/how-do-i-draw-text-at-an-angle-using-pythons-pil
    if text.text is not None and len(text.text.strip()) > 0:
        text.init()  # load default values
        if text.font in FONTS:
            text.text, font = fit_text(img.size, text)
            if text.angle == 0:
                draw.text(get_pos(img.size, text, font), text.text, fill=text.fill, align=text.align, font=font,
                          stroke_width=round(text.stroke_width * font.size), stroke_fill=text.stroke_fill)
                if debug:
                    draw.rectangle([(text.x_range[0] * img.size[0], text.y_range[0] * img.size[1]),
                                    (text.x_range[1] * img.size[0], text.y_range[1] * img.size[1])],
                                   None, (128, 128, 128))
            else:
                width = round((text.x_range[1] - text.x_range[0]) * img.size[0])
                height = round((text.y_range[1] - text.y_range[0]) * img.size[1])
                center_x = (text.x_range[0] + text.x_range[1]) * img.size[0] / 2
                center_y = (text.y_range[0] + text.y_range[1]) * img.size[1] / 2
                txt_img = Image.new('RGBA', (width, height))
                txt_draw = ImageDraw.Draw(txt_img)
                txt_draw.text(get_pos(img.size, text, font, relative=True), text.text, fill=text.fill,
                              align=text.align, font=font, stroke_width=round(text.stroke_width * font.size),
                              stroke_fill=text.stroke_fill)
                if debug:
                    txt_draw.rectangle([(0, 0), (width - 1, height - 1)],
                                       None, (128, 128, 128))
                txt_img = txt_img.rotate(text.angle, expand=1, resample=Image.BILINEAR)
                img.paste(txt_img,
                          (round(center_x - txt_img.size[0] / 2), round(center_y - txt_img.size[1] / 2)),
                          txt_img)
        else:
            logger.warning(f"Invalid font '{text.font}'")


def fit_text(size, text):
    """
    :param (int,int) size: source image size
    :param (Text) text:
    :rtype: (str, PIL.ImageFont.FreeTypeFont)
    :return:
    """
    max_width = round(size[0] * (text.x_range[1] - text.x_range[0]))
    max_height = round(size[1] * (text.y_range[1] - text.y_range[0]))
    text_size = None
    font_size = round(text.font_size * min(size)) + 1
    font = FONTS[text.font]
    t = ""
    while (text_size is None or text_size[1] >= max_height) and font_size > 1:
        font_size -= 1
        font = font.font_variant(size=font_size)
        words = text.text.split(" ")
        t = ""
        for word in words:
            spacer = " "
            if len(t) == 0:
                spacer = ""
            text_size = font.getsize_multiline(t + spacer + word, stroke_width=text.stroke_width * font_size)
            if text_size[0] >= max_width:
                t += "\n" + word
            else:
                t += spacer + word
        text_size = font.getsize_multiline(t, stroke_width=text.stroke_width * font_size)
    return t, font


def get_pos(size, text, font, relative=False):
    """
    TODO

    :param (int,int) size: source image size
    :param (Text) text:
    :param (PIL.ImageFont.FreeTypeFont) font:
    :rtype (int,int)
    :return:
    """
    min_x = round(text.x_range[0] * size[0])
    max_x = round(text.x_range[1] * size[0])
    min_y = round(text.y_range[0] * size[1])
    max_y = round(text.y_range[1] * size[1])
    text_size = font.getsize_multiline(text.text, stroke_width=text.stroke_width * font.size)

    if int(text.position.value) // 3 == 0:
        pos_y = min_y
    elif int(text.position.value) // 3 == 1:
        pos_y = round((min_y + max_y) / 2 - text_size[1] / 2)
    else:
        pos_y = max_y - text_size[1]

    if int(text.position.value) % 3 == 0:
        pos_x = min_x
    elif int(text.position.value) % 3 == 1:
        pos_x = round((min_x + max_x) / 2 - text_size[0] / 2)
    else:
        pos_x = max_x - text_size[0]
    if relative:
        return pos_x - min_x, pos_y - min_y
    else:
        return pos_x, pos_y
