# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, print_function, unicode_literals

#################################################################################################

import json
import threading
import time

import xbmc

from ..helper import LazyLogger, settings

# If numpy is installed, the websockets library tries to use it, and then
# kodi hard crashes for reasons I don't even want to pretend to understand
import sys  # noqa: E402,I100

sys.modules["numpy"] = None
import websocket  # noqa: E402,I201

##################################################################################################

LOG = LazyLogger(__name__)

##################################################################################################


class WSClient(threading.Thread):

    wsc = None
    stop = False

    def __init__(self, client):

        LOG.debug("WSClient initializing...")

        self.client = client
        threading.Thread.__init__(self)
        self.keepalive = None

    def send(self, message, data=""):

        if self.wsc is None:
            raise ValueError("The websocket client is not started.")

        self.wsc.send(json.dumps({"MessageType": message, "Data": data}))

    def run(self):

        monitor = xbmc.Monitor()
        server = self.client.config.data["auth.server"]
        server = (
            server.replace("https://", "wss://")
            if server.startswith("https")
            else server.replace("http://", "ws://")
        )
        wsc_url = "%s/socket" % server
        # Build authorization header for websocket connection
        auth_params = {}
        if "app.device_name" in self.client.config.data:
            auth_params.update(
                {
                    "Client": self.client.config.data["app.name"],
                    "Device": self.client.config.data["app.device_name"],
                    "DeviceId": self.client.config.data["app.device_id"],
                    "Version": self.client.config.data["app.version"],
                }
            )
        if "auth.token" in self.client.config.data:
            auth_params["Token"] = self.client.config.data["auth.token"]
        auth_line = ", ".join(f'{k}="{v}"' for k, v in auth_params.items())
        ws_headers = {"Authorization": f"MediaBrowser {auth_line}"}

        LOG.info("Websocket url: %s", wsc_url)

        self.wsc = websocket.WebSocketApp(
            wsc_url,
            header=ws_headers,
            on_open=lambda ws: self.on_open(ws),
            on_message=lambda ws, message: self.on_message(ws, message),
            on_error=lambda ws, error: self.on_error(ws, error),
        )

        while not self.stop:

            self.wsc.run_forever(ping_interval=10, reconnect=10)

            if not self.stop and monitor.waitForAbort(5):
                break

    def on_error(self, ws, error):
        LOG.error(error)

    def on_open(self, ws):
        LOG.info("--->[ websocket opened ]")
        # Avoid a timing issue where the capabilities are not correctly registered
        time.sleep(5)
        if settings("remoteControl.bool"):
            self.client.jellyfin.post_capabilities(
                {
                    "PlayableMediaTypes": "Audio,Video",
                    "SupportsMediaControl": True,
                    "SupportedCommands": (
                        "MoveUp,MoveDown,MoveLeft,MoveRight,Select,"
                        "Back,ToggleContextMenu,ToggleFullscreen,ToggleOsdMenu,"
                        "GoHome,PageUp,NextLetter,GoToSearch,"
                        "GoToSettings,PageDown,PreviousLetter,TakeScreenshot,"
                        "VolumeUp,VolumeDown,ToggleMute,SendString,DisplayMessage,"
                        "SetAudioStreamIndex,SetSubtitleStreamIndex,"
                        "SetRepeatMode,Mute,Unmute,SetVolume,"
                        "Play,Playstate,PlayNext,PlayMediaSource"
                    ),
                }
            )
        else:
            self.client.jellyfin.post_capabilities(
                {"PlayableMediaTypes": "Audio, Video", "SupportsMediaControl": False}
            )
        if self.keepalive is not None:
            self.keepalive.stop()
        self.keepalive = KeepAlive(self, ws)
        self.keepalive.start()

    def on_message(self, ws, message):

        message = json.loads(message)
        data = message.get("Data", {})

        if message["MessageType"] in ("RefreshProgress",):
            LOG.debug("Ignoring %s", message)

            return

        if not self.client.config.data["app.default"]:
            data["ServerId"] = self.client.auth.server_id

        self.client.callback(message["MessageType"], data)

    def stop_client(self):

        self.stop = True

        if self.keepalive is not None:
            self.keepalive.stop()

        if self.wsc is not None:
            self.wsc.close()


class KeepAlive(threading.Thread):
    def __init__(self, timeout, ws):
        self.halt = threading.Event()
        self.ws = ws

        threading.Thread.__init__(self)

    def stop(self):
        self.halt.set()
        self.join()

    def run(self):
        while not self.halt.is_set():
            if self.halt.wait(30):
                break
            else:
                self.ws.send(json.dumps({"MessageType": "KeepAlive", "Data": 30}))
