#include "xevent.h"

static int XEventError(Display *display, XErrorEvent *event) {
  char message[100];

  int error;
  error = XGetErrorText(display, event->error_code, message, sizeof(message));
  if (error) {
    goXEventError(event, "Could not get error message.");
  } else {
    goXEventError(event, message);
  }

  return 1;
}

void XEventLoop(char *name) {
  Display *display = XOpenDisplay(name);
  Window root = RootWindow(display, 0);

  int xfixes_event_base, xfixes_error_base;
  if (!XFixesQueryExtension(display, &xfixes_event_base, &xfixes_error_base)) {
    return;
  }

  Atom WM_WINDOW_ROLE = XInternAtom(display, "WM_WINDOW_ROLE", 1);
  Atom XA_CLIPBOARD = XInternAtom(display, "CLIPBOARD", 0);
  XFixesSelectSelectionInput(display, root, XA_CLIPBOARD, XFixesSetSelectionOwnerNotifyMask);
  XFixesSelectCursorInput(display, root, XFixesDisplayCursorNotifyMask);
  XSelectInput(display, root, SubstructureNotifyMask);

  XSync(display, 0);
  XSetErrorHandler(XEventError);

  while (goXEventActive()) {
    XEvent event;
    XNextEvent(display, &event);

    // XFixesDisplayCursorNotify
    if (event.type == xfixes_event_base + 1) {
      XFixesCursorNotifyEvent notifyEvent = *((XFixesCursorNotifyEvent *) &event);
      if (notifyEvent.subtype == XFixesDisplayCursorNotify) {
        goXEventCursorChanged(notifyEvent);
        continue;
      }
    }

    // XFixesSelectionNotifyEvent
    if (event.type == xfixes_event_base + XFixesSelectionNotify) {
      XFixesSelectionNotifyEvent notifyEvent = *((XFixesSelectionNotifyEvent *) &event);
      if (notifyEvent.subtype == XFixesSetSelectionOwnerNotify && notifyEvent.selection == XA_CLIPBOARD) {
        goXEventClipboardUpdated();
        continue;
      }
    }

    // ConfigureNotify
    if (event.type == ConfigureNotify) {
      Window window = event.xconfigure.window;

      char *name;
      XFetchName(display, window, &name);

      XTextProperty role;
      XGetTextProperty(display, window, &role, WM_WINDOW_ROLE);

      goXEventConfigureNotify(display, window, name, role.value);
      XFree(name);
      continue;
    }

    // UnmapNotify
    if (event.type == UnmapNotify) {
      Window window = event.xunmap.window;
      goXEventUnmapNotify(window);
      continue;
    }
  }

  XCloseDisplay(display);
}