from fabric.audio.service import Audio
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.circularprogressbar import CircularProgressBar
from fabric.widgets.eventbox import EventBox
from fabric.widgets.label import Label
from fabric.widgets.overlay import Overlay
from fabric.widgets.scale import Scale
from gi.repository import Gdk, GLib

import config.data as data
import modules.icons as icons
from services.brightness import Brightness


class VolumeSlider(Scale):
    def __init__(self, **kwargs):
        super().__init__(
            name="control-slider",
            orientation="h",
            h_expand=True,
            h_align="fill",
            has_origin=True,
            increments=(0.01, 0.1),
            **kwargs,
        )
        self.audio = Audio()
        self.audio.connect("notify::speaker", self.on_new_speaker)
        if self.audio.speaker:
            self.audio.speaker.connect("changed", self.on_speaker_changed)
        self.connect("change-value", self.on_change_value)
        self.add_style_class("vol")
        self._pending_value = None
        self._update_source_id = None
        self._debounce_timeout = 100
        self._updating_from_audio = False
        self.on_speaker_changed()

    def on_new_speaker(self, *args):
        if self.audio.speaker:
            self.audio.speaker.connect("changed", self.on_speaker_changed)
            self.on_speaker_changed()

    def on_change_value(self, widget, scroll, value):
        if self._updating_from_audio:
            return False
        if self.audio.speaker:
            self._pending_value = value * 100
            if self._update_source_id is not None:
                GLib.source_remove(self._update_source_id)
            self._update_source_id = GLib.timeout_add(
                self._debounce_timeout, self._update_volume_callback
            )
        return False

    def _update_volume_callback(self):
        if self._pending_value is not None and self.audio.speaker:
            self.audio.speaker.volume = self._pending_value
            self._pending_value = None
        self._update_source_id = None
        return False

    def on_speaker_changed(self, *_):
        if not self.audio.speaker:
            return
        self._updating_from_audio = True
        self.value = self.audio.speaker.volume / 100
        self._updating_from_audio = False

        if self.audio.speaker.muted:
            self.add_style_class("muted")
        else:
            self.remove_style_class("muted")


class MicSlider(Scale):
    def __init__(self, **kwargs):
        super().__init__(
            name="control-slider",
            orientation="h",
            h_expand=True,
            has_origin=True,
            increments=(0.01, 0.1),
            **kwargs,
        )
        self.audio = Audio()
        self.audio.connect("notify::microphone", self.on_new_microphone)
        if self.audio.microphone:
            self.audio.microphone.connect("changed", self.on_microphone_changed)
        self.connect("change-value", self.on_change_value)
        self.add_style_class("mic")
        self._updating_from_audio = False
        self.on_microphone_changed()

    def on_new_microphone(self, *args):
        if self.audio.microphone:
            self.audio.microphone.connect("changed", self.on_microphone_changed)
            self.on_microphone_changed()

    def on_change_value(self, widget, scroll, value):
        if self._updating_from_audio:
            return False
        if self.audio.microphone:
            self.audio.microphone.volume = value * 100
        return False

    def on_microphone_changed(self, *_):
        if not self.audio.microphone:
            return
        self._updating_from_audio = True
        self.value = self.audio.microphone.volume / 100
        self._updating_from_audio = False

        if self.audio.microphone.muted:
            self.add_style_class("muted")
        else:
            self.remove_style_class("muted")


class BrightnessSlider(Scale):
    def __init__(self, **kwargs):
        super().__init__(
            name="control-slider",
            orientation="h",
            h_expand=True,
            has_origin=True,
            increments=(5, 10),
            **kwargs,
        )
        self.client = Brightness.get_initial()
        if self.client.screen_brightness == -1:
            self.destroy()
            return

        self.set_range(0, self.client.max_screen)
        self.set_value(self.client.screen_brightness)
        self.add_style_class("brightness")

        self._pending_value = None
        self._update_source_id = None
        self._updating_from_brightness = False
        self._debounce_timeout = 100

        self.connect("change-value", self.on_scale_move)
        self.client.connect("screen", self.on_brightness_changed)

    def on_scale_move(self, widget, scroll, moved_pos):
        if self._updating_from_brightness:
            return False
        self._pending_value = moved_pos
        if self._update_source_id is not None:
            GLib.source_remove(self._update_source_id)
        self._update_source_id = GLib.timeout_add(
            self._debounce_timeout, self._update_brightness_callback
        )
        return False

    def _update_brightness_callback(self):
        if self._pending_value is not None:
            value_to_set = self._pending_value
            self._pending_value = None
            if value_to_set != self.client.screen_brightness:
                self.client.screen_brightness = value_to_set
            self._update_source_id = None
            return False
        else:
            self._update_source_id = None
            return False

    def on_brightness_changed(self, client, _):
        self._updating_from_brightness = True
        self.set_value(self.client.screen_brightness)
        self._updating_from_brightness = False
        percentage = int((self.client.screen_brightness / self.client.max_screen) * 100)
        self.set_tooltip_text(f"{percentage}%")

    def destroy(self):
        if self._update_source_id is not None:
            GLib.source_remove(self._update_source_id)
        super().destroy()


class BrightnessSmall(Box):
    def __init__(self, **kwargs):
        super().__init__(name="button-bar-brightness", **kwargs)
        self.brightness = Brightness.get_initial()
        if self.brightness.screen_brightness == -1:
            self.destroy()
            return

        self.progress_bar = CircularProgressBar(
            name="button-brightness",
            size=28,
            line_width=2,
            start_angle=150,
            end_angle=390,
        )
        self.brightness_label = Label(
            name="brightness-label", markup=icons.brightness_high
        )
        self.brightness_button = Button(child=self.brightness_label)
        self.event_box = EventBox(
            events=["scroll", "smooth-scroll"],
            child=Overlay(child=self.progress_bar, overlays=self.brightness_button),
        )
        self.event_box.connect("scroll-event", self.on_scroll)
        self.add(self.event_box)
        self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)

        self._updating_from_brightness = False
        self._pending_value = None
        self._update_source_id = None
        self._debounce_timeout = 100

        # Don't connect to progress_bar value changes - only use scroll events
        self.brightness.connect("screen", self.on_brightness_changed)
        self.on_brightness_changed()

    def on_scroll(self, widget, event):
        if self.brightness.max_screen == -1:
            return

        step_size = 5
        current_brightness = self.brightness.screen_brightness

        if event.delta_y < 0:
            new_brightness = min(
                current_brightness + step_size, self.brightness.max_screen
            )
        elif event.delta_y > 0:
            new_brightness = max(current_brightness - step_size, 0)
        else:
            return

        # Directly update brightness, don't touch progress_bar
        self._pending_value = new_brightness
        if self._update_source_id is not None:
            GLib.source_remove(self._update_source_id)
        self._update_source_id = GLib.timeout_add(
            self._debounce_timeout, self._update_brightness_callback
        )

    def _update_brightness_callback(self):
        if (
            self._pending_value is not None
            and self._pending_value != self.brightness.screen_brightness
        ):
            self.brightness.screen_brightness = self._pending_value
            self._pending_value = None
        self._update_source_id = None
        return False

    def on_brightness_changed(self, *args):
        if self.brightness.max_screen == -1:
            return
        normalized = (
            self.brightness.screen_brightness / self.brightness.max_screen
            if self.brightness.max_screen > 0
            else 0
        )
        self._updating_from_brightness = True
        self.progress_bar.value = normalized
        self._updating_from_brightness = False

        brightness_percentage = int(normalized * 100)
        if brightness_percentage >= 75:
            self.brightness_label.set_markup(icons.brightness_high)
        elif brightness_percentage >= 24:
            self.brightness_label.set_markup(icons.brightness_medium)
        else:
            self.brightness_label.set_markup(icons.brightness_low)
        self.set_tooltip_text(f"{brightness_percentage}%")

    def destroy(self):
        if self._update_source_id is not None:
            GLib.source_remove(self._update_source_id)
        super().destroy()


class VolumeSmall(Box):
    def __init__(self, **kwargs):
        super().__init__(name="button-bar-vol", **kwargs)
        self.audio = Audio()
        self.progress_bar = CircularProgressBar(
            name="button-volume",
            size=28,
            line_width=2,
            start_angle=150,
            end_angle=390,
        )
        self.vol_label = Label(name="vol-label", markup=icons.vol_high)
        self.vol_button = Button(on_clicked=self.toggle_mute, child=self.vol_label)
        self.event_box = EventBox(
            events=["scroll", "smooth-scroll"],
            child=Overlay(child=self.progress_bar, overlays=self.vol_button),
        )
        self.audio.connect("notify::speaker", self.on_new_speaker)
        if self.audio.speaker:
            self.audio.speaker.connect("changed", self.on_speaker_changed)
        self.event_box.connect("scroll-event", self.on_scroll)
        self.add(self.event_box)
        self.on_speaker_changed()
        self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)

    def on_new_speaker(self, *args):
        if self.audio.speaker:
            self.audio.speaker.connect("changed", self.on_speaker_changed)
            self.on_speaker_changed()

    def toggle_mute(self, event):
        current_stream = self.audio.speaker
        if current_stream:
            current_stream.muted = not current_stream.muted
            if current_stream.muted:
                self.on_speaker_changed()
                self.progress_bar.add_style_class("muted")
                self.vol_label.add_style_class("muted")
            else:
                self.on_speaker_changed()
                self.progress_bar.remove_style_class("muted")
                self.vol_label.remove_style_class("muted")

    def on_scroll(self, _, event):
        if not self.audio.speaker:
            return
        if event.direction == Gdk.ScrollDirection.SMOOTH:
            if abs(event.delta_y) > 0:
                self.audio.speaker.volume -= event.delta_y
            if abs(event.delta_x) > 0:
                self.audio.speaker.volume += event.delta_x

    def on_speaker_changed(self, *_):
        if not self.audio.speaker:
            return

        vol_high_icon = icons.vol_high
        vol_medium_icon = icons.vol_medium
        vol_mute_icon = icons.vol_off
        vol_off_icon = icons.vol_mute

        if "bluetooth" in self.audio.speaker.icon_name:
            vol_high_icon = icons.bluetooth_connected
            vol_medium_icon = icons.bluetooth
            vol_mute_icon = icons.bluetooth_off
            vol_off_icon = icons.bluetooth_disconnected

        self.progress_bar.value = self.audio.speaker.volume / 100

        if self.audio.speaker.muted:
            self.vol_button.get_child().set_markup(vol_mute_icon)
            self.progress_bar.add_style_class("muted")
            self.vol_label.add_style_class("muted")
            self.set_tooltip_text("Muted")
            return
        else:
            self.progress_bar.remove_style_class("muted")
            self.vol_label.remove_style_class("muted")
        self.set_tooltip_text(f"{round(self.audio.speaker.volume)}%")
        if self.audio.speaker.volume > 74:
            self.vol_button.get_child().set_markup(vol_high_icon)
        elif self.audio.speaker.volume > 0:
            self.vol_button.get_child().set_markup(vol_medium_icon)
        else:
            self.vol_button.get_child().set_markup(vol_off_icon)


class MicSmall(Box):
    def __init__(self, **kwargs):
        super().__init__(name="button-bar-mic", **kwargs)
        self.audio = Audio()
        self.progress_bar = CircularProgressBar(
            name="button-mic",
            size=28,
            line_width=2,
            start_angle=150,
            end_angle=390,
        )
        self.mic_label = Label(name="mic-label", markup=icons.mic)
        self.mic_button = Button(on_clicked=self.toggle_mute, child=self.mic_label)
        self.event_box = EventBox(
            events=["scroll", "smooth-scroll"],
            child=Overlay(child=self.progress_bar, overlays=self.mic_button),
        )
        self.audio.connect("notify::microphone", self.on_new_microphone)
        if self.audio.microphone:
            self.audio.microphone.connect("changed", self.on_microphone_changed)
        self.event_box.connect("scroll-event", self.on_scroll)
        self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)
        self.add(self.event_box)
        self.on_microphone_changed()

    def on_new_microphone(self, *args):
        if self.audio.microphone:
            self.audio.microphone.connect("changed", self.on_microphone_changed)
            self.on_microphone_changed()

    def toggle_mute(self, event):
        current_stream = self.audio.microphone
        if current_stream:
            current_stream.muted = not current_stream.muted
            if current_stream.muted:
                self.mic_button.get_child().set_markup(icons.mic_mute)
                self.progress_bar.add_style_class("muted")
                self.mic_label.add_style_class("muted")
            else:
                self.on_microphone_changed()
                self.progress_bar.remove_style_class("muted")
                self.mic_label.remove_style_class("muted")

    def on_scroll(self, _, event):
        if not self.audio.microphone:
            return
        if event.direction == Gdk.ScrollDirection.SMOOTH:
            if abs(event.delta_y) > 0:
                self.audio.microphone.volume -= event.delta_y
            if abs(event.delta_x) > 0:
                self.audio.microphone.volume += event.delta_x

    def on_microphone_changed(self, *_):
        if not self.audio.microphone:
            return
        if self.audio.microphone.muted:
            self.mic_button.get_child().set_markup(icons.mic_mute)
            self.progress_bar.add_style_class("muted")
            self.mic_label.add_style_class("muted")
            self.set_tooltip_text("Muted")
            return
        else:
            self.progress_bar.remove_style_class("muted")
            self.mic_label.remove_style_class("muted")
        self.progress_bar.value = self.audio.microphone.volume / 100
        self.set_tooltip_text(f"{round(self.audio.microphone.volume)}%")
        if self.audio.microphone.volume >= 1:
            self.mic_button.get_child().set_markup(icons.mic)
        else:
            self.mic_button.get_child().set_markup(icons.mic_mute)


class BrightnessIcon(Box):
    def __init__(self, **kwargs):
        super().__init__(name="brightness-icon", **kwargs)
        self.brightness = Brightness.get_initial()
        if self.brightness.screen_brightness == -1:
            self.destroy()
            return

        self.brightness_label = Label(
            name="brightness-label-dash",
            markup=icons.brightness_high,
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )
        self.brightness_button = Button(
            child=self.brightness_label,
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )

        self.event_box = EventBox(
            events=["scroll", "smooth-scroll"],
            child=self.brightness_button,
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )
        self.event_box.connect("scroll-event", self.on_scroll)
        self.add(self.event_box)

        self._pending_value = None
        self._update_source_id = None
        self._updating_from_brightness = False

        self.brightness.connect("screen", self.on_brightness_changed)
        self.on_brightness_changed()
        self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)

    def on_scroll(self, _, event):
        if self.brightness.max_screen == -1:
            return

        step_size = 5
        current_brightness = self.brightness.screen_brightness

        if event.direction == Gdk.ScrollDirection.SMOOTH:
            if event.delta_y < 0:
                new_brightness = min(
                    current_brightness + step_size, self.brightness.max_screen
                )
            elif event.delta_y > 0:
                new_brightness = max(current_brightness - step_size, 0)
            else:
                return
        else:
            if event.direction == Gdk.ScrollDirection.UP:
                new_brightness = min(
                    current_brightness + step_size, self.brightness.max_screen
                )
            elif event.direction == Gdk.ScrollDirection.DOWN:
                new_brightness = max(current_brightness - step_size, 0)
            else:
                return

        self._pending_value = new_brightness
        if self._update_source_id is None:
            self._update_source_id = GLib.timeout_add(
                100, self._update_brightness_callback
            )

    def _update_brightness_callback(self):
        if (
            self._pending_value is not None
            and self._pending_value != self.brightness.screen_brightness
        ):
            self.brightness.screen_brightness = self._pending_value
            self._pending_value = None
            return True
        else:
            self._update_source_id = None
            return False

    def on_brightness_changed(self, *args):
        if self.brightness.max_screen == -1:
            return

        self._updating_from_brightness = True
        normalized = self.brightness.screen_brightness / self.brightness.max_screen
        brightness_percentage = int(normalized * 100)

        if brightness_percentage >= 75:
            self.brightness_label.set_markup("󰃠")
        elif brightness_percentage >= 24:
            self.brightness_label.set_markup("󰃠")
        else:
            self.brightness_label.set_markup("󰃠")
        self.set_tooltip_text(f"{brightness_percentage}%")
        self._updating_from_brightness = False

    def destroy(self):
        if self._update_source_id is not None:
            GLib.source_remove(self._update_source_id)
        super().destroy()


class VolumeIcon(Box):
    def __init__(self, **kwargs):
        super().__init__(name="vol-icon", **kwargs)
        self.audio = Audio()

        self.vol_label = Label(
            name="vol-label-dash",
            markup="",
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )
        self.vol_button = Button(
            on_clicked=self.toggle_mute,
            child=self.vol_label,
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )

        self.event_box = EventBox(
            events=["scroll", "smooth-scroll"],
            child=self.vol_button,
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )
        self.event_box.connect("scroll-event", self.on_scroll)
        self.add(self.event_box)

        self._pending_value = None
        self._update_source_id = None
        self._periodic_update_source_id = None

        self.audio.connect("notify::speaker", self.on_new_speaker)
        if self.audio.speaker:
            self.audio.speaker.connect("changed", self.on_speaker_changed)

        self._periodic_update_source_id = GLib.timeout_add_seconds(
            2, self.update_device_icon
        )
        self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)

    def on_scroll(self, _, event):
        if not self.audio.speaker:
            return

        step_size = 5
        current_volume = self.audio.speaker.volume

        if event.direction == Gdk.ScrollDirection.SMOOTH:
            if event.delta_y < 0:
                new_volume = min(current_volume + step_size, 100)
            elif event.delta_y > 0:
                new_volume = max(current_volume - step_size, 0)
            else:
                return
        else:
            if event.direction == Gdk.ScrollDirection.UP:
                new_volume = min(current_volume + step_size, 100)
            elif event.direction == Gdk.ScrollDirection.DOWN:
                new_volume = max(current_volume - step_size, 0)
            else:
                return

        self._pending_value = new_volume
        if self._update_source_id is None:
            self._update_source_id = GLib.timeout_add(100, self._update_volume_callback)

    def _update_volume_callback(self):
        if (
            self._pending_value is not None
            and self._pending_value != self.audio.speaker.volume
        ):
            self.audio.speaker.volume = self._pending_value
            self._pending_value = None
            return True
        else:
            self._update_source_id = None
            return False

    def on_new_speaker(self, *args):
        if self.audio.speaker:
            self.audio.speaker.connect("changed", self.on_speaker_changed)
            self.on_speaker_changed()

    def toggle_mute(self, event):
        current_stream = self.audio.speaker
        if current_stream:
            current_stream.muted = not current_stream.muted

            self.on_speaker_changed()

    def on_speaker_changed(self, *_):
        if not self.audio.speaker:
            self.vol_label.set_markup("")
            self.remove_style_class("muted")
            self.vol_label.remove_style_class("muted")
            self.vol_button.remove_style_class("muted")
            self.set_tooltip_text("No audio device")
            return

        if self.audio.speaker.muted:
            self.vol_label.set_markup(icons.headphones)
            self.add_style_class("muted")
            self.vol_label.add_style_class("muted")
            self.vol_button.add_style_class("muted")
            self.set_tooltip_text("Muted")
        else:
            self.remove_style_class("muted")
            self.vol_label.remove_style_class("muted")
            self.vol_button.remove_style_class("muted")

            self.update_device_icon()
            self.set_tooltip_text(f"{round(self.audio.speaker.volume)}%")

    def update_device_icon(self):
        if not self.audio.speaker:
            self.vol_label.set_markup("")

            return True

        if self.audio.speaker.muted:
            return True

        try:
            device_type = self.audio.speaker.port.type
            if device_type == "headphones":
                self.vol_label.set_markup(icons.headphones)
            elif device_type == "speaker":
                self.vol_label.set_markup(icons.headphones)
            else:
                self.vol_label.set_markup(icons.headphones)

        except AttributeError:
            self.vol_label.set_markup(icons.headphones)

        return True

    def destroy(self):
        if self._update_source_id is not None:
            GLib.source_remove(self._update_source_id)

        if (
            hasattr(self, "_periodic_update_source_id")
            and self._periodic_update_source_id is not None
        ):
            GLib.source_remove(self._periodic_update_source_id)
        super().destroy()


class MicIcon(Box):
    def __init__(self, **kwargs):
        super().__init__(name="mic-icon", **kwargs)
        self.audio = Audio()

        self.mic_label = Label(
            name="mic-label-dash",
            markup=icons.mic,
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )
        self.mic_button = Button(
            on_clicked=self.toggle_mute,
            child=self.mic_label,
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )

        self.event_box = EventBox(
            events=["scroll", "smooth-scroll"],
            child=self.mic_button,
            h_align="center",
            v_align="center",
            h_expand=True,
            v_expand=True,
        )
        self.event_box.connect("scroll-event", self.on_scroll)
        self.add(self.event_box)

        self._pending_value = None
        self._update_source_id = None

        self.audio.connect("notify::microphone", self.on_new_microphone)
        if self.audio.microphone:
            self.audio.microphone.connect("changed", self.on_microphone_changed)
        self.on_microphone_changed()
        self.add_events(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)

    def on_scroll(self, _, event):
        if not self.audio.microphone:
            return

        step_size = 5
        current_volume = self.audio.microphone.volume

        if event.direction == Gdk.ScrollDirection.SMOOTH:
            if event.delta_y < 0:
                new_volume = min(current_volume + step_size, 100)
            elif event.delta_y > 0:
                new_volume = max(current_volume - step_size, 0)
            else:
                return
        else:
            if event.direction == Gdk.ScrollDirection.UP:
                new_volume = min(current_volume + step_size, 100)
            elif event.direction == Gdk.ScrollDirection.DOWN:
                new_volume = max(current_volume - step_size, 0)
            else:
                return

        self._pending_value = new_volume
        if self._update_source_id is None:
            self._update_source_id = GLib.timeout_add(100, self._update_volume_callback)

    def _update_volume_callback(self):
        if (
            self._pending_value is not None
            and self._pending_value != self.audio.microphone.volume
        ):
            self.audio.microphone.volume = self._pending_value
            self._pending_value = None
            return True
        else:
            self._update_source_id = None
            return False

    def on_new_microphone(self, *args):
        if self.audio.microphone:
            self.audio.microphone.connect("changed", self.on_microphone_changed)
            self.on_microphone_changed()

    def toggle_mute(self, event):
        current_stream = self.audio.microphone
        if current_stream:
            current_stream.muted = not current_stream.muted
            if current_stream.muted:
                self.mic_button.get_child().set_markup("")
                self.mic_label.add_style_class("muted")
                self.mic_button.add_style_class("muted")
            else:
                self.on_microphone_changed()
                self.mic_label.remove_style_class("muted")
                self.mic_button.remove_style_class("muted")

    def on_microphone_changed(self, *_):
        if not self.audio.microphone:
            return
        if self.audio.microphone.muted:
            self.mic_button.get_child().set_markup("")
            self.add_style_class("muted")
            self.mic_label.add_style_class("muted")
            self.set_tooltip_text("Muted")
            return
        else:
            self.remove_style_class("muted")
            self.mic_label.remove_style_class("muted")

        self.set_tooltip_text(f"{round(self.audio.microphone.volume)}%")
        if self.audio.microphone.volume >= 1:
            self.mic_button.get_child().set_markup("")
        else:
            self.mic_button.get_child().set_markup("")

    def destroy(self):
        if self._update_source_id is not None:
            GLib.source_remove(self._update_source_id)
        super().destroy()


class ControlSliders(Box):
    def __init__(self, **kwargs):
        super().__init__(
            name="control-sliders",
            orientation="h",
            spacing=8,
            **kwargs,
        )

        brightness = Brightness.get_initial()

        if brightness.screen_brightness != -1:
            brightness_row = Box(
                orientation="h", spacing=0, h_expand=True, h_align="fill"
            )
            brightness_row.add(BrightnessIcon())
            brightness_row.add(BrightnessSlider())
            self.add(brightness_row)

        volume_row = Box(orientation="h", spacing=0, h_expand=True, h_align="fill")
        volume_row.add(VolumeIcon())
        volume_row.add(VolumeSlider())
        self.add(volume_row)

        mic_row = Box(orientation="h", spacing=0, h_expand=True, h_align="fill")
        mic_row.add(MicIcon())
        mic_row.add(MicSlider())
        self.add(mic_row)

        self.show_all()


class ControlSmall(Box):
    def __init__(self, **kwargs):
        brightness = Brightness.get_initial()
        children = []
        if brightness.screen_brightness != -1:
            children.append(BrightnessSmall())
        children.extend([VolumeSmall(), MicSmall()])
        super().__init__(
            name="control-small",
            orientation="h" if not data.VERTICAL else "v",
            spacing=4,
            children=children,
            **kwargs,
        )
        self.show_all()
