// tilerd.c : tiler daemon, c version 
// gcc -O2 -Wall -std=c11 -Wl,-z,norelro -fstrict-aliasing -flto -ffunction-sections -fdata-sections -fno-asynchronous-unwind-tables -fno-unwind-tables -fomit-frame-pointer -fvisibility=hidden -fmerge-all-constants -fuse-ld=gold -Wl,--gc-sections,--build-id=none,--as-needed,--strip-all,--compress-debug-sections=zlib -s -o tilerd tilerd.c -lX11 && strip --strip-all ./tilerd && sstrip ./tilerd

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#define FIFO_PATH "/.tiler.sock"

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

static Display *dpy = NULL;
static Window root;
static int sw, sh, shw, shh;

static Window get_active_window() {
    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(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(Window w, int x, int y, int ww, int hh) {
if (!w) return;
XMoveResizeWindow(dpy, w, x, y, ww, hh);
XFlush(dpy);
}

static void handle_action(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();
    if (!win) return;
    Geo g = {0};
    if (!get_geometry(win, &g)) return;
    int x, y, w, h;
    compute_dims(&g, action, &x, &y, &w, &h);
    move_resize(win, x, y, w, h);
}

int main(void) {
    char fifo_path[512];
    snprintf(fifo_path, sizeof(fifo_path), "%s%s", getenv("HOME"), FIFO_PATH);
    unlink(fifo_path);
    if (mkfifo(fifo_path, 0666) < 0) {
        perror("mkfifo");
        return 1;
    }
    dpy = XOpenDisplay(NULL);
    if (!dpy) {
        fprintf(stderr, "no X display\n");
        return 1;
    }
    int scr = DefaultScreen(dpy);
    root = RootWindow(dpy, scr);
    sw = DisplayWidth(dpy, scr); sh = DisplayHeight(dpy, scr);
    shw = sw / 2; shh = sh / 2;
    for (;;) {
        int fd = open(fifo_path, O_RDONLY);
        if (fd < 0) { perror("open fifo"); break; }
        fd_set rfds;
        FD_ZERO(&rfds); FD_SET(fd, &rfds);
        struct timeval tv = {1, 0};
        int rv = select(fd+1, &rfds, NULL, NULL, &tv);
        if (rv > 0 && FD_ISSET(fd, &rfds)) {
            char buf[128];
            ssize_t n = read(fd, buf, sizeof(buf)-1);
            if (n > 0) {
                buf[n] = '\0';
                char *action = strtok(buf, "\n\r");
                if (action) handle_action(action);
            }
        }
        close(fd);
    }
    XCloseDisplay(dpy);
    return 0;
}
