import logging
from tkinter import ttk

from . import builders
from .data import _base_values, _configuration

logger = logging.getLogger(__name__)


class Config(ttk.Frame):
    def __init__(self, parent, index, _id):
        super().__init__(parent)
        self.parent = parent
        self.index = index
        self.id = _id
        self.logger = logger.getChild(self.__class__.__name__)
        self.styletable = parent.styletable
        self.phys_in, self.virt_in = parent.kind.ins
        self.phys_out, self.virt_out = parent.kind.outs

        self.parent.subject.add(self)

    @property
    def identifier(self):
        return self.id

    @property
    def target(self):
        """returns the current interface"""

        return self.parent.target

    def getter(self, param):
        param = param.split(".")
        try:
            if len(param) == 2:
                target = getattr(self.target, param[0])
                return getattr(target, param[1])
            else:
                return getattr(self.target, param[0])
        except AttributeError as e:
            self.logger.error(f"{type(e).__name__}: {e}")

    def setter(self, param, value):
        param = param.split(".")
        try:
            if len(param) == 2:
                target = getattr(self.target, param[0])
                setattr(target, param[1], value)
            else:
                setattr(self.target, param[0], value)
        except AttributeError as e:
            self.logger(f"{type(e).__name__}: {e}")

    def scale_press(self, *args):
        self.after(1, self.remove_events)

    def remove_events(self):
        self.parent.target.event.remove("pdirty")
        self.parent.target.event.remove("ldirty")

    def scale_release(self, *args):
        _base_values.run_update = False
        self.after(1, self.add_events)

    def add_events(self):
        self.parent.target.event.add("pdirty")
        self.parent.target.event.add("ldirty")
        self.after(350, self.resume_updates)

    def pause_updates(self, func, *args):
        """function wrapper, adds a 50ms delay on updates"""
        _base_values.run_update = False

        func(*args)

        self.after(50, self.resume_updates)

    def resume_updates(self):
        _base_values.run_update = True

    def scale_enter(self, param, *args):
        val = self.slider_vars[self.slider_params.index(param)].get()
        self.parent.nav_frame.info_text.set(round(val, 1))

    def scale_leave(self, *args):
        self.parent.nav_frame.info_text.set("")

    def scale_callback(self, param, *args):
        """callback function for scale widget"""

        val = self.slider_vars[self.slider_params.index(param)].get()
        self.setter(param, round(val, 1))
        self.parent.nav_frame.info_text.set(round(val, 1))

    def reset_scale(self, param, val, *args):
        self.setter(param, val)
        self.slider_vars[self.slider_params.index(param)].set(val)

    def toggle_p(self, param):
        val = self.param_vars[self.params.index(param)].get()
        self.setter(param, val)
        if not _configuration.themes_enabled:
            self.styletable.configure(
                f"{param}.TButton", background=f'{"green" if val else "white"}'
            )

    def on_update(self, subject):
        """update parameters"""
        if subject == "pdirty":
            self.sync()


class StripConfig(Config):
    def __init__(self, parent, index, _id):
        super().__init__(parent, index, _id)
        self.grid(column=0, row=1, columnspan=4, padx=(2,))
        self.builder = builders.StripConfigFrameBuilder(self)
        self.builder.setup()
        self.make_row_0()
        self.make_row_1()
        self.make_row_2()
        self.builder.grid_configure()

        self.parent.target.clear_dirty()
        self.sync()

    @property
    def target(self):
        """returns the strip class for this configframe in the current interface"""

        _target = super(StripConfig, self).target
        return getattr(_target, self.identifier)[self.index]

    def make_row_0(self):
        if self.index < self.phys_in:
            if self.parent.kind.name == "basic":
                self.builder.create_audibility_slider()
            else:
                self.builder.create_comp_slider()
                self.builder.create_gate_slider()
                self.builder.create_limit_slider()

    def make_row_1(self):
        self.builder.create_a_buttons()
        self.builder.create_b_buttons()

    def make_row_2(self):
        self.builder.create_param_buttons()

    def toggle_a(self, param):
        val = self.phys_out_params_vars[self.phys_out_params.index(param)].get()
        self.setter(param, val)
        if not _configuration.themes_enabled:
            self.styletable.configure(
                f"{param}.TButton", background=f'{"green" if val else "white"}'
            )

    def toggle_b(self, param):
        val = self.virt_out_params_vars[self.virt_out_params.index(param)].get()
        self.setter(param, val)
        if not _configuration.themes_enabled:
            self.styletable.configure(
                f"{param}.TButton", background=f'{"green" if val else "white"}'
            )

    def teardown(self):
        self.builder.teardown()

    def sync(self):
        [
            self.phys_out_params_vars[i].set(self.getter(param))
            for i, param in enumerate(self.phys_out_params)
        ]
        [
            self.virt_out_params_vars[i].set(self.getter(param))
            for i, param in enumerate(self.virt_out_params)
        ]
        [
            self.param_vars[i].set(self.getter(param))
            for i, param in enumerate(self.params)
        ]
        if not _base_values.vban_connected:  # slider vars not defined in RT Packet
            [
                self.slider_vars[i].set(self.getter(param))
                for i, param in enumerate(self.slider_params)
                if self.index < self.phys_in
            ]

        if not _configuration.themes_enabled:
            [
                self.styletable.configure(
                    f"{param}.TButton",
                    background=f'{"green" if self.phys_out_params_vars[i].get() else "white"}',
                )
                for i, param in enumerate(self.phys_out_params)
            ]
            [
                self.styletable.configure(
                    f"{param}.TButton",
                    background=f'{"green" if self.virt_out_params_vars[i].get() else "white"}',
                )
                for i, param in enumerate(self.virt_out_params)
            ]
            [
                self.styletable.configure(
                    f"{param}.TButton",
                    background=f'{"green" if self.param_vars[i].get() else "white"}',
                )
                for i, param in enumerate(self.params)
            ]


class BusConfig(Config):
    def __init__(self, parent, index, _id):
        super().__init__(parent, index, _id)
        if _configuration.extends_horizontal:
            self.grid(column=0, row=1, columnspan=4, padx=(2,))
        else:
            self.grid(column=0, row=3, columnspan=4, padx=(2,))
        self.builder = builders.BusConfigFrameBuilder(self)
        self.builder.setup()
        self.make_row_0()
        self.make_row_1()
        self.builder.grid_configure()

        self.parent.target.clear_dirty()
        self.sync()

    @property
    def target(self):
        """returns the bus class for this configframe in the current interface"""

        _target = super(BusConfig, self).target
        return getattr(_target, self.identifier)[self.index]

    def make_row_0(self):
        self.builder.create_bus_mode_button()

    def make_row_1(self):
        self.builder.create_param_buttons()

    def current_bus_mode(self):
        for mode in self.bus_modes:
            if getattr(self.target.mode, mode):
                return mode

    def rotate_bus_modes_right(self, *args):
        current_mode = self.current_bus_mode()
        next = self.bus_modes.index(current_mode) + 1
        if next < len(self.bus_modes):
            setattr(
                self.target.mode,
                self.bus_modes[next],
                True,
            )
            self.bus_mode_label_text.set(self.bus_mode_map[self.bus_modes[next]])
        else:
            self.target.mode.normal = True
            self.bus_mode_label_text.set("Normal")

    def rotate_bus_modes_left(self, *args):
        current_mode = self.current_bus_mode()
        prev = self.bus_modes.index(current_mode) - 1
        if prev < 0:
            self.target.mode.rearonly = True
            self.bus_mode_label_text.set("Rear Only")
        else:
            setattr(
                self.target.mode,
                self.bus_modes[prev],
                True,
            )
            self.bus_mode_label_text.set(self.bus_mode_map[self.bus_modes[prev]])

    def teardown(self):
        self.builder.teardown()

    def sync(self):
        [
            self.param_vars[i].set(self.getter(param))
            for i, param in enumerate(self.params)
        ]
        self.bus_mode_label_text.set(self.bus_mode_map[self.current_bus_mode()])
        if not _configuration.themes_enabled:
            [
                self.styletable.configure(
                    f"{param}.TButton",
                    background=f'{"green" if self.param_vars[i].get() else "white"}',
                )
                for i, param in enumerate(self.params)
            ]
