import math
import dbus
import os.path
import sys

import common
import Constants


def python_to_dbus(data):
    """
        convert python data types to dbus data types
    """
    if isinstance(data, str):
        data = dbus.String(data)
    elif isinstance(data, bool):
        # python bools are also ints, order is important !
        data = dbus.Boolean(data)
    elif isinstance(data, int):
        data = dbus.Int64(data)
    elif isinstance(data, float):
        data = dbus.Double(data)
    elif isinstance(data, list):
        data = dbus.Array([python_to_dbus(value) for value in data], signature='v')
    elif isinstance(data, dict):
        data = dbus.Dictionary(data, signature='sv')
        for key in data.keys():
            data[key] = python_to_dbus(data[key])
    return data


def dbus_to_python(data):
    """
        convert dbus data types to python native data types
    """
    if isinstance(data, dbus.String):
        data = str(data)
    elif isinstance(data, dbus.Boolean):
        data = bool(data)
    elif isinstance(data, dbus.Int64):
        data = int(data)
    elif isinstance(data, dbus.Double):
        data = float(data)
    elif isinstance(data, dbus.Array):
        data = [dbus_to_python(value) for value in data]
    elif isinstance(data, dbus.Dictionary):
        new_data = dict()
        for key in data.keys():
            new_data[key] = dbus_to_python(data[key])
        data = new_data
    return data


def update_board_revision():
    # Command-line argument 'ipc-board-rev' alters GPIO configuration
    for arg in sys.argv:
        if arg.strip() == '--ipc-board-rev=0':
            Constants.IPC_BOARD_REV = 0
            break
        elif arg.strip() == '--ipc-board-rev=1':
            Constants.IPC_BOARD_REV = 1
            break

    if Constants.IPC_BOARD_REV == 0:
        Constants.IPC_BOARD_GPIO_DISPCMD = Constants.IPC_BOARD_REV0_GPIO_DISPCMD
        Constants.IPC_BOARD_GPIO_DISPRST = Constants.IPC_BOARD_REV0_GPIO_DISPRST
        Constants.IPC_BOARD_GPIO_ENCA = Constants.IPC_BOARD_REV0_GPIO_ENCA
        Constants.IPC_BOARD_GPIO_ENCB = Constants.IPC_BOARD_REV0_GPIO_ENCB
        Constants.IPC_BOARD_GPIO_ENCSW = Constants.IPC_BOARD_REV0_GPIO_ENCSW
    else:
        Constants.IPC_BOARD_GPIO_DISPCMD = Constants.IPC_BOARD_REV1_GPIO_DISPCMD
        Constants.IPC_BOARD_GPIO_DISPRST = Constants.IPC_BOARD_REV1_GPIO_DISPRST
        Constants.IPC_BOARD_GPIO_ENCA = Constants.IPC_BOARD_REV1_GPIO_ENCA
        Constants.IPC_BOARD_GPIO_ENCB = Constants.IPC_BOARD_REV1_GPIO_ENCB
        Constants.IPC_BOARD_GPIO_ENCSW = Constants.IPC_BOARD_REV1_GPIO_ENCSW


def construct_luma_core_display_args():
    update_board_revision()

    args = ['--display', 'ssd1351',
            '--interface', 'spi',
            '--width', '128',
            '--height', '128',
            '--spi-port', '1',
            '--spi-device', '0',
            '--spi-bus-speed', '16000000',
            '--gpio-mode', 'BOARD',
            '--gpio-data-command', str(Constants.IPC_BOARD_GPIO_DISPCMD),
            '--gpio-reset', str(Constants.IPC_BOARD_GPIO_DISPRST)]

    return args


input_volume_slider = [{"dB_value": 6, "slider_value": float(100)},
                       {"dB_value": 0, "slider_value": float(87)},
                       {"dB_value": -5, "slider_value": float(76)},
                       {"dB_value": -10, "slider_value": float(65)},
                       {"dB_value": -15, "slider_value": float(54)},
                       {"dB_value": -20, "slider_value": float(43)},
                       {"dB_value": -25, "slider_value": float(35)},
                       {"dB_value": -30, "slider_value": float(26)},
                       {"dB_value": -35, "slider_value": float(18)},
                       {"dB_value": -40, "slider_value": float(13)},
                       {"dB_value": -50, "slider_value": float(7)},
                       {"dB_value": -60, "slider_value": float(2)}]


def get_gain_as_slider_progress(gain):
    if gain == -100:
        slider_value = 0
    elif gain < input_volume_slider[11]["dB_value"]:
        slider_value = input_volume_slider[11]["slider_value"] * math.pow(10, (gain - input_volume_slider[11]["dB_value"]) / 20)
    elif gain < input_volume_slider[10]["dB_value"]:
        slider_value = (gain - input_volume_slider[11]["dB_value"]) * \
                        (input_volume_slider[10]["slider_value"] - input_volume_slider[11]["slider_value"]) /\
                        (input_volume_slider[10]["dB_value"] - input_volume_slider[11]["dB_value"]) + input_volume_slider[11]["slider_value"]
    elif gain < input_volume_slider[9]["dB_value"]:
        slider_value = (gain - input_volume_slider[10]["dB_value"]) * \
                        (input_volume_slider[9]["slider_value"] - input_volume_slider[10]["slider_value"]) /\
                        (input_volume_slider[9]["dB_value"] - input_volume_slider[10]["dB_value"]) + input_volume_slider[10]["slider_value"]
    elif gain < input_volume_slider[8]["dB_value"]:
        slider_value = (gain - input_volume_slider[9]["dB_value"]) * \
                        (input_volume_slider[8]["slider_value"] - input_volume_slider[9]["slider_value"]) / \
                        (input_volume_slider[8]["dB_value"] - input_volume_slider[9]["dB_value"]) + input_volume_slider[9]["slider_value"]
    elif gain < input_volume_slider[7]["dB_value"]:
        slider_value = (gain - input_volume_slider[8]["dB_value"]) * \
                        (input_volume_slider[7]["slider_value"] - input_volume_slider[8]["slider_value"]) / \
                        (input_volume_slider[7]["dB_value"] - input_volume_slider[8]["dB_value"]) + input_volume_slider[8]["slider_value"]
    elif gain < input_volume_slider[6]["dB_value"]:
        slider_value = (gain - input_volume_slider[7]["dB_value"]) * \
                        (input_volume_slider[6]["slider_value"] - input_volume_slider[7]["slider_value"]) / \
                        (input_volume_slider[6]["dB_value"] - input_volume_slider[7]["dB_value"]) + input_volume_slider[7]["slider_value"]
    elif gain < input_volume_slider[5]["dB_value"]:
        slider_value = (gain - input_volume_slider[6]["dB_value"]) * \
                        (input_volume_slider[5]["slider_value"] - input_volume_slider[6]["slider_value"]) / \
                        (input_volume_slider[5]["dB_value"] - input_volume_slider[6]["dB_value"]) + input_volume_slider[6]["slider_value"]
    elif gain < input_volume_slider[4]["dB_value"]:
        slider_value = (gain - input_volume_slider[5]["dB_value"]) * \
                        (input_volume_slider[4]["slider_value"] - input_volume_slider[5]["slider_value"]) / \
                        (input_volume_slider[4]["dB_value"] - input_volume_slider[5]["dB_value"]) + input_volume_slider[5]["slider_value"]
    elif gain < input_volume_slider[3]["dB_value"]:
        slider_value = (gain - input_volume_slider[4]["dB_value"]) * \
                        (input_volume_slider[3]["slider_value"] - input_volume_slider[4]["slider_value"]) / \
                        (input_volume_slider[3]["dB_value"] - input_volume_slider[4]["dB_value"]) + input_volume_slider[4]["slider_value"]
    elif gain < input_volume_slider[2]["dB_value"]:
        slider_value = (gain - input_volume_slider[3]["dB_value"]) * \
                        (input_volume_slider[2]["slider_value"] - input_volume_slider[3]["slider_value"]) / \
                        (input_volume_slider[2]["dB_value"] - input_volume_slider[3]["dB_value"]) + input_volume_slider[3]["slider_value"]
    elif gain < input_volume_slider[1]["dB_value"]:
        slider_value = (gain - input_volume_slider[2]["dB_value"]) * \
                        (input_volume_slider[1]["slider_value"] - input_volume_slider[2]["slider_value"]) / \
                        (input_volume_slider[1]["dB_value"] - input_volume_slider[2]["dB_value"]) + input_volume_slider[2]["slider_value"]
    else:
        slider_value = gain * \
                   (input_volume_slider[0]["slider_value"] - input_volume_slider[1]["slider_value"]) / \
                   (input_volume_slider[0]["dB_value"] - input_volume_slider[1]["dB_value"]) + input_volume_slider[1]["slider_value"]

    return int(round(slider_value))


def is_ethernet_alive() -> bool:
    ifstate_file_path = '/sys/class/net/' + common.ETHERNET_INTERFACE + '/operstate'
    linkstate_file_path = '/sys/class/net/' + common.ETHERNET_INTERFACE + '/carrier'

    if not os.path.isfile(ifstate_file_path):
        # Interface doesn't exist (e.g. hardware damage)
        return False

    try:
        with open(ifstate_file_path, "r") as ifstate_file:
            operstate = ifstate_file.readline().strip()
            if operstate == 'down':  # Interface is not brought up
                return False
        with open(linkstate_file_path, "r") as linkstate_file:
            carrier = linkstate_file.readline().strip()
            return carrier == '1'   # 1 = cable connected, 0 = cable disconnected
    except IOError:
        return False
