#include "include/window_draw.h"
#include "include/window_manager.h"
#include "include/taskbar_draw.h"

#include "include/X_functions.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static inline void handle_shortcuts(XKeyEvent event) {
    if (event.keycode == XKeysymToKeycode(X_display, XK_Return) && event.state == Mod1Mask) {
        switch (fork()) {
            case 0:
                execl("/bin/xterm", "/bin/xterm", NULL);
        }
    }
    if (event.keycode == XKeysymToKeycode(X_display, XK_F4) && event.state == Mod1Mask) {
        XDestroyWindow(event.display, event.subwindow);
    }
}

int prev_x=0, prev_y=0;

void move(Window w, XMotionEvent *event) {
    linked_window * window = get_window_definite(w);
    if (window == NULL) {
        return; // invalid window
    }
    assert(window->frame != 0);
    XWindowAttributes attrs;
    XGetWindowAttributes(X_display, window->frame, &attrs);

    XMoveWindow(X_display, window->frame, attrs.x + (event->x_root-prev_x), attrs.y + (event->y_root-prev_y));
    XSync(X_display, 0);
    // need to send a synthetic ConfigureNotify via StructureNotify
    XConfigureEvent new_event = {
        .type=ConfigureNotify,
        .serial = event->serial,
        .send_event = True,
        .display = X_display,
        .event = window->client,
        .window = window->client,
        .x = attrs.x + (event->x_root-prev_x),
        .y = attrs.y + (event->y_root-prev_y),
        .width = attrs.width -18,
        .height = attrs.height - 4,
        .border_width = 0,
        .above = None,
        .override_redirect = False
    };
    XSendEvent(X_display, window->client, 0, StructureNotifyMask, (XEvent *)&new_event);

    prev_x = event->x_root, prev_y = event->y_root;
}

void scale(Window w, XMotionEvent *event) {
    linked_window * window = get_window_definite(w);
    if (window == NULL) {
        return; // invalid window
    }
    XWindowAttributes attrs;
    XGetWindowAttributes(X_display, window->frame, &attrs);
    
    int Xdelta, Ydelta;
    Xdelta = event->x_root - attrs.x - attrs.width - 18 + _ICON_SIZE/2;
    Ydelta = event->y_root - attrs.y - attrs.height - 2 + _ICON_SIZE/2;

    XResizeWindow(X_display, window->client, attrs.width + Xdelta, attrs.height+Ydelta);
    XConfigureEvent new_event = {
        .type=ConfigureNotify,
        .serial = event->serial,
        .send_event = True,
        .display = X_display,
        .event = window->client,
        .window = window->client,
        .x = attrs.x,
        .y = attrs.y,
        .width = attrs.width + Xdelta,
        .height = attrs.height+Ydelta,
        .border_width = 0,
        .above = None,
        .override_redirect = False
    };
    XSendEvent(X_display, window->client, 0, StructureNotifyMask, (XEvent *)&new_event);
    XResizeWindow(X_display, window->frame, attrs.width + Xdelta + 18, attrs.height + Ydelta + 2);
    draw_window_core(window->client);
}

void update_loop() {
    static Window grabbed, scaled;
    XEvent event;
    while (1) {
        XNextEvent(X_display, &event); // BLOCKING!
        switch (event.type) {
            case ButtonPress:
                //printf("Sub, main, root window %lu %lu %lu\n", event.xbutton.subwindow, event.xbutton.window, event.xbutton.root);

                XRaiseWindow(X_display, event.xbutton.window);

                XAllowEvents(X_display, event.xbutton.subwindow != 0? ReplayPointer:SyncPointer, event.xbutton.time);  
                XSync(X_display,0); 

                if (event.xbutton.subwindow == 0 && event.xbutton.y_root > XHeightOfScreen(XDefaultScreenOfDisplay(X_display))- TASKBAR_HEIGHT) {
                    restore_window(event.xbutton.window);
                } else if (event.xbutton.subwindow == 0) {
                    XWindowAttributes attrs;
                    XGetWindowAttributes(X_display, event.xbutton.window, &attrs);
                    if (event.xbutton.x > attrs.width-_RESIZE_X && event.xbutton.y > attrs.height - _RESIZE_Y) {
                        scaled = event.xbutton.window;
                        grabbed = 0;
                    } else if (event.xbutton.x > attrs.width - _CLOSE_X && event.xbutton.y > attrs.height - _CLOSE_Y){
                        linked_window * window = get_window_definite(event.xbutton.window);
                        XDestroyWindow(X_display, window->client);
                    } else if (event.xbutton.x > attrs.width - _ICON_SIZE && event.xbutton.y > attrs.height - _MINIMIZE_Y) {
                        linked_window * window = get_window_definite(event.xbutton.window);
                        minimize_window(*window);
                    } else {
                        grabbed = event.xbutton.window;
                        scaled = 0;
                    }
                } else {
                    grabbed = scaled = 0;
                }
                
                prev_x = event.xbutton.x_root, prev_y = event.xbutton.y_root;
                break;
            case ButtonRelease:
                XAllowEvents(X_display, event.xbutton.subwindow != 0? ReplayPointer:SyncPointer, event.xbutton.time);  
                XSync(X_display,0);
                grabbed = 0;
                scaled = 0;
                break;
            case MotionNotify:
                XAllowEvents(X_display, event.xmotion.subwindow != 0? ReplayPointer:SyncPointer, event.xbutton.time);  
                XSync(X_display,0);
                if (grabbed != 0) {
                    move(grabbed, &event.xmotion);
                } else if (scaled != 0) {
                    scale(scaled, &event.xmotion);
                }
                break;
            case FocusIn:
            case FocusOut:
                break;
            case Expose:
                draw_window(&event.xexpose);
                break;
            case UnmapNotify:
                on_unmap_request(&event.xunmap);
                break;
            case MapRequest:
                on_map_request(&event.xmaprequest);
                break;
            case ConfigureRequest:
                on_configure_request(&event.xconfigurerequest);
                break;
            case CreateNotify:
                on_create_notify(& event.xcreatewindow);
                break;
            case ReparentNotify:
                break;
            case KeyPress:
                if (event.xkey.keycode == XKeysymToKeycode(X_display,XK_Pause) && event.xkey.state == (ShiftMask | ControlMask | Mod1Mask)) {
                    printf("Killed using CTRL+SHIFT+ALT+PAUSE\n");
                    goto EXIT;
                }
                handle_shortcuts(event.xkey);
                break;
        }
    }
    EXIT:
    return;
}

int main() {
    init();
    update_loop();
    deinit();
    return EXIT_SUCCESS;
}