/**
 * libagor-resize.so — Native GTK resize for undecorated WebKitGTK windows.
 *
 * KEY INSIGHT: Signals MUST be connected on the WebKitWebView widget,
 * NOT the GtkWindow. WebKit's GdkWindow receives all pointer events;
 * the parent GtkWindow never sees them.
 *
 * This is the Wails pattern: connect button-press-event on the WebView,
 * do hit-test in C, call gtk_window_begin_resize_drag with real event data.
 *
 * Build: gcc -shared -fPIC -o libagor-resize.so agor_resize.c \
 *        $(pkg-config --cflags --libs gtk+-3.0)
 */

#include <gtk/gtk.h>
#include <stdio.h>

static GtkWindow *stored_window = NULL;
static GtkWidget *stored_webview = NULL;
static int border_width = 8;
static gboolean resize_active = FALSE;

/**
 * Hit-test: determine which edge the pointer is on.
 * Returns GdkWindowEdge (0-7) or -1 if inside content area.
 * x, y are window-local coordinates (same as webview coords when it fills the window).
 */
static GdkWindowEdge edge_for_position(int win_w, int win_h, double x, double y)
{
    int B = border_width;
    int left   = x < B;
    int right  = x > win_w - B;
    int top    = y < B;
    int bottom = y > win_h - B;

    if (top    && left)  return GDK_WINDOW_EDGE_NORTH_WEST;
    if (top    && right) return GDK_WINDOW_EDGE_NORTH_EAST;
    if (bottom && left)  return GDK_WINDOW_EDGE_SOUTH_WEST;
    if (bottom && right) return GDK_WINDOW_EDGE_SOUTH_EAST;
    if (top)             return GDK_WINDOW_EDGE_NORTH;
    if (bottom)          return GDK_WINDOW_EDGE_SOUTH;
    if (left)            return GDK_WINDOW_EDGE_WEST;
    if (right)           return GDK_WINDOW_EDGE_EAST;
    return (GdkWindowEdge)-1;
}

/**
 * Button-press handler on the WebKitWebView.
 * If click is in the 8px border zone: start resize drag and consume the event.
 * If click is in the interior: return FALSE to let WebKit handle it.
 */
/**
 * Clear min-size on the entire widget tree so shrinking is allowed.
 * set_size_request(1,1) forces a 1x1 minimum, overriding preferred size.
 */
static void clear_min_size(GtkWidget *widget, int depth)
{
    if (!widget || depth > 10) return;
    gtk_widget_set_size_request(widget, 1, 1);
    if (GTK_IS_BIN(widget)) {
        GtkWidget *child = gtk_bin_get_child(GTK_BIN(widget));
        if (child) clear_min_size(child, depth + 1);
    }
    if (GTK_IS_CONTAINER(widget)) {
        GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
        for (GList *l = children; l; l = l->next)
            clear_min_size(GTK_WIDGET(l->data), depth + 1);
        g_list_free(children);
    }
}

static gboolean on_webview_button_press(GtkWidget *widget,
                                        GdkEventButton *event,
                                        gpointer user_data)
{
    if (!stored_window || !event) return FALSE;
    if (event->button != 1) return FALSE;  /* LMB only */

    int w, h;
    gtk_window_get_size(stored_window, &w, &h);

    GdkWindowEdge edge = edge_for_position(w, h, event->x, event->y);
    if ((int)edge == -1) return FALSE;  /* Interior — let WebKit handle */

    fprintf(stderr, "[agor-resize] RESIZE edge=%d btn=%d xy=(%.0f,%.0f) root=(%.0f,%.0f) t=%u size=%dx%d\n",
            edge, event->button, event->x, event->y, event->x_root, event->y_root, event->time, w, h);

    /* Clear min-size on entire tree BEFORE resize drag — allows shrinking */
    clear_min_size(GTK_WIDGET(stored_window), 0);

    /* Set geometry hints with small minimum */
    GdkGeometry geom = { .min_width = 400, .min_height = 300, .max_width = 32767, .max_height = 32767 };
    gtk_window_set_geometry_hints(stored_window, NULL, &geom, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);

    /* Disable WebView input to prevent it from stealing the WM's pointer grab.
     * The WebView's GdkWindow won't interfere with events during the resize. */
    if (stored_webview) {
        gtk_widget_set_sensitive(stored_webview, FALSE);
        resize_active = TRUE;
        fprintf(stderr, "[agor-resize] WebView DISABLED for resize\n");
    }

    gtk_window_begin_resize_drag(
        stored_window,
        edge,
        event->button,
        (gint)event->x_root,
        (gint)event->y_root,
        event->time
    );

    return TRUE;
}

/**
 * Motion handler on the WebKitWebView — changes cursor on edge hover.
 */
static gboolean on_webview_motion(GtkWidget *widget,
                                  GdkEventMotion *event,
                                  gpointer user_data)
{
    if (!stored_window || !event) return FALSE;

    int w, h;
    gtk_window_get_size(stored_window, &w, &h);
    GdkWindowEdge edge = edge_for_position(w, h, event->x, event->y);

    GdkWindow *gdk_win = gtk_widget_get_window(GTK_WIDGET(stored_window));
    if (!gdk_win) return FALSE;

    const char *cursor_name = NULL;
    switch ((int)edge) {
    case GDK_WINDOW_EDGE_NORTH:      cursor_name = "n-resize";  break;
    case GDK_WINDOW_EDGE_SOUTH:      cursor_name = "s-resize";  break;
    case GDK_WINDOW_EDGE_WEST:       cursor_name = "w-resize";  break;
    case GDK_WINDOW_EDGE_EAST:       cursor_name = "e-resize";  break;
    case GDK_WINDOW_EDGE_NORTH_WEST: cursor_name = "nw-resize"; break;
    case GDK_WINDOW_EDGE_NORTH_EAST: cursor_name = "ne-resize"; break;
    case GDK_WINDOW_EDGE_SOUTH_WEST: cursor_name = "sw-resize"; break;
    case GDK_WINDOW_EDGE_SOUTH_EAST: cursor_name = "se-resize"; break;
    default: break;
    }

    if (cursor_name) {
        GdkCursor *cursor = gdk_cursor_new_from_name(gdk_display_get_default(), cursor_name);
        gdk_window_set_cursor(gdk_win, cursor);
        if (cursor) g_object_unref(cursor);
    } else {
        gdk_window_set_cursor(gdk_win, NULL);  /* default cursor */
    }

    return FALSE;  /* Always let WebKit see motion events too */
}

static guint reenable_timer_id = 0;

/**
 * Timer callback: re-enable WebView after resize drag ends.
 * Runs 500ms after the last configure event.
 */
static gboolean reenable_webview(gpointer data)
{
    if (stored_webview && resize_active) {
        gtk_widget_set_sensitive(stored_webview, TRUE);
        resize_active = FALSE;
        fprintf(stderr, "[agor-resize] WebView RE-ENABLED (resize ended)\n");
    }
    reenable_timer_id = 0;
    return G_SOURCE_REMOVE;
}

/**
 * Configure-event handler — logs window size changes.
 * Resets the re-enable timer on each configure (resize still in progress).
 */
static gboolean on_configure(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
{
    if (resize_active) {
        /* CRITICAL: During active resize, re-clear min-size on EVERY configure.
         * GTK's layout cycle re-asserts the WebView's preferred size after each
         * WM resize step, sending a conflicting ConfigureRequest that cancels the drag. */
        clear_min_size(GTK_WIDGET(stored_window), 0);
        GdkGeometry geom = { .min_width = 400, .min_height = 300, .max_width = 32767, .max_height = 32767 };
        gtk_window_set_geometry_hints(stored_window, NULL, &geom, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);

        if (reenable_timer_id) g_source_remove(reenable_timer_id);
        reenable_timer_id = g_timeout_add(500, reenable_webview, NULL);
    }
    return FALSE;
}

/**
 * Initialize: find the deepest WebView child and connect signals there.
 * Call ONCE after window creation.
 */
void agor_resize_init(void *window_ptr, int border)
{
    if (!GTK_IS_WINDOW(window_ptr)) {
        fprintf(stderr, "[agor-resize] ERROR: not a GtkWindow: %p\n", window_ptr);
        return;
    }

    stored_window = GTK_WINDOW(window_ptr);
    border_width = border > 0 ? border : 8;

    /* Walk down the widget tree to find the deepest child (the WebKitWebView) */
    GtkWidget *webview = GTK_WIDGET(stored_window);
    while (GTK_IS_BIN(webview)) {
        GtkWidget *child = gtk_bin_get_child(GTK_BIN(webview));
        if (!child) break;
        webview = child;
    }

    stored_webview = webview;
    if (webview == GTK_WIDGET(stored_window)) {
        fprintf(stderr, "[agor-resize] WARNING: no child widget found, connecting on window\n");
    } else {
        fprintf(stderr, "[agor-resize] Found WebView widget: %p (type: %s)\n",
                (void *)webview, G_OBJECT_TYPE_NAME(webview));
    }

    /* CRITICAL: add event masks on the WebView — without this, GTK won't deliver events */
    gtk_widget_add_events(webview,
        GDK_BUTTON_PRESS_MASK |
        GDK_POINTER_MOTION_MASK);

    /* Connect signals on the WebView (NOT the GtkWindow!) */
    g_signal_connect(webview, "button-press-event",
                     G_CALLBACK(on_webview_button_press), NULL);
    g_signal_connect(webview, "motion-notify-event",
                     G_CALLBACK(on_webview_motion), NULL);
    /* Log every window size change */
    g_signal_connect(GTK_WIDGET(stored_window), "configure-event",
                     G_CALLBACK(on_configure), NULL);

    fprintf(stderr, "[agor-resize] Initialized on %s (border=%dpx)\n",
            G_OBJECT_TYPE_NAME(webview), border_width);
}

/**
 * Start resize from stored state (for RPC fallback — not needed if C handler works).
 */
int agor_resize_start(int edge)
{
    fprintf(stderr, "[agor-resize] agor_resize_start called with edge=%d (C handler should do this automatically)\n", edge);
    return 0;
}
