import os
import sys
import importlib
from itertools import accumulate, chain
from pathlib import Path
import ruamel.yaml as yaml

os.environ["GI_TYPELIB_PATH"] = "/usr/local/lib/x86_64-linux-gnu/girepository-1.0"

from ctypes import CDLL
CDLL('libgtk4-layer-shell.so')

import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Gtk4LayerShell", "1.0")

from gi.repository import Gtk, GLib, Gtk4LayerShell, Gdk, Gio

sys.path.insert(0, str((Path(__file__).parent / "shared").resolve()))

import panorama_panel


def get_applet_directories():
    data_home = Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local" / "share"))
    data_dirs = [Path(d) for d in os.getenv("XDG_DATA_DIRS", "/usr/local/share:/usr/share").split(":")]

    all_paths = [data_home / "panorama-panel" / "applets"] + [d / "panorama-panel" / "applets" for d in data_dirs]
    return [d for d in all_paths if d.is_dir()]


def get_config_file():
    config_home = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))

    return config_home / "panorama-panel" / "config.yaml"


panels = []


class ManagerWindow(Gtk.Window):
    def __init__(self):
        super().__init__()
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.set_child(box)
        switch_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.edit_mode_switch = Gtk.Switch()
        switch_box.append(self.edit_mode_switch)
        edit_mode_label = Gtk.Label()
        edit_mode_label.set_text("Panel editing")
        edit_mode_label.set_mnemonic_widget(self.edit_mode_switch)
        switch_box.append(edit_mode_label)
        box.append(switch_box)

        self.edit_mode_switch.connect("state-set", self.set_edit_mode)

        self.set_title("Panel configuration")

        self.connect("close-request", self.on_destroy)

    def on_destroy(self, widget):
        global manager_window
        self.destroy()
        manager_window = None

    def set_edit_mode(self, switch, value):
        print(f"Editing is {value}")

        if value:
            for panel in panels:
                panel.set_edit_mode(True)
        else:
            for panel in panels:
                panel.set_edit_mode(False)


manager_window = None


class AppletArea(Gtk.Box):
    def __init__(self, orientation=Gtk.Orientation.HORIZONTAL):
        super().__init__()

    def set_edit_mode(self, value):
        child = self.get_first_child()
        while child is not None:
            child.set_sensitive(not value)
            child.set_opacity(0.75 if value else 1)
            child = child.get_next_sibling()


class Panel(Gtk.Window):
    def __init__(self, monitor: Gdk.Monitor, position: Gtk.PositionType = Gtk.PositionType.TOP, size: int = 40):
        super().__init__()
        self.set_default_size(800, size)
        self.set_decorated(False)

        Gtk4LayerShell.init_for_window(self)

        Gtk4LayerShell.set_layer(self, Gtk4LayerShell.Layer.TOP)

        Gtk4LayerShell.auto_exclusive_zone_enable(self)
        Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.TOP, True)
        Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.LEFT, True)
        Gtk4LayerShell.set_anchor(self, Gtk4LayerShell.Edge.RIGHT, True)

        box = Gtk.CenterBox()

        match position:
            case Gtk.PositionType.TOP | Gtk.PositionType.BOTTOM:
                box.set_orientation(Gtk.Orientation.HORIZONTAL)
            case Gtk.PositionType.LEFT | Gtk.PositionType.RIGHT:
                box.set_orientation(Gtk.Orientation.VERTICAL)

        self.set_child(box)

        self.left_area = AppletArea(orientation=box.get_orientation())
        self.centre_area = AppletArea(orientation=box.get_orientation())
        self.right_area = AppletArea(orientation=box.get_orientation())

        box.set_start_widget(self.left_area)
        box.set_center_widget(self.centre_area)
        box.set_end_widget(self.right_area)

        # Add a context menu
        menu = Gio.Menu()

        menu.append("Open _manager", "panel.manager")

        self.context_menu = Gtk.PopoverMenu.new_from_model(menu)
        self.context_menu.set_has_arrow(False)
        self.context_menu.set_parent(self)
        self.context_menu.set_halign(Gtk.Align.START)
        self.context_menu.set_flags(Gtk.PopoverMenuFlags.NESTED)

        right_click_controller = Gtk.GestureClick()
        right_click_controller.set_button(3)
        right_click_controller.connect("pressed", self.show_context_menu)

        self.add_controller(right_click_controller)

        action_group = Gio.SimpleActionGroup()
        manager_action = Gio.SimpleAction.new("manager", None)
        manager_action.connect("activate", self.show_manager)
        action_group.add_action(manager_action)
        self.insert_action_group("panel", action_group)

    def set_edit_mode(self, value):
        for area in (self.left_area, self.centre_area, self.right_area):
            area.set_edit_mode(value)

    def show_context_menu(self, gesture, n_presses, x, y):
        rect = Gdk.Rectangle()
        rect.x = int(x)
        rect.y = int(y)
        rect.width = 1
        rect.height = 1

        self.context_menu.set_pointing_to(rect)
        self.context_menu.popup()

    def show_manager(self, _0=None, _1=None):
        print("Showing manager")
        global manager_window
        if not manager_window:
            manager_window = ManagerWindow()
        manager_window.present()

    def get_orientation(self):
        box = self.get_first_child()
        return box.get_orientation()


display = Gdk.Display.get_default()
monitors = display.get_monitors()

for i, monitor in enumerate(monitors):
    geometry = monitor.get_geometry()
    print(f"Monitor {i}: {geometry.width}x{geometry.height} at {geometry.x},{geometry.y}")


def get_all_subclasses(klass: type) -> list[type]:
    subclasses = []
    for subclass in klass.__subclasses__():
        subclasses.append(subclass)
        subclasses += get_all_subclasses(subclass)

    return subclasses


def load_packages_from_dir(dir_path: Path):
    loaded_modules = []

    for path in dir_path.iterdir():
        if path.name.startswith("_"):
            continue

        if path.is_dir() and (path / "__init__.py").exists():
            module_name = path.name
            spec = importlib.util.spec_from_file_location(module_name, path / "__init__.py")
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)
            loaded_modules.append(module)
        else:
            continue

    return loaded_modules


all_applets = list(chain.from_iterable(load_packages_from_dir(d) for d in get_applet_directories()))


print("Applets:")
subclasses = get_all_subclasses(panorama_panel.Applet)
applets_by_name = {}
for subclass in subclasses:
    if subclass.__name__ in applets_by_name:
        print(f"Name conflict for applet {subclass.__name__}. Only one will be loaded.", file=sys.stderr)
    applets_by_name[subclass.__name__] = subclass


PANEL_POSITIONS = {
    "top": Gtk.PositionType.TOP,
    "bottom": Gtk.PositionType.BOTTOM,
    "left": Gtk.PositionType.LEFT,
    "right": Gtk.PositionType.RIGHT,
}


with open(get_config_file(), "r") as config_file:
    yaml_loader = yaml.YAML(typ="rt")
    yaml_file = yaml_loader.load(config_file)
    for panel_data in yaml_file["panels"]:
        position = PANEL_POSITIONS[panel_data["position"]]
        monitor_index = panel_data["monitor"]
        monitor = monitors[monitor_index]
        size = panel_data["size"]

        panel = Panel(monitor, position, size)
        panels.append(panel)
        panel.show()

        print(f"{size}px panel on {position} edge of monitor {monitor_index}")

        for area_name, area in (("left", panel.left_area), ("centre", panel.centre_area), ("right", panel.right_area)):
            applet_list = panel_data["applets"].get(area_name)
            if applet_list is None:
                continue

            for applet in applet_list:
                item = list(applet.items())[0]
                AppletClass = applets_by_name[item[0]]
                options = item[1]
                applet_widget = AppletClass(orientation=panel.get_orientation(), config=options)

                area.append(applet_widget)


loop = GLib.MainLoop()
loop.run()
