#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# External Dependencies
import kivy

kivy.require("2.3.1")

# Standard Library
import logging
import queue
from datetime import datetime, timedelta
from glob import glob
from os.path import dirname, join
from random import randint

from . import utils

# External Dependencies
from kivy.animation import Animation
from kivy.app import App
from kivy.clock import Clock
from kivy.config import Config
from kivy.core.window import Window
from kivy.loader import Loader
from kivy.logger import LOG_LEVELS, Logger
from kivy.uix.screenmanager import FadeTransition, Screen, ScreenManager

Window.fullscreen = "auto"
Window.show_cursor = False
logging.getLogger("PIL").setLevel(LOG_LEVELS["info"])
logger = logging.getLogger(__name__)
SSDP_PORT = 1900
SSDP_ADDR = "239.255.255.250"
UUID = utils.get_uuid()
SERV_IP = utils.get_ip_address()
logger.debug("IP ADDRESS: %s", SERV_IP)
SERV_PORT = 8008
NAME = utils.get_friendly_name()
Config.set("kivy", "exit_on_escape", 0)


class DefaultScreen(Screen):
    timer = None

    def on_pre_enter(self, *args, **kwargs):
        self.update(0)

        image_id = self.manager.pick_bg()
        self.ids["background"].source = image_id

        self.timer = Clock.schedule_interval(self.update, 1.0)

    def on_leave(self, *args):
        self.timer.cancel()

    def update(self, _dt):
        self.ids["time"].text = datetime.now().strftime("%-I:%M")


class IdleScreenA(DefaultScreen):
    pass


class IdleScreenB(DefaultScreen):
    pass


class VideoScreen(Screen):
    controls_hide_anim = Animation(opacity=0, duration=0.5)
    controls_show_anim = Animation(opacity=1, duration=0.1)
    duration = 0
    pending_play = False

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ids["video"].bind(
            position=self.on_position_change,
            duration=self.on_duration_change,
            eos=self.on_eos_change,
            loaded=self.on_loaded_change,
            state=self.on_state_change,
        )

    def on_eos_change(self, *args):
        utils.ironcast.state = "STOPPED"
        self.manager.current = "ScreenA"

    def on_position_change(self, _vid_object, pos):
        self.ids["videobar"].value = pos
        self.ids["time"].text = (
            f"{str(timedelta(seconds=int(pos) + utils.ironcast.current_offset))} / "
            f"{str(timedelta(seconds=int(utils.ironcast.duration)))}"
        )
        utils.ironcast.position = int(pos) + utils.ironcast.current_offset

    def on_duration_change(self, _vid_object, duration):
        if duration in [1.0, 0.0]:
            return
        logger.error("VIDEO: DURATION CHANGED TO %s, cached is: %s", duration, utils.ironcast.duration)
        self.ids["videobar"].max = duration
        self.ids["videobar"].value = 0
        self.duration = duration

    def on_loaded_change(self, video, _is_loaded):
        logger.error("VIDEO: Loaded pending play %s", repr(video.pending_play))
        if video.pending_play:
            video.state = "play"
            utils.ironcast.state = "PLAYING"
            video.pending_play = False
        else:
            video.state = "pause"
        self.manager.current = "VideoScreen"

    def on_state_change(self, _video, new_state):
        logger.error("VIDEO: state change to %s", repr(new_state))
        if new_state == "play":
            self.ids["play_button"].opacity = 0
            self.ids["pause_button"].opacity = 1
            Clock.schedule_once(self.hide_controls, 3)
        else:
            self.ids["play_button"].opacity = 1
            self.ids["pause_button"].opacity = 0
            self.controls_show_anim.start(self.ids["controls"])

    def hide_controls(self, _dt):
        self.controls_hide_anim.start(self.ids["controls"])

    def on_leave(self, *args, **kwargs):
        self.ids["video"].state = "pause"
        self.ids["controls"].opacity = 0


class IronManager(ScreenManager):
    background_images = []
    current_image_id = ""
    pending_play = False

    def __init__(self, *args, **kwargs):
        self.load_bg(_dt=None)
        super().__init__(*args, **kwargs)
        Clock.schedule_interval(self.update_bg, 60.0)  # Every 5m, rotate
        Clock.schedule_interval(self.load_bg, 1800.0)  # every 30m, check for more images.
        Clock.schedule_interval(self.handle_ironcast_event, 1 / 30.0)

    def update_bg(self, _dt):
        if self.current == "ScreenA":
            self.current = "ScreenB"
        elif self.current == "ScreenB":
            self.current = "ScreenA"

    def handle_ironcast_event(self, _dt):
        video_screen = self.get_screen("VideoScreen")
        video = video_screen.ids["video"]
        while True:
            try:
                event, args = utils.ironcast.gui_queue.get(block=False)
                logger.info("EVENT: %s %s", event, str(args))
                if event == "play":
                    if video.loaded:
                        video.state = "play"
                        utils.ironcast.state = "PLAYING"
                    else:
                        utils.ironcast.state = "TRANSITIONING"
                        video.pending_play = True
                elif event == "pause":
                    utils.ironcast.state = "PAUSED_PLAYBACK"
                    video.state = "pause"
                elif event == "stop":
                    utils.ironcast.state = "STOPPED"
                    video.state = "stop"
                elif event == "source":
                    self.pending_play = False
                    video.source = args[0]
                    # TODO: replace with IronVideo class that handles pre-buffering properly.
                    video.state = "play"
                    if video.preview:
                        self.current = "VideoScreen"
                elif event == "preview":
                    video.preview = args[0]
                elif event == "seek":
                    video.seek(args[0] / utils.ironcast.duration, precise=True)
                else:
                    logger.error("EVENT: Unknown event %s %s", event, str(args))
            except queue.Empty:
                break

    def load_bg(self, _dt=None):
        curdir = dirname(__file__)
        for filename in glob(join(curdir, "data", "*")):
            if filename not in self.background_images:
                self.background_images.append(filename)
                Logger.info("Loaded BG image %s", filename)

    def pick_bg(self):
        new_id = self.current_image_id
        while self.current_image_id == new_id:
            new_id = self.background_images[randint(0, len(self.background_images) - 1)]
        self.current_image_id = new_id
        return new_id


class IronCastApp(App):
    # pylint: disable=too-few-public-methods
    def build(self):
        Loader.loading_image = join(dirname(__file__), "img", "loading.png")
        sm = IronManager(transition=FadeTransition(duration=2))
        sm.add_widget(IdleScreenA(name="ScreenA"))
        sm.add_widget(IdleScreenB(name="ScreenB"))
        sm.add_widget(VideoScreen(name="VideoScreen"))

        return sm
