#!./bin/python3

import os
import sys
import threading
import time
import traceback
from typing import Dict

import pygame # type: ignore
import tornado.ioloop, tornado.web
import asyncio

from display import Display
from player import Player

class WebHandler(tornado.web.RequestHandler):

    def initialize(self, musikautomat):
        self.musikautomat = musikautomat

    def get(self, a):
        if a.startswith("play"):

            if a == "play/dir":
                path = self.get_argument('path')
                self.musikautomat.play({
                    "type" : "dir",
                    "name" : self.get_argument('name', path),
                    "path" : path
                }, start=True)

class Musikautomat:

    def __init__(self, pydsp, config: Dict):

        # Initialise state objects

        self._pydsp = pydsp
        self._display = Display(config, self._pydsp)
        self._display.update()
        self._eventSink = self._display
        self._player = Player(config, self._pydsp)
        self._dx = config.get("dimx", 128)

        threading.Thread(target=self.runWebServer, daemon=True).start()


    def runWebServer(self):
        '''
        Run event loop for web interface.
        '''
        urls = [
            (r"/api/(.*)", WebHandler, dict(musikautomat=self)),
            (r"/(.*)", tornado.web.StaticFileHandler, {"path" : "./web",
            "default_filename" : "index.html" }),
        ]

        asyncio.set_event_loop(asyncio.new_event_loop())
        app = tornado.web.Application(urls, debug=False)
        app.listen(8080)
        tornado.ioloop.IOLoop.instance().start()


    def runDisplay(self):
        '''
        Run event loop for display and buttons.
        '''
        clk = pygame.time.Clock()
        while True:
            clk.tick(10) # We only need 10 FPS

            try:
                for event in pygame.event.get():

                    # Handle exit event

                    if event.type == pygame.QUIT:
                        pygame.quit()
                        sys.exit()

                    # Handle key events

                    if event.type == pygame.KEYDOWN:

                        if event.key == pygame.K_DOWN:
                            self._eventSink.action("down")

                        elif event.key == pygame.K_UP:
                            self._eventSink.action("up")

                        elif event.key == pygame.K_RIGHT:

                            if self._eventSink == self._player:
                                self._player.togglePlay()
                            else:
                                self.play(self._display.currentItem())

                        elif event.key == pygame.K_LEFT:

                            # Get back to display

                            self._eventSink = self._display
                            self._display.update()

            except Exception as e:

                # Display error
                traceback.print_exc()

                self._pydsp.fill((0,0,0))
                font = pygame.font.Font('freesansbold.ttf', 12)

                s = str(e)
                n = int(self._dx / 6)

                for i, start in enumerate(range(0, len(s), n)):
                    text = font.render(s[start:start+n], True, (255, 0, 0) , (0, 0, 0))
                    self._pydsp.blit(text, (0, i * 12))

                self._eventSink = self._display

            pygame.display.update()


    def play(self, item, start=False):
        '''
        Play an item.
        '''
        print("Player data:", item)

        self._eventSink = self._player

        t = item.get("type")
        if t == "dir":
            self.playDir(item)
        else:
            raise Exception("Unknown type: %s" % t)

        if start:
            self._player.togglePlay()


    def playDir(self, item):
        '''
        Play all files in a directory.
        '''
        path = item.get("path")
        files = os.listdir(path)

        def toDisplayName(s):
            s = os.path.splitext(s)[0]
            return s.replace("_", " ").title()

        self._player.setPlaylist(sorted([{
            "name" : toDisplayName(f),
            "path" : os.path.join(path, f)
        } for f in files], key=lambda i: i["name"]))

        self._player.update()
