import array
import os
import subprocess
import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Adw, Gtk, GObject, Gio, GLib, Gdk, GdkPixbuf

import stillControl.LayoutManager as LayoutManager
import stillControl.constants as constants
from stillControl.i18n import _

layout_dir = os.path.join(constants.MAIN_DIR, "layouts")
preview_dir = os.path.join(layout_dir, "previews")

buttons = []
_pending_layout_id = None  # Stores the layout ID that will be applied on logout


def refresh_buttons(layout_id):
    for button in buttons:
        button.layout_setting_changed(layout_id)
        button.change_layout = True


def _is_shift_held():
    """Check if the Shift key is currently held down."""
    display = Gdk.Display.get_default()
    if display:
        seat = display.get_default_seat()
        if seat:
            keyboard = seat.get_keyboard()
            if keyboard:
                # Get modifier state from the default seat
                modifiers = keyboard.get_modifier_state()
                return bool(modifiers & Gdk.ModifierType.SHIFT_MASK)
    return False


def _logout_session():
    """Trigger a GNOME session logout."""
    try:
        subprocess.Popen(["gnome-session-quit", "--logout", "--no-prompt"])
    except Exception as e:
        print(f"Failed to logout: {e}")
        # Fallback: try via D-Bus
        try:
            subprocess.Popen([
                "dbus-send", "--session", "--type=method_call",
                "--dest=org.gnome.SessionManager",
                "/org/gnome/SessionManager",
                "org.gnome.SessionManager.Logout",
                "uint32:1"
            ])
        except Exception as e2:
            print(f"Fallback logout also failed: {e2}")


def _show_logout_dialog(window, layout_id, layout_name):
    """Show the normal logout dialog."""
    dialog = Adw.AlertDialog.new(
        _("Apply '{layout_name}' Layout?").format(layout_name=layout_name),
        _("This layout will be applied after you log out. Some changes may not take effect until you log back in.")
    )
    dialog.add_response("cancel", _("Cancel"))
    dialog.add_response("logout", _("Log Out Now"))
    dialog.set_response_appearance("logout", Adw.ResponseAppearance.SUGGESTED)
    dialog.set_default_response("logout")
    dialog.set_close_response("cancel")
    
    def on_response(dialog, response):
        if response == "logout":
            # Apply the layout settings (they'll take effect on next login)
            LayoutManager.set_layout(layout_id)
            # Trigger logout
            _logout_session()
        else:
            # User cancelled - revert the checkbox to the current layout
            current_layout = LayoutManager.get_current_layout()
            refresh_buttons(current_layout)
    
    dialog.connect("response", on_response)
    dialog.present(window)


def _show_immediate_apply_dialog(window, layout_id, layout_name):
    """Show the hidden immediate apply warning dialog (Shift+click)."""
    dialog = Adw.AlertDialog.new(
        _("Apply Without Logout?"),
        _("You are about to apply the '{layout_name}' layout immediately without logging out.\n\n"
          "This may cause visual glitches, broken panel elements, or other unexpected behavior. "
          "If things break, logging out and back in should fix them.").format(layout_name=layout_name)
    )
    dialog.add_response("cancel", _("Cancel"))
    dialog.add_response("apply", _("Apply Anyway"))
    dialog.set_response_appearance("apply", Adw.ResponseAppearance.DESTRUCTIVE)
    dialog.set_default_response("cancel")
    dialog.set_close_response("cancel")
    
    def on_response(dialog, response):
        if response == "apply":
            # Apply immediately without logout
            LayoutManager.set_layout(layout_id)
            refresh_buttons(layout_id)
        else:
            # User cancelled - revert the checkbox to the current layout
            current_layout = LayoutManager.get_current_layout()
            refresh_buttons(current_layout)
    
    dialog.connect("response", on_response)
    dialog.present(window)


@Gtk.Template(filename=os.path.join(constants.UI_DIR, "LayoutButton.ui"))
class LayoutButton(Adw.Bin):
    __gtype_name__ = "LayoutButton"
    change_layout = False
    preview: Gtk.Picture = Gtk.Template.Child()
    label: Gtk.Label = Gtk.Template.Child()
    check: Gtk.CheckButton = Gtk.Template.Child()

    def __init__(self, layout_id, last_button: None):
        super().__init__()
        buttons.append(self)
        self.layout_name = LayoutManager.get_layout_name_from_id(layout_id)
        self.layout_id = layout_id
        self.svg_path = os.path.join(preview_dir, f"{layout_id}.svg")
        self.style_manager = Adw.StyleManager.get_default()

        self.style_manager.connect("notify::dark", lambda _style, _dark: self.set_image())

        if layout_id == "custom":
            self.set_sensitive(False)
            self.check.set_sensitive(False)

        self.set_image()
        self.label.set_label(self.layout_name)
        if last_button:
            self.check.set_group(last_button.check)

    def set_image(self):
        if os.path.exists(self.svg_path):
            pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.svg_path)
            # Invert colors for dark mode, otherwise keep it for light
            if self.style_manager.get_dark():
                pixbuf = invert_pixbuf(pixbuf)
            self.preview.set_paintable(Gdk.Texture.new_for_pixbuf(pixbuf))
        else:
            print(self.svg_path)
            #self.preview.set_from_icon_name("dialog-question-symbolic")
            #self.preview.set_pixel_size(72)

        self.check.connect("toggled", self.toggled)

    def _get_window(self):
        """Get the top-level window for showing dialogs."""
        widget = self
        while widget:
            if isinstance(widget, Gtk.Window):
                return widget
            widget = widget.get_parent()
        return None

    def toggled(self, _check_button):
        if self.change_layout and self.check.get_active() and self.layout_id != "custom":
            window = self._get_window()
            
            if _is_shift_held():
                # Hidden feature: Shift+click for immediate apply with warning
                _show_immediate_apply_dialog(window, self.layout_id, self.layout_name)
            else:
                # Normal behavior: show logout dialog
                _show_logout_dialog(window, self.layout_id, self.layout_name)

    def layout_setting_changed(self, layout_id):
        self.change_layout = False
        if layout_id == self.layout_id:
            self.check.set_active(True)


def invert_pixbuf(pixbuf):
    # Get the dimensions and number of channels
    width = pixbuf.get_width()
    height = pixbuf.get_height()
    channels = pixbuf.get_n_channels()

    # Get the pixel data as a Python array
    pixels = pixbuf.get_pixels()
    arr = array.array('B', pixels)

    # Invert the colors
    for i in range(0, len(arr), channels):
        arr[i] = 255 - arr[i]     # Red
        arr[i+1] = 255 - arr[i+1] # Green
        arr[i+2] = 255 - arr[i+2] # Blue
        # If there's an alpha channel, leave it unchanged

    # Create a new pixbuf with the inverted data
    inverted_pixbuf = GdkPixbuf.Pixbuf.new_from_data(
        arr.tobytes(),
        GdkPixbuf.Colorspace.RGB,
        pixbuf.get_has_alpha(),
        8,
        width,
        height,
        pixbuf.get_rowstride()
    )

    return inverted_pixbuf
