#ifndef TILER_H
#define TILER_H

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <string.h>
#include <stdio.h>

typedef struct {
    Window win;
    int w, h, x, y, aw, ah, ax, ay;
} Geo;

static Window root;
static int sw, sh, shw, shh;

static Window get_active_window(Display *dpy) {
    Atom net = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", True);
    if (net == None) return 0;
    Atom type;
    int format;
    unsigned long nitems, after;
    unsigned char *data = NULL;
    if (XGetWindowProperty(dpy, root, net, 0, 1, False, XA_WINDOW,
        &type, &format, &nitems, &after, &data) != Success || !data || nitems == 0)
        return 0;
    Window win = *(Window*)data;
    XFree(data);
    return win;
}

static int get_geometry(Display *dpy, Window win, Geo *g) {
    Window cur = win, parent, ret_root, *children = NULL;
    unsigned int nchildren;
    XWindowAttributes attr;
    int first = 1;
    if (!win) return 0;
    while (cur) {
        if (!XGetWindowAttributes(dpy, cur, &attr)) break;
        if (first) {
            g->win = win;
            g->w = attr.width; g->h = attr.height;
            first = 0;
        }
        g->ax = attr.x; g->ay = attr.y;
        g->aw = attr.width; g->ah = attr.height;
        if (!XQueryTree(dpy, cur, &ret_root, &parent, &children, &nchildren)) break;
        if (children) XFree(children);
        if (!parent || parent == ret_root) break;
        cur = parent;
    }
    g->x = g->ax; g->y = g->ay;
    return 1;
}

static void compute_dims(Geo *g, const char *action, int *x, int *y, int *w, int *h) {
    int ox = g->aw - g->w, oy = g->ah - g->h;
    int fw = sw - ox, fh = sh - oy, hw = shw - ox, hh = shh - oy;
    *x = *y = *w = *h = 0;
    if (strcmp(action, "center") == 0) {
        *w = sw * 3 / 4; *h = sh * 3 / 4;
        *x = (sw - *w - ox) / 2; *y = (sh - *h - oy) / 2;
    } else if (strcmp(action, "fullscreen") == 0) {
        *x = *y = 0; *w = fw; *h = fh;
    } else if (strcmp(action, "left") == 0 || strcmp(action, "right") == 0) {
        *x = (strcmp(action, "left") == 0) ? 0 : shw; *w = hw;
        if ((g->w <= hw && g->x == *x && (g->h >= hh || g->h == fh))) {
            *y = 0; *h = fh;
        } else {
            *y = (g->y < shh) ? 0 : shh; *h = hh;
        }
    } else if (strcmp(action, "up") == 0 || strcmp(action, "down") == 0) {
        *y = (strcmp(action, "up") == 0) ? 0 : shh; *h = hh;
        if ((g->h <= hh && g->y == *y && (g->w >= hw || g->w == fw))) {
            *x = 0; *w = fw;
        } else {
            *x = (g->x < shw) ? 0 : shw; *w = hw;
        }
    }
}

static void move_resize(Display *dpy, Window w, int x, int y, int ww, int hh) {
    if (!w) return;
    XMoveResizeWindow(dpy, w, x, y, ww, hh);
    XFlush(dpy);
}

static int is_window_fullscreen(Display *dpy, Window win) {
    Atom state = XInternAtom(dpy, "_NET_WM_STATE", False);
    Atom fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
    Atom type;
    int format;
    unsigned long nitems, after;
    unsigned char *data = NULL;
    if (XGetWindowProperty(dpy, win, state, 0, 1024, False, XA_ATOM, &type, &format, &nitems, &after, &data) != Success || !data)
        return 0;
    Atom *atoms = (Atom *)data;
    for (unsigned long i = 0; i < nitems; i++) {
        if (atoms[i] == fullscreen) {
            XFree(data);
            return 1;
        }
    }
    XFree(data);
    return 0;
}

int tiler_init(Display *dpy) {
    int scr = DefaultScreen(dpy);
    root = RootWindow(dpy, scr);
    sw = DisplayWidth(dpy, scr); sh = DisplayHeight(dpy, scr);
    shw = sw / 2; shh = sh / 2;
    return 1;
}

void handle_action(Display *dpy, const char *action) {
    if (strcmp(action, "center") && strcmp(action, "fullscreen") && strcmp(action, "left") && strcmp(action, "right") && strcmp(action, "up") && strcmp(action, "down"))
        return;
    Window win = get_active_window(dpy);
    if (!win) return;
    if (strcmp(action, "fullscreen") == 0) {
        XEvent xev = {0};
        xev.xclient.type = ClientMessage;
        xev.xclient.window = win;
        xev.xclient.message_type = XInternAtom(dpy, "_NET_WM_STATE", False);
        xev.xclient.format = 32;
        xev.xclient.data.l[1] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
        xev.xclient.data.l[2] = 0;
        int is_fs = is_window_fullscreen(dpy, win);
        xev.xclient.data.l[0] = is_fs ? 0 : 1;
        XSendEvent(dpy, root, False, SubstructureNotifyMask, &xev);
        XFlush(dpy);
        return;
    }
    Geo g = {0};
    if (!get_geometry(dpy, win, &g)) return;
    int x, y, w, h;
    compute_dims(&g, action, &x, &y, &w, &h);
    move_resize(dpy, win, x, y, w, h);
}

#endif
