import asyncio
import serial_asyncio
from led_cmds import (ColorRGBW, ColorHSV, EffectStaticConfig,
                      EffectRandomTwoColorInterpolationConfig, EffectAlexaSwipeConfig,
                      EffectSwipeAndChange, EffectReverseSwipe)
from host_driver import MusicMouseProtocol, RfidTokenRead, RotaryEncoderEvent, ButtonEvent, TouchButton, TouchButtonPress, TouchButtonRelease
from player import AudioPlayer
from glob import glob
from copy import deepcopy
import os

MUSIC_FOLDER = "/home/martin/code/musicmouse/espmusicmouse/host_driver/music"

audio_player = AudioPlayer()
current_figure = None
last_figure = None

rfid_token_map = {
    bytes.fromhex("88041174e9"): "elefant",
    bytes.fromhex("8804ce7230"): "fuchs",
    bytes.fromhex("88040d71f0"): "eule",
    bytes.fromhex("88043c6ede"): "omnom",
    bytes.fromhex("88040b78ff"): "eichhoernchen",
    bytes.fromhex("8804bc7444"): "hund",
}


def parse_hex_color(color_str: str):
    if isinstance(color_str, ColorRGBW):
        return color_str
    color_str = color_str.lstrip('#')
    t = tuple(int(color_str[i:i + 2], 16) / 255 for i in (0, 2, 4))
    return ColorRGBW(*t, 0)


class FigureColorCfg:
    def __init__(self, primary, secondary, background, accent):
        self.primary = parse_hex_color(primary)
        self.secondary = parse_hex_color(secondary)
        self.background = parse_hex_color(background)
        self.accent = parse_hex_color(accent)


color_cfg = {
    "elefant": FigureColorCfg("#ffff00", "#00c8ff", "#094b46", "#c20099"),
    "fuchs": FigureColorCfg("#F4D35E", "#F95738", "#F95738", "#083d77"),
    "omnom": FigureColorCfg("#005102", "#3bc405", "#005102", "#3bc405"),
    "eichhoernchen": FigureColorCfg("#ff0ada", "#4BC6B9", "#69045a", "#4BC6B9"),
    "hund": FigureColorCfg("#ffff00", "#00c8ff", "#094b46", "#c20099"),
    "eule": FigureColorCfg("#e5a200", "#f8e300", ColorRGBW(0, 0, 0, 0.2), ColorRGBW(0, 0, 0, 1)),
}

playlists = {
    fig: audio_player.create_playlist(sorted(glob(os.path.join(MUSIC_FOLDER, fig, "*.mp3"))))
    for fig in rfid_token_map.values()
}

mouse_leds = {
    TouchButton.RIGHT_FOOT: (0, 6),
    TouchButton.LEFT_FOOT: (6, 6 + 6),
    TouchButton.LEFT_EAR: (6 + 6, 6 + 6 + 16),
    TouchButton.RIGHT_EAR: (6 + 6 + 16, 6 + 6 + 16 + 17),
}


def run_off_animation(protocol):
    ring_eff = EffectReverseSwipe()
    mouse_eff = EffectReverseSwipe()
    mouse_eff.startPosition = 6 / 45 * 360
    protocol.led_ring_effect(ring_eff)
    protocol.mouse_led_effect(mouse_eff)
    protocol.button_background_led_prev(0)
    protocol.button_background_led_next(0)


def on_music_end_callback(protocol):
    run_off_animation(protocol)


def on_rfid(protocol, tagid):
    global current_figure, last_figure

    if tagid == bytes.fromhex("0000000000"):
        # Off
        if audio_player.is_playing():
            run_off_animation(protocol)
            audio_player.pause()
        #else:
        #    protocol.led_ring_effect(EffectStaticConfig(ColorRGBW(0, 0, 0, 0)))
        #    protocol.mouse_led_effect(EffectStaticConfig(ColorRGBW(0, 0, 0, 0)))
        last_figure = current_figure
    else:
        figure = rfid_token_map[tagid]
        current_figure = figure
        ccfg = color_cfg[figure]
        ring_eff = EffectSwipeAndChange()
        ring_eff.swipe.primary_color = ccfg.primary
        ring_eff.swipe.secondary_color = ccfg.secondary
        ring_eff.change.color1 = ccfg.primary
        ring_eff.change.color2 = ccfg.secondary
        protocol.led_ring_effect(ring_eff)

        mouse_eff = EffectStaticConfig(ccfg.background)
        mouse_eff = deepcopy(ring_eff)
        mouse_eff.swipe.start_position = 6 / 45 * 360
        mouse_eff.swipe.bell_curve_width_in_leds = 16
        protocol.mouse_led_effect(mouse_eff)

        protocol.button_background_led_prev(0.3)
        protocol.button_background_led_next(0.3)

        if figure in playlists:
            audio_player.set_playlist(playlists[figure])
            if last_figure == current_figure:
                audio_player.play()
            else:
                audio_player.play_from_start()


def on_firmware_msg(protocol: MusicMouseProtocol, message):
    print("FW msg:", message)
    if isinstance(message, RfidTokenRead):
        on_rfid(protocol, message.id)
    elif isinstance(message, RotaryEncoderEvent):
        if audio_player.is_playing():
            if message.direction == 2:
                audio_player.change_volume(2)
            elif message.direction == 1:
                audio_player.change_volume(-2)
    elif isinstance(message, ButtonEvent):
        if message.button == "left" and message.event == "pressed":
            audio_player.previous()
        elif message.button == "right" and message.event == "pressed":
            audio_player.next()
    elif isinstance(message, TouchButtonPress):
        ccfg = color_cfg[current_figure]
        protocol.mouse_led_effect(EffectStaticConfig(ccfg.accent,
                                                     *mouse_leds[message.touch_button]))
    elif isinstance(message, TouchButtonRelease):
        ccfg = color_cfg[current_figure]
        eff_change = EffectRandomTwoColorInterpolationConfig()
        eff_static = EffectStaticConfig(ColorRGBW(0, 0, 0, 0), *mouse_leds[message.touch_button])
        if audio_player.is_playing():
            eff_static.color = ccfg.primary
        protocol.mouse_led_effect(eff_static)

        if audio_player.is_playing():
            eff_change.color1 = ccfg.primary
            eff_change.color2 = ccfg.secondary
            eff_change.start_with_existing = True
            protocol.mouse_led_effect(eff_change)


loop = asyncio.get_event_loop()
coro = serial_asyncio.create_serial_connection(loop,
                                               MusicMouseProtocol,
                                               '/dev/ttyUSB0',
                                               baudrate=115200)
transport, protocol = loop.run_until_complete(coro)
protocol.register_message_callback(on_firmware_msg)

audio_player.on_playlist_end_callback = lambda: on_music_end_callback(protocol)

loop.run_forever()
loop.close()