from PIL import Image, ImageDraw, ImageFont
import textwrap
import time
import requests
from io import BytesIO
import warnings
from datetime import datetime
import yaml
from typing import Dict

from lib import LCD_2inch
from api import Api


def parse_config(path="spotiplayer_pi/config.yaml"):
    with open(path, "r") as file:
        data = yaml.safe_load(file)
    for key, value in data["color_theme"].items():
        data["color_theme"][key] = tuple(value)
    return data


def display_loop(api: Api, cfg: Dict):
    try:
        d = LCD_2inch()
        d.Init()
        d.clear()
        bg = Image.new("RGB", (d.height, d.width), (0, 0, 0))
        d.ShowImage(bg)

        Font0 = ImageFont.truetype("Font/Font00.ttf", 14)
        Font1 = ImageFont.truetype("Font/Font00.ttf", 18)
        Font2 = ImageFont.truetype("Font/Font00.ttf", 36)

        unavailable_img = Image.open("imgs/unavailable.jpg")
        error_img = Image.new("RGB", (240, 320), color=(255, 0, 0))
        draw = ImageDraw.Draw(error_img)
        draw.text((150, 150), "Error", font=Font1, fill=cfg["color_theme"]["text"])
        del draw

        spoti_logo = Image.open("imgs/logo128.jpg")
        qr = Image.open("imgs/qr.jpg")
        spoti_logo = spoti_logo.resize((128, 128))
        not_playing_img = Image.new("RGB", (320, 240), (0, 0, 0))
        not_playing_img.paste(spoti_logo, (0, 10))
        not_playing_img.paste(qr, (120, 40))
        draw = ImageDraw.Draw(not_playing_img)
        draw.text(
            (124, 10),
            "Connect to speakers",
            font=Font1,
            fill=cfg["color_theme"]["text"],
        )
        del spoti_logo, qr, draw

        last_api_call = 0
        last_auth_refresh = 0
        current_mode = None
        last_track = None
        data = None
        auth_interval = 0

        while True:
            if time.time() - last_auth_refresh >= auth_interval:
                auth_interval = api.refreshAuth() - 120
                last_auth_refresh = time.time()
                print(f"Refreshed auth at {datetime.now().strftime('%d-%m %H:%M:%S')}")

            if time.time() - last_api_call >= cfg["api_interval"]:
                data = api.getPlaying()
                last_api_call = time.time()

            if data == None:
                warnings.warn("No data found")
                d.ShowImage(error_img)

            elif data == "not-playing":
                draw = ImageDraw.Draw(not_playing_img)
                current_time = datetime.now().time()
                draw.rectangle([(00, 120), (119, 170)], fill=(0, 0, 0))
                draw.text(
                    (20, 120),
                    f"{current_time.hour}:{current_time.minute:02d}",
                    font=Font2,
                    angle=0,
                )
                if current_mode != 0:
                    current_mode = 0
                    print("Standby mode")
                d.ShowImage(not_playing_img)
                time.sleep(cfg["api_interval"])

            elif type(data) == dict:
                current_track = data["track"] + data["artists"][0] + data["album"]
                if current_track != last_track:
                    print("updating track")
                    last_track = current_track
                    img = None
                    try:
                        img = requests.get(
                            data["img_url"], timeout=cfg["refresh_interval"] / 2
                        )
                        img = Image.open(BytesIO(img.content))
                    except Exception as e:
                        warnings.warn(
                            f"Failed to fetch album cover at: {data['img_url']}\n{e}"
                        )
                    if img == None:
                        img = unavailable_img
                    img = img.resize((128, 128))
                    bg = Image.new("RGB", (320, 240), cfg["color_theme"]["background"])
                    bg.paste(img, (10, 30))
                    draw = ImageDraw.Draw(bg)
                    draw.text(
                        (150, 40),
                        "\n".join(textwrap.wrap(", ".join(data["artists"]), width=16)),
                        font=Font1,
                        fill=cfg["color_theme"]["text"],
                    )
                    draw.text(
                        (10, 160),
                        "\n".join(textwrap.wrap(data["track"], width=32)),
                        font=Font1,
                        fill=cfg["color_theme"]["text"],
                    )

                w, h = bg.size
                w -= 92
                progress_time = data["progress_ms"] + int(
                    (time.time() - last_api_call) * 1000
                )
                progress = progress_time / data["duration_ms"]
                bar_width = int(w * progress)
                draw.rectangle(
                    [(8, h - 22), (w + 2, h - 8)],
                    outline=cfg["color_theme"]["bar_outline"],
                )
                draw.rectangle(
                    [(10, h - 20), (w, h - 10)], fill=cfg["color_theme"]["background"]
                )
                draw.rectangle(
                    [(10, h - 20), (bar_width, h - 10)],
                    fill=cfg["color_theme"]["bar_inside"],
                )
                f_current_time = "{:02.0f}:{:02.0f}".format(
                    *divmod(progress_time // 1000, 60)
                )
                f_total_time = "{:02d}:{:02d}".format(
                    *divmod(data["duration_ms"] // 1000, 60)
                )
                draw.rectangle(
                    [(234, 215), (310, 235)], fill=cfg["color_theme"]["background"]
                )
                draw.text(
                    (235, 215),
                    f"{f_current_time}/{f_total_time}",
                    font=Font0,
                    fill=cfg["color_theme"]["text"],
                )
                d.ShowImage(bg)
                time.sleep(cfg["refresh_interval"])

    except IOError as e:
        raise e
    except KeyboardInterrupt:
        d.module_exit()


if __name__ == "__main__":
    cfg = parse_config()
    api = Api(cfg["refresh_token"], cfg["base_64"])
    display_loop(api, cfg)
