import math
import subprocess
from datetime import datetime

import dbus

import ipc_common
import ipc_constants
import ipc_filters


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 convert_to_8_24(number):
    if number < 0:
        fTemp = number * float(1 << 24)
        fTemp = float(1 << 32) + fTemp - 0.5
        nValue8_24 = fTemp
    else:
        fTemp = number * float(1 << 24) + 0.5
        nValue8_24 = fTemp
    return int(nValue8_24)


def convert_str_to_dbus_msg(str_msg):
    if str(str_msg).lower() == "true":
        mute = python_to_dbus(True)
    elif str(str_msg).lower() == "false":
        mute = python_to_dbus(False)
    else:
        mute = python_to_dbus(ipc_constants.DEFAULT_BOOL)
    return mute


def set_attack_time_by_slider(slider_value):
    if slider_value < ipc_constants.ATT_02_5["position"]:
        attack_value = slider_value * (ipc_constants.ATT_02_5["value"]-ipc_constants.ATT_01_0["value"]) / \
                     (ipc_constants.ATT_02_5["position"]-ipc_constants.ATT_01_0["position"]) + ipc_constants.ATT_01_0["value"]
    elif slider_value < ipc_constants.ATT_05_0["position"]:
        attack_value = (slider_value - ipc_constants.ATT_02_5["position"]) * \
                     (ipc_constants.ATT_05_0["value"]-ipc_constants.ATT_02_5["value"]) / \
                     (ipc_constants.ATT_05_0["position"]-ipc_constants.ATT_02_5["position"]) + ipc_constants.ATT_02_5["value"]
    elif slider_value < ipc_constants.ATT_10_0["position"]:
        attack_value = (slider_value - ipc_constants.ATT_05_0["position"]) * \
                     (ipc_constants.ATT_10_0["value"]-ipc_constants.ATT_05_0["value"]) / \
                     (ipc_constants.ATT_10_0["position"]-ipc_constants.ATT_05_0["position"]) + ipc_constants.ATT_05_0["value"]
    elif slider_value < ipc_constants.ATT_20_0["position"]:
        attack_value = (slider_value - ipc_constants.ATT_10_0["position"]) * \
                     (ipc_constants.ATT_20_0["value"]-ipc_constants.ATT_10_0["value"]) / \
                     (ipc_constants.ATT_20_0["position"]-ipc_constants.ATT_10_0["position"]) + ipc_constants.ATT_10_0["value"]
    elif slider_value < ipc_constants.ATT_30_0["position"]:
        attack_value = (slider_value - ipc_constants.ATT_20_0["position"]) * \
                     (ipc_constants.ATT_30_0["value"]-ipc_constants.ATT_20_0["value"]) / \
                     (ipc_constants.ATT_30_0["position"]-ipc_constants.ATT_20_0["position"]) + ipc_constants.ATT_20_0["value"]
    elif slider_value < ipc_constants.ATT_50_0["position"]:
        attack_value = (slider_value - ipc_constants.ATT_30_0["position"]) * \
                     (ipc_constants.ATT_50_0["value"]-ipc_constants.ATT_30_0["value"]) / \
                     (ipc_constants.ATT_50_0["position"]-ipc_constants.ATT_30_0["position"]) + ipc_constants.ATT_30_0["value"]
    else:
        attack_value = (slider_value - ipc_constants.ATT_50_0["position"]) * \
                     (ipc_constants.ATT_100_0["value"]-ipc_constants.ATT_50_0["value"]) / \
                     (ipc_constants.ATT_100_0["position"]-ipc_constants.ATT_50_0["position"]) + ipc_constants.ATT_50_0["value"]

    attack_for_dsp = 1.0 / float(round(attack_value)) * 0.042143809
    return attack_for_dsp


def set_release_time_by_slider(slider_value):
    if slider_value < ipc_constants.REL_10["position"]:
        release_value = slider_value * (ipc_constants.REL_10["value"]-ipc_constants.REL_05["value"]) / \
                        (ipc_constants.REL_10["position"]-ipc_constants.REL_05["position"]) + ipc_constants.REL_05["value"]
    elif slider_value < ipc_constants.REL_25["position"]:
        release_value = (slider_value-ipc_constants.REL_10["position"]) * \
                        (ipc_constants.REL_25["value"]-ipc_constants.REL_10["value"]) / \
                        (ipc_constants.REL_25["position"]-ipc_constants.REL_10["position"]) + ipc_constants.REL_10["value"]
    elif slider_value < ipc_constants.REL_50["position"]:
        release_value = (slider_value-ipc_constants.REL_25["position"]) * \
                        (ipc_constants.REL_50["value"]-ipc_constants.REL_25["value"]) / \
                        (ipc_constants.REL_50["position"]-ipc_constants.REL_25["position"]) + ipc_constants.REL_25["value"]
    elif slider_value < ipc_constants.REL_100["position"]:
        release_value = (slider_value-ipc_constants.REL_50["position"]) * \
                        (ipc_constants.REL_100["value"]-ipc_constants.REL_50["value"]) / \
                        (ipc_constants.REL_100["position"]-ipc_constants.REL_50["position"]) + ipc_constants.REL_50["value"]
    elif slider_value < ipc_constants.REL_250["position"]:
        release_value = (slider_value-ipc_constants.REL_100["position"]) * \
                        (ipc_constants.REL_250["value"]-ipc_constants.REL_100["value"]) / \
                        (ipc_constants.REL_250["position"]-ipc_constants.REL_100["position"]) + ipc_constants.REL_100["value"]
    else:
        release_value = (slider_value-ipc_constants.REL_250["position"]) * \
                        (ipc_constants.REL_500["value"]-ipc_constants.REL_250["value"]) / \
                        (ipc_constants.REL_500["position"]-ipc_constants.REL_250["position"]) + ipc_constants.REL_250["value"]

    release_for_dsp = 1.0 / float(round(release_value)) * 0.001885851
    return release_for_dsp


def set_threshold_value_by_slider(slider_value):
    if slider_value < ipc_constants.THR_N30["position"]:
        threshold_value = slider_value * (ipc_constants.THR_N30["value"]-ipc_constants.THR_N40["value"]) / \
                          (ipc_constants.THR_N30["position"]-ipc_constants.THR_N40["position"]) + ipc_constants.THR_N40["value"]
    elif slider_value < ipc_constants.THR_N20["position"]:
        threshold_value = (slider_value - ipc_constants.THR_N30["position"]) * \
                          (ipc_constants.THR_N20["value"]-ipc_constants.THR_N30["value"]) / \
                          (ipc_constants.THR_N20["position"]-ipc_constants.THR_N30["position"]) + ipc_constants.THR_N30["value"]
    elif slider_value < ipc_constants.THR_N10["position"]:
        threshold_value = (slider_value - ipc_constants.THR_N20["position"]) * \
                          (ipc_constants.THR_N10["value"]-ipc_constants.THR_N20["value"]) / \
                          (ipc_constants.THR_N10["position"]-ipc_constants.THR_N20["position"]) + ipc_constants.THR_N20["value"]
    elif slider_value < ipc_constants.THR_P00["position"]:
        threshold_value = (slider_value - ipc_constants.THR_N10["position"]) * \
                          (ipc_constants.THR_P00["value"]-ipc_constants.THR_N10["value"]) / \
                          (ipc_constants.THR_P00["position"]-ipc_constants.THR_N10["position"]) + ipc_constants.THR_N10["value"]
    elif slider_value < ipc_constants.THR_P05["position"]:
        threshold_value = (slider_value - ipc_constants.THR_P00["position"]) * \
                          (ipc_constants.THR_P05["value"]-ipc_constants.THR_P00["value"]) / \
                          (ipc_constants.THR_P05["position"]-ipc_constants.THR_P00["position"]) + ipc_constants.THR_P00["value"]
    elif slider_value < ipc_constants.THR_P10["position"]:
        threshold_value = (slider_value - ipc_constants.THR_P05["position"]) * \
                          (ipc_constants.THR_P10["value"]-ipc_constants.THR_P05["value"]) / \
                          (ipc_constants.THR_P10["position"]-ipc_constants.THR_P05["position"]) + ipc_constants.THR_P05["value"]
    elif slider_value < ipc_constants.THR_P15["position"]:
        threshold_value = (slider_value - ipc_constants.THR_P10["position"]) * \
                          (ipc_constants.THR_P15["value"]-ipc_constants.THR_P10["value"]) / \
                          (ipc_constants.THR_P15["position"]-ipc_constants.THR_P10["position"]) + ipc_constants.THR_P10["value"]
    else:
        threshold_value = (slider_value - ipc_constants.THR_P15["position"]) * \
                          (ipc_constants.THR_P20["value"]-ipc_constants.THR_P15["value"]) / \
                          (ipc_constants.THR_P20["position"]-ipc_constants.THR_P15["position"]) + ipc_constants.THR_P15["value"]

    threshold_value = (threshold_value - 20.0) * 2.0 / 3.0
    return threshold_value


def set_gain_by_slider(slider_value):
    if 0 == slider_value:
        volDB = -100  # Do not calculate as log is infinite! Write inf in dB field.
    elif slider_value < ipc_constants.LIM_60dB["sliderValue"]:
        volDB = 20 * math.log10(slider_value * float(1.0) / ipc_constants.LIM_60dB["sliderValue"]) + ipc_constants.LIM_60dB["dbValue"]  # See if log represents log10 or ln. If it is log10 remove  0.4343 const
    elif slider_value < ipc_constants.LIM_50dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_60dB["sliderValue"]) * float(10.0) / \
                (ipc_constants.LIM_50dB["sliderValue"] - ipc_constants.LIM_60dB["sliderValue"]) + ipc_constants.LIM_60dB["dbValue"]
    elif slider_value < ipc_constants.LIM_40dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_50dB["sliderValue"]) * float(10.0) / \
                (ipc_constants.LIM_40dB["sliderValue"] - ipc_constants.LIM_50dB["sliderValue"]) + ipc_constants.LIM_50dB["dbValue"]
    elif slider_value < ipc_constants.LIM_35dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_40dB["sliderValue"]) * float(5.0) / \
                (ipc_constants.LIM_35dB["sliderValue"] - ipc_constants.LIM_40dB["sliderValue"]) + ipc_constants.LIM_40dB["dbValue"]
    elif slider_value < ipc_constants.LIM_30dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_35dB["sliderValue"]) * float(5.0) / \
                (ipc_constants.LIM_30dB["sliderValue"] - ipc_constants.LIM_35dB["sliderValue"]) + ipc_constants.LIM_35dB["dbValue"]
    elif slider_value < ipc_constants.LIM_25dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_30dB["sliderValue"]) * float(5.0) / \
                (ipc_constants.LIM_25dB["sliderValue"] - ipc_constants.LIM_30dB["sliderValue"]) + ipc_constants.LIM_30dB["dbValue"]
    elif slider_value < ipc_constants.LIM_20dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_25dB["sliderValue"]) * float(5.0) / \
                (ipc_constants.LIM_20dB["sliderValue"] - ipc_constants.LIM_25dB["sliderValue"]) + ipc_constants.LIM_25dB["dbValue"]
    elif slider_value < ipc_constants.LIM_15dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_20dB["sliderValue"]) * float(5.0) / \
                (ipc_constants.LIM_15dB["sliderValue"] - ipc_constants.LIM_20dB["sliderValue"]) + ipc_constants.LIM_20dB["dbValue"]
    elif slider_value < ipc_constants.LIM_10dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_15dB["sliderValue"]) * float(5.0) / \
                (ipc_constants.LIM_10dB["sliderValue"] - ipc_constants.LIM_15dB["sliderValue"]) + ipc_constants.LIM_15dB["dbValue"]
    elif slider_value < ipc_constants.LIM_05dB["sliderValue"]:
        volDB = (slider_value - ipc_constants.LIM_10dB["sliderValue"]) * float(5.0) / \
                (ipc_constants.LIM_05dB["sliderValue"] - ipc_constants.LIM_10dB["sliderValue"]) + ipc_constants.LIM_10dB["dbValue"]
    else:
        volDB = (slider_value - ipc_constants.LIM_05dB["sliderValue"]) * float(5.0) / \
                (ipc_constants.LIM_00dB["sliderValue"] - ipc_constants.LIM_05dB["sliderValue"]) + ipc_constants.LIM_05dB["dbValue"]

    return int(round(volDB))


def get_timestamp():
    now = datetime.now()
    timestamp = datetime.timestamp(now)
    return timestamp


def our_real_ip_addr(require_gateway):
    proc = subprocess.Popen(
        ["/bin/dash", "-c", "ifconfig " + ipc_common.ETHERNET_INTERFACE + " | grep \'inet \' | awk \'{ print $2, $4 }\'"],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT)
    res = proc.communicate()[0].strip().decode()
    if res == '':
        res = '0.0.0.0 0.0.0.0'

    if require_gateway:  # If not required, speed up things by skipping expensive process chain invocations
        proc = subprocess.Popen(
            ["/bin/dash", "-c", "ip route show | grep \'default\' | grep \'" + ipc_common.ETHERNET_INTERFACE + "\'| awk \'{ print $3 }\'"],
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        res2 = proc.communicate()[0].strip().decode()
        if res2 == '' or res2 == 'eth0':
            res2 = '0.0.0.0'
    else:
        res2 = '0.0.0.0'

    return res + ' ' + res2


def is_network_static():
    try:
        conf_file = open('/etc/network/interfaces.d/' + ipc_common.ETHERNET_INTERFACE + '.conf', 'r')
        lines = conf_file.readlines()
        conf_file.close()
    except IOError:
        print('Unable to open /etc/network/interfaces.d/' + ipc_common.ETHERNET_INTERFACE + '.conf for reading.')
        return True

    # If we can't reliably determine that it's DHCP, we assume static configuration
    if len(lines) == 0:
        return True
    else:
        for s in lines:
            if ipc_common.ETHERNET_INTERFACE in s and 'dhcp' in s:
                return False
        return True


def our_eth_mac_addr():
    try:
        devfile = open("/sys/class/net/" + ipc_common.ETHERNET_INTERFACE + "/address", "r")
        mac_addr = devfile.readline().strip().upper()
        devfile.close()
    except (IOError, UnicodeDecodeError):
        mac_addr = "<NONE>"
    return mac_addr


def our_wlan_mac_addr():
    try:
        devfile = open("/sys/class/net/" + ipc_common.WLAN_INTERFACE + "/address", "r")
        mac_addr = devfile.readline().strip().upper()
        devfile.close()
    except (IOError, UnicodeDecodeError):
        mac_addr = "<NONE>"
    return mac_addr


def scale_xover_coeff(coeff):
    dsp_coeff = []
    for i in range(0, 4):
        a1_dsp = -1 * coeff[i]["a1"] / coeff[i]["a0"]
        a2_dsp = -1 * coeff[i]["a2"] / coeff[i]["a0"]
        b0_dsp = coeff[i]["b0"] / coeff[i]["a0"]
        b1_dsp = coeff[i]["b1"] / coeff[i]["a0"]
        b2_dsp = coeff[i]["b2"] / coeff[i]["a0"]
        dsp_coeff.append([b2_dsp, b1_dsp, b0_dsp, a2_dsp, a1_dsp])
    return dsp_coeff


def recalculate_peq_values(settings, channel, peq_channel):
    if settings[channel]["peq"][peq_channel]["enable"]:  # if enabled, recalculate and send
        a0, a1, a2, b0, b1, b2 = ipc_filters.compute_peq_coeff(settings[channel]["peq"][peq_channel]["type"],
                                                               settings[channel]["peq"][peq_channel]["freq"],
                                                               settings[channel]["peq"][peq_channel]["gain"],
                                                               settings[channel]["peq"][peq_channel]["q"])
        a1, a2, b0, b1, b2 = rescale_peq_values(a0, a1, a2, b0, b1, b2)
    else:
        a1, a2, b0, b1, b2 = rescale_peq_values(ipc_constants.DEFAULT_FILTER["a0"],
                                                ipc_constants.DEFAULT_FILTER["a1"],
                                                ipc_constants.DEFAULT_FILTER["a2"],
                                                ipc_constants.DEFAULT_FILTER["b0"],
                                                ipc_constants.DEFAULT_FILTER["b1"],
                                                ipc_constants.DEFAULT_FILTER["b2"])
    return [b2, b1, b0, a2, a1]


def rescale_peq_values(a0, a1, a2, b0, b1, b2):
    a1_dsp = -1 * a1/a0
    a2_dsp = -1 * a2/a0
    b0_dsp = b0/a0
    b1_dsp = b1/a0
    b2_dsp = b2/a0
    return a1_dsp, a2_dsp, b0_dsp, b1_dsp, b2_dsp


# Used for updating compressor points when threshold changes
def calculate_compressor_rms_coefficients(threshold_value):
    points_106 = []
    sig_strength = -135.0
    # limiter ratio : 1/ratio
    ratio = 10
    ratio_compute = math.pow(10, -((1.5 - 1.5 / ratio) / 20))
    threshold = threshold_value
    threshold -= 6  # because isLout is TRUE, from GIVA LIVIAU

    knee_lim = 2

    for i in range(0, ipc_constants.NUM_OF_LIM_RMS_THRESHOLD_POINTS):
        if sig_strength < float(threshold - knee_lim):
            points_106.append(1.0)
        elif sig_strength < float(threshold + knee_lim):
            out_vol_db = float(threshold) - \
                         (math.pow(sig_strength - float(threshold) - float(knee_lim), 2) / (float(knee_lim) * 4.0))
            points_106.append(math.pow(10, (math.exp(math.log(10)) * ((out_vol_db - sig_strength) / 20.0)) / 20))
        else:
            points_106.append((points_106[i - 1] * ratio_compute))
        sig_strength += 1.5

    points_106[105] *= (1 / math.sqrt(2))
    f = open("/home/orangepi/IpcSystemService/lim_db.json", "w")
    f.write(repr(points_106))
    f.close()
    return points_106


# Used for updating compressor points when threshold changes
def calculate_compressor_peak_coefficients(threshold_value):
    points_124 = []
    sig_strength = -140.0
    # limiter ratio : 1/ratio
    ratio = 10
    ratio_compute = math.pow(10, -((1.5 - 1.5 / ratio) / 20))

    threshold = threshold_value
    threshold -= 0  # because isLout is TRUE, from GIVA LIVIAU

    knee_lim = 2

    for i in range(0, ipc_constants.NUM_OF_LIM_PEAK_THRESHOLD_POINTS):
        if sig_strength < float(threshold - knee_lim):
            points_124.append(1.0)
        elif sig_strength < float(threshold + knee_lim):
            out_vol_db = float(threshold) - \
                         (math.pow(sig_strength - float(threshold) - float(knee_lim), 2) / (float(knee_lim) * 4.0))
            points_124.append(math.pow(10, (math.exp(math.log(10)) * ((out_vol_db - sig_strength) / 20.0)) / 20))
        else:
            points_124.append((points_124[i - 1] * ratio_compute))

        sig_strength += 1.5

    points_124[123] *= (1 / math.sqrt(2))

    return points_124


def debug_print(message: str, debug: bool):
    if debug:
        print(message)


def transform_register_address(address):
    return [address >> 8 & 0xFF, address & 0xFF]
