/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * Main entry point and event loop
 */

#include "dwn.h"
#include "config.h"
#include "atoms.h"
#include "client.h"
#include "workspace.h"
#include "layout.h"
#include "decorations.h"
#include "panel.h"
#include "keys.h"
#include "notifications.h"
#include "systray.h"
#include "news.h"
#include "applauncher.h"
#include "ai.h"
#include "autostart.h"
#include "services.h"
#include "api.h"
#include "demo.h"
#include "screenshot.h"
#include "ocr.h"
#include "util.h"
#include "slider.h"
#include "rules.h"
#include "marks.h"

/* New abstraction layer */
#include "core/wm_client.h"
#include "plugins/layout_plugin.h"
#include "plugins/builtin_layouts.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <math.h>
#include <sys/select.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include <X11/keysymdef.h>
#include <X11/XKBlib.h>
#include <X11/extensions/Xinerama.h>
#include <X11/extensions/XInput2.h>

/* Forward declarations for fade controls (defined in systray.c) */
void fade_controls_init(void);
void fade_controls_cleanup(void);

DWNState *dwn = NULL;
static DWNState dwn_state;

static volatile sig_atomic_t received_signal = 0;

static void signal_handler(int sig)
{
    received_signal = sig;
}

static void crash_signal_handler(int sig)
{
    log_flush();

    signal(sig, SIG_DFL);
    raise(sig);
}

static int last_x_error = 0;

static int x_error_handler(Display *dpy, XErrorEvent *ev)
{
    (void)dpy;

    last_x_error = ev->error_code;

    /* Async-signal-safe error logging using write() only.
     * Do NOT use stdio functions (fprintf, fflush, etc.) here as this
     * handler may be called from signal context. */
    char buf[512];
    int len;

    if (ev->error_code == BadWindow) {
        len = snprintf(buf, sizeof(buf),
                       "[X_ERROR] BadWindow (request %d, resource %lu)\n",
                       ev->request_code, ev->resourceid);
    } else if (ev->error_code == BadMatch || ev->error_code == BadValue ||
               ev->error_code == BadDrawable || ev->error_code == BadPixmap) {
        len = snprintf(buf, sizeof(buf),
                       "[X_ERROR] code=%d request=%d - continuing\n",
                       ev->error_code, ev->request_code);
    } else {
        len = snprintf(buf, sizeof(buf),
                       "[X_ERROR] code=%d request=%d resource=%lu\n",
                       ev->error_code, ev->request_code, ev->resourceid);
    }

    if (len > 0 && len < (int)sizeof(buf)) {
        ssize_t _wrote = write(STDERR_FILENO, buf, (size_t)len);
        (void)_wrote; /* Best effort in signal handler */
    }

    return 0;
}

static int x_io_error_handler(Display *dpy)
{
    (void)dpy;

    /* Async-signal-safe logging - write directly to stderr */
    const char msg[] = "[IO_ERROR] Fatal X11 I/O error - X server connection lost\n";
    ssize_t _wrote = write(STDERR_FILENO, msg, sizeof(msg) - 1);
    (void)_wrote; /* Best effort in signal handler */
    fprintf(stderr, "[IO_ERROR] Fatal X11 I/O error - X server connection lost\n");
    fflush(stderr);

    log_flush();
    LOG_ERROR("Fatal X11 I/O error - X server connection lost");
    log_flush();
    _exit(EXIT_FAILURE);
    return 0;
}

static int wm_detected = 0;

static int wm_detect_error_handler(Display *dpy, XErrorEvent *ev)
{
    (void)dpy;
    (void)ev;
    wm_detected = 1;
    return 0;
}

static bool check_other_wm(void)
{
    XSetErrorHandler(wm_detect_error_handler);

    XSelectInput(dwn->display, dwn->root,
                 SubstructureRedirectMask | SubstructureNotifyMask);
    XSync(dwn->display, False);

    XSetErrorHandler(x_error_handler);

    return wm_detected != 0;
}

static void init_xinput2(void)
{
    int event, error;
    if (!XQueryExtension(dwn->display, "XInputExtension",
                         &dwn->xi2_opcode, &event, &error)) {
        LOG_WARN("XInput2 extension not available - key counting disabled");
        dwn->xi2_opcode = 0;
        return;
    }

    int major = 2, minor = 0;
    if (XIQueryVersion(dwn->display, &major, &minor) != Success) {
        LOG_WARN("XInput2 version 2.0 not available - key counting disabled");
        dwn->xi2_opcode = 0;
        return;
    }

    XIEventMask evmask;
    unsigned char mask[XIMaskLen(XI_LASTEVENT)] = {0};

    evmask.deviceid = XIAllMasterDevices;
    evmask.mask_len = sizeof(mask);
    evmask.mask = mask;

    XISetMask(mask, XI_RawKeyPress);
    XISetMask(mask, XI_RawKeyRelease);
    XISetMask(mask, XI_RawMotion);

    XISelectEvents(dwn->display, dwn->root, &evmask, 1);

    LOG_INFO("XInput2 initialized for raw key and mouse event monitoring");
}

static void handle_xi2_event(XGenericEventCookie *cookie)
{
    if (cookie->type != GenericEvent || cookie->extension != dwn->xi2_opcode) {
        return;
    }

    if (!XGetEventData(dwn->display, cookie)) {
        return;
    }

    if (cookie->evtype == XI_RawKeyPress) {
        XIRawEvent *raw = (XIRawEvent *)cookie->data;
        KeyCode keycode = (KeyCode)raw->detail;
        KeySym keysym = XkbKeycodeToKeysym(dwn->display, keycode, 0, 0);

        if (keysym != XK_Super_L && keysym != XK_Super_R &&
            keysym != XK_Alt_L && keysym != XK_Alt_R &&
            keysym != XK_Control_L && keysym != XK_Control_R &&
            keysym != XK_Shift_L && keysym != XK_Shift_R &&
            keysym != XK_Caps_Lock && keysym != XK_Num_Lock &&
            keysym != XK_Scroll_Lock) {
            dwn->key_press_count++;
            dwn->typing_activity_time = get_time_ms();

            if (keysym == XK_BackSpace || keysym == XK_Delete) {
                dwn->key_delete_flicker_time = get_time_ms();
            }
        }
    } else if (cookie->evtype == XI_RawMotion) {
        XIRawEvent *raw = (XIRawEvent *)cookie->data;
        if (raw->valuators.mask_len > 0) {
            double dx = 0.0, dy = 0.0;
            double *values = raw->raw_values;
            int idx = 0;
            if (XIMaskIsSet(raw->valuators.mask, 0)) {
                dx = values[idx++];
            }
            if (XIMaskIsSet(raw->valuators.mask, 1)) {
                dy = values[idx];
            }
            dwn->mouse_distance_pixels += sqrt(dx * dx + dy * dy);
        }
    }

    XFreeEventData(dwn->display, cookie);
}

static void init_monitors(void)
{
    if (!XineramaIsActive(dwn->display)) {
        dwn->monitors[0].x = 0;
        dwn->monitors[0].y = 0;
        dwn->monitors[0].width = dwn->screen_width;
        dwn->monitors[0].height = dwn->screen_height;
        dwn->monitors[0].index = 0;
        dwn->monitors[0].primary = true;
        dwn->monitor_count = 1;
        return;
    }

    int num_screens;
    XineramaScreenInfo *info = XineramaQueryScreens(dwn->display, &num_screens);

    if (info == NULL || num_screens == 0) {
        dwn->monitors[0].x = 0;
        dwn->monitors[0].y = 0;
        dwn->monitors[0].width = dwn->screen_width;
        dwn->monitors[0].height = dwn->screen_height;
        dwn->monitors[0].index = 0;
        dwn->monitors[0].primary = true;
        dwn->monitor_count = 1;
        return;
    }

    dwn->monitor_count = (num_screens > MAX_MONITORS) ? MAX_MONITORS : num_screens;

    for (int i = 0; i < dwn->monitor_count; i++) {
        dwn->monitors[i].x = info[i].x_org;
        dwn->monitors[i].y = info[i].y_org;
        dwn->monitors[i].width = info[i].width;
        dwn->monitors[i].height = info[i].height;
        dwn->monitors[i].index = i;
        dwn->monitors[i].primary = (i == 0);
    }

    XFree(info);

    LOG_INFO("Detected %d monitor(s)", dwn->monitor_count);
}

static void scan_existing_windows(void)
{
    Window root_return, parent_return;
    Window *children;
    unsigned int num_children;

    if (XQueryTree(dwn->display, dwn->root, &root_return, &parent_return,
                   &children, &num_children)) {
        for (unsigned int i = 0; i < num_children; i++) {
            XWindowAttributes wa;
            if (XGetWindowAttributes(dwn->display, children[i], &wa)) {
                if (wa.map_state == IsViewable && wa.override_redirect == False) {
                    client_manage(children[i]);
                }
            }
        }
        if (children != NULL) {
            XFree(children);
        }
    }

    LOG_INFO("Scanned %d existing window(s)", client_count());
}


static void handle_map_request(XMapRequestEvent *ev)
{
    if (ev == NULL || dwn == NULL || dwn->display == NULL) {
        return;
    }

    if (ev->window == None) {
        LOG_WARN("handle_map_request: received invalid window (None)");
        return;
    }

    XWindowAttributes wa;
    if (!XGetWindowAttributes(dwn->display, ev->window, &wa)) {
        LOG_WARN("handle_map_request: XGetWindowAttributes failed for window %lu", ev->window);
        return;
    }

    if (wa.override_redirect) {
        return;
    }

    client_manage(ev->window);
}

static void handle_unmap_notify(XUnmapEvent *ev)
{
    if (ev == NULL || dwn == NULL) {
        return;
    }

    Client *c = client_find_by_window(ev->window);
    if (c == NULL) {
        return;
    }

    if (ev->send_event) {
        return;
    }

    if (c->workspace != (unsigned int)dwn->current_workspace) {
        return;
    }
    if (c->flags & CLIENT_MINIMIZED) {
        return;
    }

    if (c->flags & (CLIENT_MAXIMIZED | CLIENT_FULLSCREEN)) {
        return;
    }

    client_unmanage(c);
}

static void handle_destroy_notify(XDestroyWindowEvent *ev)
{
    if (ev == NULL) {
        return;
    }

    if (xembed_find_icon(ev->window) != NULL) {
        xembed_remove_icon(ev->window);
        return;
    }

    Client *c = client_find_by_window(ev->window);
    if (c != NULL) {
        client_unmanage(c);
    }
}

static void handle_configure_request(XConfigureRequestEvent *ev)
{
    if (ev == NULL || dwn == NULL || dwn->display == NULL) {
        return;
    }

    Client *c = client_find_by_window(ev->window);

    if (c != NULL) {
        if (client_is_floating(c) || client_is_fullscreen(c)) {
            if (ev->value_mask & CWX) c->x = ev->x;
            if (ev->value_mask & CWY) c->y = ev->y;
            if (ev->value_mask & CWWidth) c->width = ev->width;
            if (ev->value_mask & CWHeight) c->height = ev->height;
            client_configure(c);
        } else {
            client_configure(c);
        }
    } else {
        XWindowChanges wc;
        wc.x = ev->x;
        wc.y = ev->y;
        wc.width = ev->width;
        wc.height = ev->height;
        wc.border_width = 0;
        wc.sibling = ev->above;
        wc.stack_mode = ev->detail;

        XConfigureWindow(dwn->display, ev->window, ev->value_mask, &wc);
    }
}

static void handle_property_notify(XPropertyEvent *ev)
{
    if (ev == NULL || dwn == NULL) {
        return;
    }

    if (ev->atom == ewmh.XEMBED_INFO) {
        if (xembed_find_icon(ev->window) != NULL) {
            xembed_update_icon_state(ev->window);
            return;
        }
    }

    Client *c = client_find_by_window(ev->window);
    if (c == NULL) {
        return;
    }

    if (ev->atom == icccm.WM_NAME || ev->atom == ewmh.NET_WM_NAME) {
        client_update_title(c);
        panel_invalidate_taskbar();
    }
}

static void handle_expose(XExposeEvent *ev)
{
    if (ev == NULL || dwn == NULL) {
        return;
    }

    if (ev->count != 0) {
        return;
    }

    if (dwn->top_panel != NULL && ev->window == dwn->top_panel->window) {
        panel_render(dwn->top_panel);
        return;
    }
    if (dwn->bottom_panel != NULL && ev->window == dwn->bottom_panel->window) {
        panel_render(dwn->bottom_panel);
        return;
    }

    Notification *notif = notification_find_by_window(ev->window);
    if (notif != NULL) {
        notification_render(notif);
        return;
    }

    Client *c = client_find_by_frame(ev->window);
    if (c != NULL) {
        Workspace *ws = workspace_get(c->workspace);
        bool focused = (ws != NULL && ws->focused == c);
        decorations_render(c, focused);
    }
}

static void handle_enter_notify(XCrossingEvent *ev)
{
    if (ev == NULL || dwn == NULL || dwn->config == NULL) {
        return;
    }

    if (dwn->config->focus_mode != FOCUS_FOLLOW) {
        return;
    }

    Client *c = client_find_by_frame(ev->window);
    if (c == NULL) {
        c = client_find_by_window(ev->window);
    }

    if (c != NULL && c->workspace == (unsigned int)dwn->current_workspace) {
        int delay = dwn->config->focus_follow_delay_ms;
        if (delay <= 0) {
            client_focus(c, true);
        } else {
            dwn->pending_focus_client = c;
            dwn->pending_focus_time = get_time_ms() + delay;
        }
    }
}

static void handle_leave_notify(XCrossingEvent *ev)
{
    if (ev == NULL || dwn == NULL) {
        return;
    }

    Client *c = client_find_by_frame(ev->window);
    if (c == NULL) {
        c = client_find_by_window(ev->window);
    }

    if (c != NULL && dwn->pending_focus_client == c) {
        dwn->pending_focus_client = NULL;
    }
}

static void handle_button_press(XButtonEvent *ev)
{
    if (ev == NULL || dwn == NULL || dwn->display == NULL) {
        return;
    }

    if (volume_slider != NULL && volume_slider->visible) {
        if (ev->window == volume_slider->window) {
            volume_slider_handle_click(volume_slider, ev->x, ev->y);
            return;
        } else {
            volume_slider_hide(volume_slider);
        }
    }

    /* Handle fade control sliders */
    if (fade_speed_control.slider != NULL && fade_speed_control.slider->visible) {
        if (ev->window == fade_speed_control.slider->window) {
            LOG_DEBUG("Fade speed slider click: x=%d, y=%d", ev->x, ev->y);
            slider_handle_click(fade_speed_control.slider, ev->x, ev->y);
            return;
        } else {
            slider_hide(fade_speed_control.slider);
        }
    }
    
    if (fade_intensity_control.slider != NULL && fade_intensity_control.slider->visible) {
        if (ev->window == fade_intensity_control.slider->window) {
            LOG_DEBUG("Fade intensity slider click: x=%d, y=%d", ev->x, ev->y);
            slider_handle_click(fade_intensity_control.slider, ev->x, ev->y);
            return;
        } else {
            slider_hide(fade_intensity_control.slider);
        }
    }

    Notification *notif = notification_find_by_window(ev->window);
    if (notif != NULL) {
        notification_close(notif->id);
        return;
    }

    if (dwn->top_panel != NULL && ev->window == dwn->top_panel->window) {
        panel_handle_click(dwn->top_panel, ev->x, ev->y, ev->button);
        return;
    }
    if (dwn->bottom_panel != NULL && ev->window == dwn->bottom_panel->window) {
        panel_handle_click(dwn->bottom_panel, ev->x, ev->y, ev->button);
        return;
    }

    Client *c = client_find_by_frame(ev->window);
    bool is_client_window = false;
    if (c == NULL) {
        c = client_find_by_window(ev->window);
        is_client_window = (c != NULL);
    }

    if (c == NULL) {
        return;
    }

    if (is_client_window) {
        XAllowEvents(dwn->display, ReplayPointer, ev->time);
        client_focus(c, true);
        return;
    }

    client_focus(c, true);

    if (c->frame != None && ev->window == c->frame) {
        ButtonType btn = decorations_hit_test_button(c, ev->x, ev->y);
        if (btn != BUTTON_COUNT) {
            decorations_button_press(c, btn);
            return;
        }

        if (decorations_hit_test_title_bar(c, ev->x, ev->y)) {
            if (ev->button == 1) {
                dwn->drag_client = c;
                dwn->drag_start_x = ev->x_root;
                dwn->drag_start_y = ev->y_root;
                dwn->drag_orig_x = c->x;
                dwn->drag_orig_y = c->y;
                dwn->resizing = false;
                dwn->drag_direction = 0;

                api_emit_drag_started(c->window, false);

                XGrabPointer(dwn->display, c->frame, True,
                             PointerMotionMask | ButtonReleaseMask,
                             GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
            }
            return;
        }

        int direction;
        if (decorations_hit_test_resize_area(c, ev->x, ev->y, &direction)) {
            if (ev->button == 1) {
                dwn->drag_client = c;
                dwn->drag_start_x = ev->x_root;
                dwn->drag_start_y = ev->y_root;
                dwn->drag_orig_x = c->x;
                dwn->drag_orig_y = c->y;
                dwn->drag_orig_w = c->width;
                dwn->drag_orig_h = c->height;
                dwn->resizing = true;
                dwn->drag_direction = direction;

                api_emit_drag_started(c->window, true);

                XGrabPointer(dwn->display, c->frame, True,
                             PointerMotionMask | ButtonReleaseMask,
                             GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
            }
        }
    }

    api_emit_mouse_button_pressed(ev->button, ev->x_root, ev->y_root);
}

static void handle_button_release(XButtonEvent *ev)
{
    if (dwn == NULL || dwn->display == NULL) {
        return;
    }

    if (volume_slider != NULL && volume_slider->visible && volume_slider->dragging) {
        volume_slider_handle_release(volume_slider);
    }

    if (fade_speed_control.slider != NULL && fade_speed_control.slider->visible && fade_speed_control.slider->dragging) {
        slider_handle_release(fade_speed_control.slider);
    }
    
    if (fade_intensity_control.slider != NULL && fade_intensity_control.slider->visible && fade_intensity_control.slider->dragging) {
        slider_handle_release(fade_intensity_control.slider);
    }

    if (dwn->drag_client != NULL) {
        api_emit_drag_ended(dwn->drag_client->window, dwn->resizing);
        XUngrabPointer(dwn->display, CurrentTime);
        dwn->drag_client = NULL;
    }

    api_emit_mouse_button_released(ev->button, ev->x_root, ev->y_root);
}

static void handle_motion_notify(XMotionEvent *ev)
{
    if (ev == NULL || dwn == NULL || dwn->display == NULL) {
        return;
    }

    if (volume_slider != NULL && volume_slider->visible && ev->window == volume_slider->window) {
        volume_slider_handle_motion(volume_slider, ev->x, ev->y);
        return;
    }

    if (fade_speed_control.slider != NULL && fade_speed_control.slider->visible && ev->window == fade_speed_control.slider->window) {
        LOG_DEBUG("Fade speed slider motion: x=%d, y=%d", ev->x, ev->y);
        slider_handle_motion(fade_speed_control.slider, ev->x, ev->y);
        return;
    }
    
    if (fade_intensity_control.slider != NULL && fade_intensity_control.slider->visible && ev->window == fade_intensity_control.slider->window) {
        LOG_DEBUG("Fade intensity slider motion: x=%d, y=%d", ev->x, ev->y);
        slider_handle_motion(fade_intensity_control.slider, ev->x, ev->y);
        return;
    }

    if (dwn->drag_client == NULL) {
        return;
    }

    Client *c = dwn->drag_client;
    if (c == NULL) {
        dwn->drag_client = NULL;
        return;
    }

    int dx = ev->x_root - dwn->drag_start_x;
    int dy = ev->y_root - dwn->drag_start_y;

    if (dwn->resizing) {
        int dir = dwn->drag_direction;
        int new_x = dwn->drag_orig_x;
        int new_y = dwn->drag_orig_y;
        int new_w = dwn->drag_orig_w;
        int new_h = dwn->drag_orig_h;

        if (dir & RESIZE_LEFT) {
            new_x = dwn->drag_orig_x + dx;
            new_w = dwn->drag_orig_w - dx;
        } else if (dir & RESIZE_RIGHT) {
            new_w = dwn->drag_orig_w + dx;
        }

        if (dir & RESIZE_TOP) {
            new_y = dwn->drag_orig_y + dy;
            new_h = dwn->drag_orig_h - dy;
        } else if (dir & RESIZE_BOTTOM) {
            new_h = dwn->drag_orig_h + dy;
        }

        if (new_w < 50) {
            if (dir & RESIZE_LEFT) new_x = dwn->drag_orig_x + dwn->drag_orig_w - 50;
            new_w = 50;
        }
        if (new_h < 50) {
            if (dir & RESIZE_TOP) new_y = dwn->drag_orig_y + dwn->drag_orig_h - 50;
            new_h = 50;
        }

        client_move_resize(c, new_x, new_y, new_w, new_h);
    } else {
        client_move(c, dwn->drag_orig_x + dx, dwn->drag_orig_y + dy);
    }
}

static void handle_client_message(XClientMessageEvent *ev)
{
    if (ev == NULL || dwn == NULL || dwn->display == NULL) {
        return;
    }

    Client *c = client_find_by_window(ev->window);

    if (ev->message_type == ewmh.NET_ACTIVE_WINDOW) {
        if (c != NULL) {
            if (c->workspace != (unsigned int)dwn->current_workspace) {
                workspace_switch(c->workspace);
            }
            client_focus(c, true);
        }
    } else if (ev->message_type == ewmh.NET_CLOSE_WINDOW) {
        if (c != NULL) {
            client_close(c);
        }
    } else if (ev->message_type == ewmh.NET_WM_STATE) {
        if (c != NULL) {
            Atom action = ev->data.l[0];
            Atom prop1 = ev->data.l[1];
            Atom prop2 = ev->data.l[2];

            bool set = (action == 1);
            bool toggle = (action == 2);

            if (prop1 == ewmh.NET_WM_STATE_FULLSCREEN ||
                prop2 == ewmh.NET_WM_STATE_FULLSCREEN) {
                if (toggle) {
                    client_toggle_fullscreen(c);
                } else {
                    client_set_fullscreen(c, set);
                }
            }

            if (prop1 == ewmh.NET_WM_STATE_MAXIMIZED_VERT ||
                prop1 == ewmh.NET_WM_STATE_MAXIMIZED_HORZ ||
                prop2 == ewmh.NET_WM_STATE_MAXIMIZED_VERT ||
                prop2 == ewmh.NET_WM_STATE_MAXIMIZED_HORZ) {
                if (toggle) {
                    client_toggle_maximize(c);
                } else {
                    client_set_maximize(c, set);
                }
            }
        }
    } else if (ev->message_type == ewmh.NET_CURRENT_DESKTOP) {
        int desktop = ev->data.l[0];
        if (desktop >= 0 && desktop < MAX_WORKSPACES) {
            workspace_switch(desktop);
        }
    } else if (ev->message_type == ewmh.NET_SYSTEM_TRAY_OPCODE) {
        long opcode = ev->data.l[1];
        if (opcode == SYSTEM_TRAY_REQUEST_DOCK) {
            Window icon_win = ev->data.l[2];
            xembed_dock_icon(icon_win);
        }
    }
}

#if 0
static FILE *crash_log_file = NULL;

static void sync_log(const char *msg)
{
    if (crash_log_file == NULL) {
        crash_log_file = fopen("/tmp/dwn_crash.log", "a");
    }
    fprintf(stderr, "[SYNC] %s\n", msg);
    fflush(stderr);
    if (crash_log_file != NULL) {
        fprintf(crash_log_file, "[SYNC] %s\n", msg);
        fflush(crash_log_file);
        fsync(fileno(crash_log_file));
    }
}
#endif

void dwn_handle_event(XEvent *ev)
{
    if (ev == NULL) {
        LOG_WARN("dwn_handle_event: received NULL event");
        return;
    }

    if (dwn == NULL || dwn->display == NULL) {
        LOG_ERROR("dwn_handle_event: dwn or display is NULL");
        return;
    }

    switch (ev->type) {
        case MapRequest:
            handle_map_request(&ev->xmaprequest);
            break;
        case UnmapNotify:
            handle_unmap_notify(&ev->xunmap);
            break;
        case DestroyNotify:
            handle_destroy_notify(&ev->xdestroywindow);
            break;
        case ConfigureRequest:
            handle_configure_request(&ev->xconfigurerequest);
            break;
        case PropertyNotify:
            handle_property_notify(&ev->xproperty);
            break;
        case Expose:
            handle_expose(&ev->xexpose);
            break;
        case EnterNotify:
            handle_enter_notify(&ev->xcrossing);
            break;
        case LeaveNotify:
            handle_leave_notify(&ev->xcrossing);
            break;
        case ButtonPress:
            handle_button_press(&ev->xbutton);
            break;
        case ButtonRelease:
            handle_button_release(&ev->xbutton);
            break;
        case MotionNotify:
            handle_motion_notify(&ev->xmotion);
            break;
        case KeyPress:
            keys_handle_press(&ev->xkey);
            break;
        case KeyRelease:
            keys_handle_release(&ev->xkey);
            break;
        case ClientMessage:
            handle_client_message(&ev->xclient);
            break;
        default:
            break;
    }
}


int dwn_init(void)
{
    dwn = &dwn_state;
    memset(dwn, 0, sizeof(DWNState));

    dwn->display = XOpenDisplay(NULL);
    if (dwn->display == NULL) {
        fprintf(stderr, "Cannot open X display\n");
        return -1;
    }

    dwn->screen = DefaultScreen(dwn->display);
    dwn->root = RootWindow(dwn->display, dwn->screen);
    dwn->screen_width = DisplayWidth(dwn->display, dwn->screen);
    dwn->screen_height = DisplayHeight(dwn->display, dwn->screen);
    dwn->colormap = DefaultColormap(dwn->display, dwn->screen);

    XSetErrorHandler(x_error_handler);
    XSetIOErrorHandler(x_io_error_handler);

    if (check_other_wm()) {
        fprintf(stderr, "Another window manager is already running\n");
        XCloseDisplay(dwn->display);
        return -1;
    }

    log_init("~/.local/share/dwn/dwn.log");
    LOG_INFO("DWN %s starting", DWN_VERSION);

    dwn->config = config_create();
    config_load(dwn->config, NULL);

    extern void config_init_colors(Config *cfg);
    config_init_colors(dwn->config);

    extern int cli_port;
    if (cli_port > 0) {
        dwn->config->api_port = cli_port;
        LOG_INFO("API port overridden by CLI: %d", cli_port);
    }

    dwn->font = XLoadQueryFont(dwn->display, dwn->config->font_name);
    if (dwn->font == NULL) {
        dwn->font = XLoadQueryFont(dwn->display, "fixed");
    }

    dwn->xft_font = XftFontOpenName(dwn->display, dwn->screen,
                                     "monospace:size=10:antialias=true");
    if (dwn->xft_font == NULL) {
        dwn->xft_font = XftFontOpenName(dwn->display, dwn->screen,
                                         "fixed:size=10");
    }
    if (dwn->xft_font != NULL) {
        LOG_INFO("Loaded Xft font for UTF-8 support");
    }

    dwn->xft_font_bold = XftFontOpenName(dwn->display, dwn->screen,
                                          "monospace:size=10:bold:antialias=true");
    if (dwn->xft_font_bold == NULL) {
        dwn->xft_font_bold = dwn->xft_font;
    }

    XGCValues gcv;
    gcv.foreground = dwn->config->colors.panel_fg;
    gcv.background = dwn->config->colors.panel_bg;
    gcv.font = dwn->font ? dwn->font->fid : None;
    dwn->gc = XCreateGC(dwn->display, dwn->root,
                        GCForeground | GCBackground | (dwn->font ? GCFont : 0), &gcv);

    atoms_init(dwn->display);

    init_monitors();

    init_xinput2();

    workspace_init();

    /* Initialize new abstraction layer */
    wm_client_manager_init();
    wm_layout_manager_init();
    wm_layouts_builtin_init();
    LOG_INFO("Layout plugin system initialized");

    decorations_init();

    panels_init();

    systray_init();
    fade_controls_init();

    xembed_init();

    news_init();

    applauncher_init();

    autostart_init();
    autostart_run();

    services_init();
    services_run();

    keys_init();

    notifications_init();

    ai_init();

    screenshot_init();

    ocr_init();

    api_init();

    demo_init();

    rules_init();
    rules_load(NULL);

    marks_init();

    atoms_setup_ewmh();

    XSelectInput(dwn->display, dwn->root,
                 SubstructureRedirectMask | SubstructureNotifyMask |
                 StructureNotifyMask | PropertyChangeMask |
                 ButtonPressMask | PointerMotionMask);

    Cursor cursor = XCreateFontCursor(dwn->display, XC_left_ptr);
    XDefineCursor(dwn->display, dwn->root, cursor);

    scan_existing_windows();

    workspace_arrange_current();

    panel_render_all();

    dwn->running = true;

    dwn->ambient_phase = 0.0f;
    dwn->ambient_speed = 0.5f;
    dwn->typing_activity_time = 0;

    LOG_INFO("DWN initialized successfully");

    return 0;
}

void dwn_cleanup(void)
{
    LOG_INFO("DWN shutting down");

    /* Cleanup new abstraction layer */
    wm_layout_manager_shutdown();
    wm_client_manager_shutdown();

    marks_cleanup();
    rules_cleanup();
    demo_cleanup();
    api_cleanup();
    ocr_cleanup();
    screenshot_cleanup();
    ai_cleanup();
    notifications_cleanup();
    news_cleanup();
    applauncher_cleanup();
    services_cleanup();
    autostart_cleanup();
    keys_cleanup();
    xembed_cleanup();
    fade_controls_cleanup();
    systray_cleanup();
    panels_cleanup();
    decorations_cleanup();
    workspace_cleanup();

    while (dwn->client_list != NULL) {
        client_unmanage(dwn->client_list);
    }

    if (dwn->gc != None) {
        XFreeGC(dwn->display, dwn->gc);
    }
    if (dwn->font != NULL) {
        XFreeFont(dwn->display, dwn->font);
    }
    if (dwn->xft_font_bold != NULL && dwn->xft_font_bold != dwn->xft_font) {
        XftFontClose(dwn->display, dwn->xft_font_bold);
    }
    if (dwn->xft_font != NULL) {
        XftFontClose(dwn->display, dwn->xft_font);
    }
    if (dwn->config != NULL) {
        config_destroy(dwn->config);
    }

    if (dwn->display != NULL) {
        XCloseDisplay(dwn->display);
    }

    log_close();

    dwn = NULL;
}

void dwn_run(void)
{
    int x11_fd = ConnectionNumber(dwn->display);
    int dbus_fd = -1;

    if (dbus_conn != NULL) {
        dbus_connection_get_unix_fd(dbus_conn, &dbus_fd);
    }

    long last_clock_update = 0;
    long last_news_update = 0;

    while (dwn->running && received_signal == 0) {
        while (XPending(dwn->display)) {
            XEvent ev;
            XNextEvent(dwn->display, &ev);

            if (ev.type == GenericEvent && dwn->xi2_opcode > 0) {
                handle_xi2_event(&ev.xcookie);
            }

            dwn_handle_event(&ev);
        }

        notifications_process_messages();

        api_process();

        ai_process_pending();

        exa_process_pending();

        screenshot_process_pending();

        ocr_process_pending();

        demo_update();

        long now = get_time_ms();

        float speed_multiplier = 1.0f;
        if (now - dwn->typing_activity_time < 500) {
            speed_multiplier = 10.0f;
        }

        dwn->ambient_phase += 0.008f * dwn->ambient_speed * speed_multiplier;
        if (dwn->ambient_phase > 6.283f) {
            dwn->ambient_phase -= 6.283f;
        }

        client_update_animations();

        notifications_update();

        if (dwn->pending_focus_client != NULL) {
            if (now >= dwn->pending_focus_time) {
                Workspace *ws = workspace_get_current();
                if (ws != NULL && ws->focused != dwn->pending_focus_client) {
                    if (dwn->pending_focus_client->workspace == (unsigned int)dwn->current_workspace) {
                        client_focus(dwn->pending_focus_client, true);
                    }
                }
                dwn->pending_focus_client = NULL;
            }
        }

        bool news_rendered = false;

        if (now - last_news_update >= 50) {
            news_update();
            if (panel_news_needs_render()) {
                panel_render_news_only();
                news_rendered = true;
            }
            last_news_update = now;
        }

        if (panel_needs_render()) {
            panel_render_all();
        } else if (news_rendered) {
            XFlush(dwn->display);
        }

        if (now - last_clock_update >= 1000) {
            panel_update_clock();
            panel_update_system_stats();
            systray_update();
            last_clock_update = now;
        }

        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(x11_fd, &fds);
        if (dbus_fd >= 0) {
            FD_SET(dbus_fd, &fds);
        }

        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 50000;

        int max_fd = x11_fd;
        if (dbus_fd > max_fd) max_fd = dbus_fd;

        select(max_fd + 1, &fds, NULL, NULL, &tv);
    }

    if (received_signal != 0) {
        LOG_INFO("Received signal %d", received_signal);
    }
}

void dwn_quit(void)
{
    dwn->running = false;
}


static void print_usage(const char *program)
{
    printf("Usage: %s [OPTIONS]\n", program);
    printf("\n");
    printf("Options:\n");
    printf("  -h, --help        Show this help message\n");
    printf("  -v, --version     Show version information\n");
    printf("  -c, --config FILE Use specified config file\n");
    printf("  -p, --port PORT   Use specified API port (overrides config)\n");
    printf("\n");
    printf("Environment variables:\n");
    printf("  OPENROUTER_API_KEY  Enable AI features\n");
    printf("  EXA_API_KEY         Enable semantic search\n");
}

static void print_version(void)
{
    printf("DWN - Desktop Window Manager %s\n", DWN_VERSION);
    printf("AI-enhanced X11 window manager\n");
}

int cli_port = -1;
static char *cli_config = NULL;

int main(int argc, char *argv[])
{
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_usage(argv[0]);
            return 0;
        }
        if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
            print_version();
            return 0;
        }
        if ((strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) && i + 1 < argc) {
            cli_port = atoi(argv[++i]);
            if (cli_port <= 0 || cli_port > 65535) {
                fprintf(stderr, "Invalid port number: %s\n", argv[i]);
                return EXIT_FAILURE;
            }
        }
        if ((strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) && i + 1 < argc) {
            cli_config = argv[++i];
        }
    }

    XInitThreads();

    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);
    signal(SIGHUP, signal_handler);

    signal(SIGSEGV, crash_signal_handler);
    signal(SIGABRT, crash_signal_handler);
    signal(SIGFPE, crash_signal_handler);
    signal(SIGBUS, crash_signal_handler);
    signal(SIGILL, crash_signal_handler);

    if (dwn_init() != 0) {
        return EXIT_FAILURE;
    }

    dwn_run();

    dwn_cleanup();

    return EXIT_SUCCESS;
}
