import binascii
import logging
import socket

from pyamasicp.client import Client

CMD_SET_POWER_STATE = b'\x18'
CMD_GET_POWER_STATE = b'\x19'
CMD_SET_VOLUME = b'\x44'
CMD_GET_VOLUME = b'\x45'
CMD_SET_INPUT_SOURCE = b'\xAC'
CMD_GET_INPUT_SOURCE = b'\xAD'
CMD_GET_INFO = b'\xA1'
CMD_GET_VERSION = b'\xA2'
CMD_IR = b'\xDB'

MODEL_INFO_MODEL_NUMBER = b'\x00'
MODEL_INFO_FW_VERSION = b'\x01'
MODEL_INFO_BUILD_DATE = b'\x02'

VERSION_INFO_OTSC_IMPLEMENTATION_VERSION = b'\x00'
VERSION_INFO_PLATFORM_LABEL = b'\x01'
VERSION_INFO_PLATFORM_VERSION = b'\x02'

VAL_POWER_OFF = b'\x01'
VAL_POWER_ON = b'\x02'

IR_POWER = b'\xA0'
IR_MENU = b'\xA1'
IR_INPUT = b'\xA2'
IR_VOL_UP = b'\xA3'
IR_VOL_DOWN = b'\xA4'
IR_MUTE = b'\xA5'
IR_CURSOR_UP = b'\xA6'
IR_CURSOR_DOWN = b'\xA7'
IR_CURSOR_LEFT = b'\xA8'
IR_CURSOR_RIGHT = b'\xA9'
IR_OK = b'\xB1'
IR_RETURN = b'\xB2'
IR_RED = b'\xC1'
IR_GREEN = b'\xC2'
IR_YELLOW = b'\xC3'
IR_BLUE = b'\xC4'
IR_FORMAT = b'\xD1'
IR_INFO = b'\xD2'
IR_BTN_0 = b'\x00'
IR_BTN_1 = b'\x01'
IR_BTN_2 = b'\x02'
IR_BTN_3 = b'\x03'
IR_BTN_4 = b'\x04'
IR_BTN_5 = b'\x05'
IR_BTN_6 = b'\x06'
IR_BTN_7 = b'\x07'
IR_BTN_8 = b'\x08'
IR_BTN_9 = b'\x09'

INPUT_SOURCES = {
    "VIDEO": 0x00,
    "Display Port": 0x01,
    "S-VIDEO": 0x02,
    "COMPONENT": 0x03,
    "VGA": 0x05,
    "HDMI 2": 0x06,
    "Display Port 2": 0x07,
    "USB 2": 0x08,
    "Card DVI-D": 0x09,
    "Display Port 1": 0x0A,
    "Card OPS": 0x0B,
    "USB 1": 0x0C,
    "HDMI 1": 0x0D,
    "DVI-D": 0x0E,
    "HDMI 3": 0x0F,
    "BROWSER": 0x10,
    "SMARTCMS": 0x11,
    "DMS (Digital Media Server)": 0x12,
    "INTERNAL STORAGE": 0x13,
    "Reserved": 0x14,
    "Media Player": 0x16,
    "PDF Player": 0x17,
    "Custom": 0x18,
    "HDMI 4": 0x19,
}


class Commands:

    def __init__(self, client: Client, id=b'\x01'):
        self._logger = logging.getLogger(self.__class__.__qualname__)
        self._client = client
        self._id = id

    def get_power_state(self):
        try:
            result = self._client.send(self._id, CMD_GET_POWER_STATE)
            match result:
                case b'\x01':
                    return False
                case b'\x02':
                    return True
                case None:
                    self._logger.warning("No power state retrieved.")
                    return None
                case _:
                    self._logger.warning("Unknown power state: %s" % binascii.hexlify(result))
                    return None
        except socket.error:
            return None

    def set_power_state(self, state: bool):
        self._client.send(self._id, CMD_SET_POWER_STATE, VAL_POWER_ON if state else VAL_POWER_OFF)

    def get_volume(self):
        result = [b for b in self._client.send(self._id, CMD_GET_VOLUME)]
        result.reverse()
        return result

    def set_volume(self, output_volume=0, volume=0):
        self._client.send(self._id, CMD_SET_VOLUME, bytearray([volume, output_volume, 0, 0]))

    def get_input_source(self):
        return [b for b in self._client.send(self._id, CMD_GET_INPUT_SOURCE)]

    def set_input_source(self, input_type=0, input_number=0, osd_style=0, reserved=0):
        self._client.send(self._id, CMD_SET_INPUT_SOURCE, bytearray([input_type, input_number, osd_style, reserved]))

    def get_osc_implementation_version(self):
        return self._get_string(CMD_GET_VERSION, bytes(VERSION_INFO_OTSC_IMPLEMENTATION_VERSION))

    def get_platform_label(self):
        return self._get_string(CMD_GET_VERSION, bytes(VERSION_INFO_PLATFORM_LABEL))

    def get_platform_version(self):
        return self._get_string(CMD_GET_VERSION, bytes(VERSION_INFO_PLATFORM_VERSION))

    def get_model_number(self):
        return self._get_string(CMD_GET_INFO, bytes(MODEL_INFO_MODEL_NUMBER))

    def get_fw_version(self):
        return self._client.send(CMD_GET_INFO, bytes(MODEL_INFO_FW_VERSION))

    def get_build_date(self):
        return self._client.send(CMD_GET_INFO, bytes(MODEL_INFO_BUILD_DATE))

    def _get_string(self, cmd, data):
        response = self._client.send(self._id, cmd, data)
        return response.decode('utf-8') if response else None

    def ir_command(self, code):
        self._client.send(self._id, CMD_IR, code)


class CommandException(Exception):
    pass
