# Copyright 2021 - 2022, Martijn Braam and the OpenAtem contributors
# SPDX-License-Identifier: GPL-3.0-only
import gi

from gtk_switcher.adjustmententry import AdjustmentEntry
from gtk_switcher.dial import Dial
from gtk_switcher.eqwindow import EqWindow
from gtk_switcher.gtklogadjustment import LogAdjustment
from gtk_switcher.eqcurve import EqCurve
from pyatem.command import AudioInputCommand, FairlightStripPropertiesCommand, FairlightMasterPropertiesCommand, \
    AudioMasterPropertiesCommand, AudioMonitorPropertiesCommand, SendAudioLevelsCommand, SendFairlightLevelsCommand, \
    AuxSourceCommand

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, GObject, Gio, Gdk

gi.require_version('Handy', '1')
from gi.repository import Handy


class AudioPage:
    def __init__(self, builder):
        self.audio_channels = builder.get_object('audio_channels')

        self.mixer = 'atem'

        self.volume_level = {}
        self.input_gain = {}
        self.pan = {}
        self.delay = {}
        self.audio_tally = {}
        self.audio_strip = {}
        self.audio_on = {}
        self.audio_afv = {}
        self.audio_monitor = {}
        self.vu = {}
        self.eq = {}

        self.master_level = None
        self.master_afv = None
        self.monitor_level = None
        self.monitor_on = None
        self.monitor_dim = None

    def clear_audio_state(self):
        self.mixer = 'atem'
        self.volume_level = {}
        self.input_gain = {}
        self.pan = {}
        self.delay = {}
        self.audio_tally = {}
        self.audio_strip = {}
        self.audio_on = {}
        self.audio_afv = {}
        self.audio_monitor = {}
        self.vu = {}
        self.eq = {}

        self.master_level = None
        self.master_afv = None
        self.monitor_level = None
        self.monitor_on = None
        self.monitor_dim = None

        self.unrealize_audio()

    def _make_mixer_ui_label(self, index, name):
        label = Gtk.Label(label=name)
        label.get_style_context().add_class('dim-label')
        label.set_halign(Gtk.Align.END)
        label.set_valign(Gtk.Align.START)
        self.audio_channels.attach(label, 0, index, 1, 1)

    def unrealize_audio(self):
        for child in self.audio_channels:
            child.destroy()

    def make_mixer_ui(self, inputs):
        # Clear the existing channels
        self.unrealize_audio()

        # Create row of labels again
        if self.mixer == "fairlight":
            self._make_mixer_ui_label(2, "Input")
            self._make_mixer_ui_label(3, "Equalizer")
            self._make_mixer_ui_label(4, "Dynamics")
        self._make_mixer_ui_label(5, "dB")
        self._make_mixer_ui_label(6, "Pan")

        # Create channel strips
        strip_index = 1
        last_type = 0
        for input in inputs.values():
            if self.mixer == 'atem':
                self.audio_strip[input.strip_id] = input

            # Fairlight stereo inputs can be split in multiple mono strips
            num_subchannels = 1
            if self.mixer == 'fairlight' and input.split == 4:
                num_subchannels = 2

            # Create seperator if the last strip was a different category
            if input.type != last_type:
                last_type = input.type
                type_sep = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
                type_sep.get_style_context().add_class('dark')
                type_sep.set_margin_left(8)
                type_sep.set_margin_right(8)
                self.audio_channels.attach(type_sep, strip_index, 0, 1, 8)
                strip_index += 1

            # Get channelstrip label
            if input.type == 0:
                label = self.connection.mixer.mixerstate['input-properties'][input.index].short_name
            elif input.type == 1:
                label = "MP {}".format(input.number + 1)
            else:
                connector = 'Analog'
                if hasattr(input, 'plug_name'):
                    connector = input.plug_name()
                elif 1300 <= input.index <= 1400:
                    connector = 'Mic'
                label = f"{connector} {input.number + 1}"

            label = Gtk.Label(label=label)
            self.audio_channels.attach(label, strip_index, 0, num_subchannels, 1)

            # Loop through all the strips generated by this input, always 1 for atem audio
            for c in range(0, num_subchannels):
                if self.mixer == 'fairlight':
                    strip_id = str(input.index) + '.' + str(c)
                else:
                    strip_id = input.strip_id

                # Create GTK backing controls if the strip has never existed
                if strip_id not in self.volume_level:
                    if self.mixer == 'atem':
                        self.volume_level[strip_id] = LogAdjustment(input.volume, 0, 65381, 10, 10, 100)
                        self.pan[strip_id] = Gtk.Adjustment(input.balance, -10000, 10000, 10, 10, 100)
                    else:
                        self.volume_level[strip_id] = LogAdjustment(0, -10000, 1000, 10, 10, 100, exp=True, range=30)
                        self.pan[strip_id] = Gtk.Adjustment(0, -10000, 10000, 10, 10, 100)
                        self.input_gain[strip_id] = LogAdjustment(0, -10000, 600, 10, 10, 100, exp=True, range=30)
                        self.delay[strip_id] = Gtk.Adjustment(0, 0, 8, 1, 1, 1)

                    self.volume_level[strip_id].connect('value-changed', self.on_volume_changed)
                    if strip_id in self.input_gain:
                        self.input_gain[strip_id].connect('value-changed', self.on_input_gain_changed)
                    self.pan[strip_id].connect('value-changed', self.on_pan_changed)

                    self.volume_level[strip_id].source = input.index
                    self.volume_level[strip_id].channel = c if num_subchannels > 1 else -1
                    self.pan[strip_id].source = input.index
                    self.pan[strip_id].channel = c if num_subchannels > 1 else -1
                    if self.mixer == 'fairlight':
                        self.input_gain[strip_id].source = input.index
                        self.input_gain[strip_id].channel = c if num_subchannels > 1 else -1

                # Create the tally "led"
                tally = Gtk.Box()
                tally.get_style_context().add_class('tally')
                if strip_id in self.audio_strip:
                    if self.mixer == 'fairlight':
                        tally_state = self.audio_strip[strip_id].state & 4
                    else:
                        tally_state = self.audio_strip[strip_id].state == 2
                    self.set_class(tally, 'afv', tally_state)
                self.audio_tally[strip_id] = tally
                self.audio_channels.attach(tally, strip_index + c, 1, 1, 1)

                # Create the controls
                if self.mixer == 'fairlight':
                    if len(self.input_gain) == 0:
                        return
                    input_frame = Gtk.Frame()
                    input_frame.get_style_context().add_class('view')
                    dial = Dial()
                    dial.set_adjustment(self.input_gain[strip_id])
                    gain_input = AdjustmentEntry(self.input_gain[strip_id], -100, 6)
                    gain_input.get_style_context().add_class('mini')
                    gain_input.set_margin_left(16)
                    gain_input.set_margin_right(16)
                    gain_input.set_margin_end(8)
                    gain_input.set_max_width_chars(6)
                    gain_input.set_width_chars(6)
                    gain_input.set_alignment(xalign=0.5)
                    self.hook_up_focus(gain_input)
                    gain_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
                    gain_box.add(dial)
                    gain_box.add(gain_input)
                    input_frame.add(gain_box)
                    self.audio_channels.attach(input_frame, strip_index + c, 2, 1, 1)

                    eq_frame = EqCurve()
                    eq_frame.connect('button-release-event', self.on_open_eq_overlay)
                    eq_frame.strip_id = strip_id
                    if 'atem-eq-band-properties' in self.connection.mixer.mixerstate and strip_id in \
                            self.connection.mixer.mixerstate['atem-eq-band-properties']:
                        for band_id in self.connection.mixer.mixerstate['atem-eq-band-properties'][strip_id]:
                            band = self.connection.mixer.mixerstate['atem-eq-band-properties'][strip_id][band_id]
                            eq_frame.update_band(band)
                    if 'fairlight-strip-properties' in self.connection.mixer.mixerstate and strip_id in \
                            self.connection.mixer.mixerstate['fairlight-strip-properties']:
                        strip = self.connection.mixer.mixerstate['fairlight-strip-properties'][strip_id]
                        eq_frame.set_enabled(strip.eq_enable)

                    eq_frame.get_style_context().add_class('view')
                    eq_frame.set_size_request(0, 64)
                    self.eq[strip_id] = eq_frame
                    self.audio_channels.attach(eq_frame, strip_index + c, 3, 1, 1)

                    dynamics_frame = Gtk.Frame()
                    dynamics_frame.get_style_context().add_class('view')
                    dynamics_frame.set_size_request(0, 64)
                    self.audio_channels.attach(dynamics_frame, strip_index + c, 4, 1, 1)

                # Volume fader
                volume_frame = Gtk.Frame()
                volume_frame.get_style_context().add_class('view')
                volume_frame.set_size_request(76, 0)
                volume_frame.set_vexpand(True)
                volume_box = Gtk.Box()
                volume_frame.add(volume_box)
                volume_slider = Gtk.Scale(orientation=Gtk.Orientation.VERTICAL,
                                          adjustment=self.volume_level[strip_id])
                volume_slider.set_inverted(True)
                volume_slider.set_draw_value(False)
                volume_slider.get_style_context().add_class('volume')
                volume_slider.connect('button-press-event', self.on_slider_held)
                volume_slider.connect('button-release-event', self.on_slider_released)
                volume_box.pack_start(volume_slider, True, True, 0)
                volume_box.set_margin_left(8)
                volume_box.set_margin_right(8)
                volume_box.set_margin_top(24)
                volume_box.set_margin_bottom(8)
                vu_left = Gtk.ProgressBar()
                vu_right = Gtk.ProgressBar()
                vu_left.set_orientation(Gtk.Orientation.VERTICAL)
                vu_left.set_inverted(True)
                vu_right.set_orientation(Gtk.Orientation.VERTICAL)
                vu_right.set_inverted(True)
                self.vu[strip_id] = (vu_left, vu_right)
                volume_box.pack_start(vu_left, False, True, 0)
                volume_box.pack_start(vu_right, False, True, 0)
                self.audio_channels.attach(volume_frame, strip_index + c, 5, 1, 1)

                # Pan dial
                pan_frame = Gtk.Frame()
                pan_frame.get_style_context().add_class('view')
                pan_dial = Dial()
                pan_dial.set_adjustment(self.pan[strip_id])

                pan_input = AdjustmentEntry(self.pan[strip_id], -100, 100)
                pan_input.get_style_context().add_class('mini')
                pan_input.set_margin_left(16)
                pan_input.set_margin_right(16)
                pan_input.set_margin_end(8)
                pan_input.set_max_width_chars(6)
                pan_input.set_width_chars(6)
                pan_input.set_alignment(xalign=0.5)
                self.hook_up_focus(pan_input)
                pan_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
                pan_box.add(pan_dial)
                pan_box.add(pan_input)
                pan_frame.add(pan_box)
                self.audio_channels.attach(pan_frame, strip_index + c, 6, 1, 1)

                # Channel strip routing controls
                routing_grid = Gtk.Grid()
                routing_grid.set_row_homogeneous(True)
                routing_grid.set_column_homogeneous(True)
                routing_grid.set_row_spacing(8)
                routing_grid.set_column_spacing(8)
                on = Gtk.Button(label="ON")
                on.source = input.index
                on.channel = c if num_subchannels > 1 else -1
                on.connect('clicked', self.do_channel_on)
                on.get_style_context().add_class('bmdbtn')
                routing_grid.attach(on, 0, 0, 1, 1)
                afv = Gtk.Button(label="AFV")
                afv.connect('clicked', self.do_channel_afv)
                afv.get_style_context().add_class('bmdbtn')
                afv.source = input.index
                afv.channel = c if num_subchannels > 1 else -1
                if input.type == 2:
                    afv.set_sensitive(False)
                routing_grid.attach(afv, 1, 0, 1, 1)
                mon = Gtk.Button(label="Monitor")
                mon.get_style_context().add_class('bmdbtn')
                mon.connect('clicked', self.do_channel_monitor)
                mon.source = input.index
                mon.channel = c if num_subchannels > 1 else -1
                routing_grid.attach(mon, 0, 1, 2, 1)
                self.audio_channels.attach(routing_grid, strip_index + c, 7, 1, 1)
                self.audio_on[strip_id] = on
                self.audio_afv[strip_id] = afv
                self.audio_monitor[strip_id] = mon
                if strip_id in self.audio_strip:
                    if self.mixer == 'atem':
                        self.set_class(afv, 'active', self.audio_strip[strip_id].state == 2)
                        self.set_class(on, 'program', self.audio_strip[strip_id].state == 1)
                    else:
                        self.set_class(afv, 'active', self.audio_strip[strip_id].state & 4)
                        self.set_class(on, 'program', self.audio_strip[strip_id].state & 2)

            strip_index += num_subchannels

        # Create the seperator for the master bus group
        master_sep = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
        master_sep.get_style_context().add_class('dark')
        master_sep.set_margin_left(8)
        master_sep.set_margin_right(8)
        self.audio_channels.attach(master_sep, strip_index, 0, 1, 8)
        strip_index += 1

        # Create the bus strips
        for c in range(0, 1):
            label = Gtk.Label(label="Master")
            self.audio_channels.attach(label, strip_index + c, 0, 1, 1)
            tally = Gtk.Box()
            tally.get_style_context().add_class('tally')
            tally.get_style_context().add_class('program')
            self.audio_channels.attach(tally, strip_index + c, 1, 1, 1)

            volume_frame = Gtk.Frame()
            volume_frame.set_size_request(76, 0)
            volume_frame.get_style_context().add_class('view')
            volume_frame.set_vexpand(True)
            volume_box = Gtk.Box()
            volume_frame.add(volume_box)
            volume_slider = Gtk.Scale(orientation=Gtk.Orientation.VERTICAL,
                                      adjustment=self.master_level)
            volume_slider.set_inverted(True)
            volume_slider.set_draw_value(False)
            volume_slider.get_style_context().add_class('volume')
            volume_slider.connect('button-press-event', self.on_slider_held)
            volume_slider.connect('button-release-event', self.on_slider_released)
            volume_box.pack_start(volume_slider, True, True, 0)
            volume_box.set_margin_left(8)
            volume_box.set_margin_right(8)
            volume_box.set_margin_top(24)
            volume_box.set_margin_bottom(8)
            vu_left = Gtk.ProgressBar()
            vu_right = Gtk.ProgressBar()
            vu_left.set_orientation(Gtk.Orientation.VERTICAL)
            vu_left.set_inverted(True)
            vu_right.set_orientation(Gtk.Orientation.VERTICAL)
            vu_right.set_inverted(True)
            self.vu['master'] = (vu_left, vu_right)
            volume_box.pack_start(vu_left, False, True, 0)
            volume_box.pack_start(vu_right, False, True, 0)
            self.audio_channels.attach(volume_frame, strip_index + c, 5, 1, 1)

            # Create monitoring frame
            self.master_afv = Gtk.Button(label="AFV")
            self.master_afv.connect('clicked', self.do_master_afv)
            self.master_afv.set_margin_bottom(8)
            self.master_afv.get_style_context().add_class('bmdbtn')

            label = Gtk.Label("Monitor")
            monitoring_wrap = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
            monitoring_frame = Gtk.Frame()
            monitoring_frame.get_style_context().add_class('view')
            monitoring_wrap.add(self.master_afv)
            monitoring_wrap.add(monitoring_frame)
            self.audio_channels.attach(monitoring_wrap, strip_index + c, 6, 1, 2)
            monitor_grid = Gtk.Grid()
            monitor_grid.set_column_spacing(4)
            monitor_grid.set_row_spacing(4)
            monitor_grid.set_margin_top(2)
            monitor_grid.set_margin_bottom(4)
            monitor_grid.set_margin_start(6)
            monitor_grid.set_margin_end(6)
            monitoring_frame.add(monitor_grid)
            monitor_dial = Dial()
            monitor_dial.set_adjustment(self.monitor_level)
            monitor_grid.attach(label, 0, 0, 3, 1)
            monitor_grid.attach(monitor_dial, 0, 1, 2, 1)

            self.monitor_on = Gtk.Button(label="ON")
            self.monitor_on.get_style_context().add_class('bmdbtn')
            self.monitor_on.connect('clicked', self.do_monitor_on)
            self.monitor_dim = Gtk.Button(label="Dim")
            self.monitor_dim.get_style_context().add_class('bmdbtn')
            self.monitor_dim.connect('clicked', self.do_monitor_dim)
            monitor_grid.attach(self.monitor_on, 0, 2, 1, 1)
            monitor_grid.attach(self.monitor_dim, 1, 2, 1, 1)
            vu_left = Gtk.ProgressBar()
            vu_right = Gtk.ProgressBar()
            vu_left.set_orientation(Gtk.Orientation.VERTICAL)
            vu_left.set_inverted(True)
            vu_right.set_orientation(Gtk.Orientation.VERTICAL)
            vu_right.set_inverted(True)
            vu_box = Gtk.Box()
            vu_box.pack_start(vu_left, False, False, 0)
            vu_box.pack_start(vu_right, False, False, 0)
            self.vu['monitor'] = (vu_left, vu_right)
            monitor_grid.attach(vu_box, 2, 1, 1, 2)

        self.apply_css(self.audio_channels, self.provider)
        self.audio_channels.show_all()

    def on_audio_input_change(self, data):
        self.mixer = 'atem'

        if len(self.volume_level) == 0:
            self.master_level = LogAdjustment(0, 0, 65381, 10, 10, 100)
            self.master_level.connect('value-changed', self.on_master_changed)
            self.monitor_level = LogAdjustment(0, 0, 65381, 10, 10, 100)
            self.monitor_level.connect('value-changed', self.on_monitor_level_changed)
            self.make_mixer_ui(self.connection.mixer.mixerstate['audio-input'])

        self.audio_strip[data.strip_id] = data
        if data.strip_id not in self.volume_level:
            self.volume_level[data.strip_id] = LogAdjustment(0, 65381, 0, 10, 10, 100)
            self.pan[data.strip_id] = Gtk.Adjustment(0, -10000, 10000, 10, 10, 100)
            self.volume_level[data.strip_id].connect('value-changed', self.on_volume_changed)
            self.pan[data.strip_id].source = data.index
            self.pan[data.strip_id].connect('value-changed', self.on_pan_changed)

        self.model_changing = True
        self.volume_level[data.strip_id].set_value_log(data.volume)
        self.pan[data.strip_id].set_value(data.balance)
        if data.strip_id in self.audio_tally:
            tally = self.audio_tally[data.strip_id]
            self.set_class(tally, 'afv', data.state == 2)
        if data.strip_id in self.audio_on:
            self.set_class(self.audio_on[data.strip_id], 'program', data.state == 1)
            self.set_class(self.audio_afv[data.strip_id], 'active', data.state == 2)
        self.model_changing = False

    def on_fairlight_audio_input_change(self, data):
        self.mixer = 'fairlight'
        self.master_level = LogAdjustment(0, -10000, 1100, 10, 10, 100, range=30, exp=True)
        self.master_level.connect('value-changed', self.on_master_changed)
        self.monitor_level = LogAdjustment(0, -10000, 1100, 10, 10, 100, range=30, exp=True)
        self.monitor_level.connect('value-changed', self.on_monitor_level_changed)
        inputs = self.connection.mixer.mixerstate['fairlight-audio-input']
        self.make_mixer_ui(inputs)

    def on_audio_mixer_tally_change(self, data):
        for strip_id in data.tally:
            if strip_id in self.audio_tally:
                self.set_class(self.audio_tally[strip_id], 'program', data.tally[strip_id])

    def on_fairlight_tally_change(self, data):
        for strip_id in data.tally:
            if strip_id in self.audio_tally:
                self.set_class(self.audio_tally[strip_id], 'program', data.tally[strip_id])

    def on_fairlight_strip_properties_change(self, data):
        """
        :type data: FairlightStripPropertiesField
        :return:
        """
        self.audio_strip[data.strip_id] = data
        if data.strip_id not in self.volume_level:
            self.volume_level[data.strip_id] = LogAdjustment(0, -10000, 1000, 10, 10, 100, exp=True)
            self.pan[data.strip_id] = Gtk.Adjustment(0, -10000, 10000, 10, 10, 100)
            self.input_gain[data.strip_id] = LogAdjustment(0, -10000, 600, 10, 10, 100)
            self.delay[data.strip_id] = Gtk.Adjustment(0, 0, 8, 1, 1, 1)
            self.volume_level[data.strip_id].connect('value-changed', self.on_volume_changed)

        self.model_changing = True
        self.volume_level[data.strip_id].set_value_log(data.volume)
        self.pan[data.strip_id].set_value(data.pan)
        self.input_gain[data.strip_id].set_value_log(data.gain)
        self.delay[data.strip_id].set_value(data.delay)
        if data.strip_id in self.audio_tally:
            tally = self.audio_tally[data.strip_id]
            self.set_class(tally, 'afv', data.state == 4)
        if data.strip_id in self.audio_on:
            self.set_class(self.audio_on[data.strip_id], 'program', data.state & 2)
            self.set_class(self.audio_afv[data.strip_id], 'active', data.state & 4)

        self.eq[data.strip_id].set_enabled(data.eq_enable)
        self.model_changing = False

    def on_audio_mixer_master_properties_change(self, data):
        self.model_changing = True
        self.master_level.set_value_log(data.volume)
        self.set_class(self.master_afv, 'active', data.afv)
        self.set_class(self.ftb_afv, 'active', data.afv)
        self.model_changing = False

    def on_fairlight_eq_band_change(self, data):
        if data.strip_id not in self.eq:
            return
        self.eq[data.strip_id].update_band(data)

    def on_volume_changed(self, widget, *args):
        if self.model_changing:
            return
        if self.mixer == 'fairlight':
            cmd = FairlightStripPropertiesCommand(source=widget.source, channel=widget.channel,
                                                  volume=int(widget.get_value_log()))
            self.connection.mixer.send_commands([cmd])
        elif self.mixer == 'atem':
            cmd = AudioInputCommand(source=widget.source, volume=int(widget.get_value_log()))
            self.connection.mixer.send_commands([cmd])

    def on_master_changed(self, widget, *args):
        if self.model_changing:
            return
        if self.mixer == 'fairlight':
            cmd = FairlightMasterPropertiesCommand(volume=int(widget.get_value_log()))
            self.connection.mixer.send_commands([cmd])
        elif self.mixer == 'atem':
            cmd = AudioMasterPropertiesCommand(volume=int(widget.get_value_log()))
            self.connection.mixer.send_commands([cmd])

    def on_monitor_level_changed(self, widget, *args):
        if self.model_changing:
            return
        if self.mixer == 'atem':
            cmd = AudioMonitorPropertiesCommand(volume=int(widget.get_value_log()))
            self.connection.mixer.send_commands([cmd])
        else:
            pass

    def on_pan_changed(self, widget, *args):
        if self.model_changing:
            return
        if self.mixer == 'fairlight':
            cmd = FairlightStripPropertiesCommand(source=widget.source, channel=widget.channel,
                                                  balance=int(widget.get_value()))
            self.connection.mixer.send_commands([cmd])
        elif self.mixer == 'atem':
            cmd = AudioInputCommand(source=widget.source, balance=int(widget.get_value()))
            self.connection.mixer.send_commands([cmd])

    def on_input_gain_changed(self, widget, *args):
        if self.model_changing:
            return
        if self.mixer == 'fairlight':
            cmd = FairlightStripPropertiesCommand(source=widget.source, channel=widget.channel,
                                                  gain=int(widget.get_value_log()))
            self.connection.mixer.send_commands([cmd])

    def do_channel_on(self, widget, *args):
        if self.mixer == 'atem':
            cmd = AudioInputCommand(source=widget.source, on=(not widget.get_style_context().has_class('program')))
        else:
            if widget.get_style_context().has_class('program'):
                state = 1
            else:
                state = 2
            cmd = FairlightStripPropertiesCommand(source=widget.source, channel=widget.channel, state=state)
        self.connection.mixer.send_commands([cmd])

    def do_channel_afv(self, widget, *args):
        if self.mixer == 'atem':
            cmd = AudioInputCommand(source=widget.source, afv=(not widget.get_style_context().has_class('active')))
        else:
            if widget.get_style_context().has_class('active'):
                state = 1
            else:
                state = 4
            cmd = FairlightStripPropertiesCommand(source=widget.source, channel=widget.channel, state=state)
        self.connection.mixer.send_commands([cmd])

    def do_master_afv(self, widget, *args):
        if self.mixer == 'atem':
            cmd = AudioMasterPropertiesCommand(afv=not widget.get_style_context().has_class('active'))
        else:
            cmd = FairlightMasterPropertiesCommand(afv=not widget.get_style_context().has_class('active'))
        self.connection.mixer.send_commands([cmd])

    def do_monitor_on(self, widget, *args):
        if self.mixer == 'atem':
            state = widget.get_style_context().has_class('active')
            cmd = AudioMonitorPropertiesCommand(mute=state)
            self.connection.mixer.send_commands([cmd])
        else:
            pass

    def do_monitor_dim(self, widget, *args):
        if self.mixer == 'atem':
            state = widget.get_style_context().has_class('active')
            cmd = AudioMonitorPropertiesCommand(dim=not state)
            self.connection.mixer.send_commands([cmd])
        else:
            pass

    def on_aux_monitor_source_change(self, data):
        strip_id = f'{data.source}.0'
        if strip_id in self.audio_monitor:
            for mon in self.audio_monitor:
                self.set_class(self.audio_monitor[mon], 'active', mon == strip_id)

    def do_channel_monitor(self, widget, *args):
        # When aux-follow-monitor is enabled send the monitor commands as aux switching commands to
        # the hardware for the selected aux outputs, this will make the aux sdi out have monitor
        # audio for ATEM devices without any real monitor outputs, which is a lot of them
        if len(self.aux_follow_audio) > 0:
            cmd = []
            for bus in self.aux_follow_audio:
                cmd.append(AuxSourceCommand(bus, source=widget.source))
            self.connection.mixer.send_commands(cmd)

        if self.mixer == 'atem':
            state = widget.get_style_context().has_class('active')
            cmd = AudioMonitorPropertiesCommand(solo=not state, solo_source=widget.source)
            self.connection.mixer.send_commands([cmd])
        else:
            pass

    def on_fairlight_master_properties_change(self, data):
        self.model_changing = True
        self.master_level.set_value_log(data.volume)
        self.set_class(self.master_afv, 'active', data.afv)
        self.set_class(self.ftb_afv, 'active', data.afv)
        self.model_changing = False

    def on_audio_monitor_properties_change(self, data):
        self.model_changing = True
        self.monitor_level.set_value_log(data.volume)
        self.set_class(self.monitor_on, 'active', not data.mute)
        self.set_class(self.monitor_dim, 'active', data.dim)
        for m in self.audio_monitor:
            self.audio_monitor[m].set_sensitive(data.enabled)
            en = (m == str(data.solo_source) + ".0") and data.solo
            self.set_class(self.audio_monitor[m], 'active', en)
        self.monitor_on.set_sensitive(data.enabled)
        self.monitor_dim.set_sensitive(data.enabled)
        self.model_changing = False

    def enable_levels(self):
        """ The audio page was opened, request data for the levels """
        if self.mixer == 'atem':
            cmd = SendAudioLevelsCommand(True)
        else:
            cmd = SendFairlightLevelsCommand(True)
        self.connection.mixer.send_commands([cmd])

    def disable_levels(self):
        """ The audio page was closed, stop getting the realtime levels """
        if self.mixer == 'atem':
            cmd = SendAudioLevelsCommand(False)
        else:
            cmd = SendFairlightLevelsCommand(False)
        self.connection.mixer.send_commands([cmd])

    def on_audio_meter_levels_change(self, data):
        if 'master' not in self.vu:
            return
        self.vu['master'][0].set_fraction((data.master[0] + 60) / 60)
        self.vu['master'][1].set_fraction((data.master[1] + 60) / 60)
        self.vu['monitor'][0].set_fraction((data.monitor[0] + 60) / 60)
        self.vu['monitor'][1].set_fraction((data.monitor[1] + 60) / 60)
        for strip in data.input:
            strip_id = f'{strip}.0'
            self.vu[strip_id][0].set_fraction((data.input[strip][0] + 60) / 60)
            self.vu[strip_id][1].set_fraction((data.input[strip][1] + 60) / 60)

    def on_fairlight_meter_levels_change(self, data):
        if data.strip_id not in self.vu:
            return
        self.vu[data.strip_id][0].set_fraction((data.level[0] + 60) / 60)
        self.vu[data.strip_id][1].set_fraction((data.level[1] + 60) / 60)

    def on_fairlight_master_levels_change(self, data):
        if 'master' not in self.vu:
            return
        self.vu['master'][0].set_fraction((data.level[0] + 60) / 60)
        self.vu['master'][1].set_fraction((data.level[1] + 60) / 60)

    def on_open_eq_overlay(self, widget, *args):
        EqWindow(widget.strip_id, self.window, self.connection, self.provider)
