/*
 * Copyright (c) 2019 Derek Stevens, 2005 Russ Cox, 1994-1996 David Hogan
 * see README for license details
 */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/extensions/shape.h>
#include "config.h"
#include "dat.h"
#include "fns.h"
#include "patchlevel.h"

enum { GrabAltTab, GrabAltAny };

/*static int tabcode = 0x17; */
/*static int altcode = 0x40; */
/*static int pgupcode = 0x63; */
/*static int pgdowncode = 0x69; */

static void alttab(int shift);

void keysetup(void) {
  int i;
  int tabcode = XKeysymToKeycode(dpy, XK_Tab);
#ifdef DESTROY_KEY
  int dcode = XKeysymToKeycode(dpy, DESTROY_KEY);
#endif
#ifdef ICON_KEY
  int icode = XKeysymToKeycode(dpy, ICON_KEY);
#endif
#ifdef UNHIDE_KEY
  int ucode = XKeysymToKeycode(dpy, UNHIDE_KEY);
#endif
#ifdef MAX_KEY
  int mcode = XKeysymToKeycode(dpy, MAX_KEY);
#endif
#ifdef MOVE_KEY
  int vcode = XKeysymToKeycode(dpy, MOVE_KEY);
#endif
#ifdef RESIZE_KEY
  int rcode = XKeysymToKeycode(dpy, RESIZE_KEY);
#endif
#ifdef STICK_KEY
  int scode = XKeysymToKeycode(dpy, STICK_KEY);
#endif
#ifdef SNAPLEFT_KEY
  int hcode = XKeysymToKeycode(dpy, SNAPLEFT_KEY);
#endif
#ifdef SNAPRIGHT_KEY
  int lcode = XKeysymToKeycode(dpy, SNAPRIGHT_KEY);
#endif
#ifdef SNAPBOTTOM_KEY
  int jcode = XKeysymToKeycode(dpy, SNAPBOTTOM_KEY);
#endif
#ifdef SNAPTOP_KEY
  int kcode = XKeysymToKeycode(dpy, SNAPTOP_KEY);
#endif
#ifdef SNAPTOPLEFT_KEY
  int qcode = XKeysymToKeycode(dpy, SNAPTOPLEFT_KEY);
#endif
#ifdef SNAPBOTTOMLEFT_KEY
  int wcode = XKeysymToKeycode(dpy, SNAPBOTTOMLEFT_KEY);
#endif
#ifdef SNAPBOTTOMRIGHT_KEY
  int ocode = XKeysymToKeycode(dpy, SNAPBOTTOMRIGHT_KEY);
#endif
#ifdef SNAPTOPRIGHT_KEY
  int pcode = XKeysymToKeycode(dpy, SNAPTOPRIGHT_KEY);
#endif
#ifdef SNAPCENTER_KEY
  int ccode = XKeysymToKeycode(dpy, SNAPCENTER_KEY);
#endif
#ifdef LAUNCH_KEY
  int slcode = XKeysymToKeycode(dpy, LAUNCH_KEY);
#endif
#ifdef ZOOM_KEY
  int zcode = XKeysymToKeycode(dpy, ZOOM_KEY);
#endif
#ifdef NEXTVIRT_KEY
  int rightcode = XKeysymToKeycode(dpy, NEXTVIRT_KEY);
#endif
#ifdef PREVVIRT_KEY
  int leftcode = XKeysymToKeycode(dpy, PREVVIRT_KEY);
#endif
#ifdef DEVEL
  int tcode = XKeysymToKeycode(dpy, STICKYTOFRONT_KEY);
  int bcode = XKeysymToKeycode(dpy, STICKYTOBACK_KEY);
#endif

  for (i = 0; i < num_screens; i++) {
    XGrabKey(
      dpy,
      tabcode,
      Mod1Mask,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#ifdef DESTROY_KEY
    XGrabKey(
      dpy,
      dcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef ICON_KEY
    XGrabKey(
      dpy,
      icode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef UNHIDE_KEY
    XGrabKey(
      dpy,
      ucode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef RESIZE_KEY
    XGrabKey(
      dpy,
      rcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef MOVE_KEY
    XGrabKey(
      dpy,
      vcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef MAX_KEY
    XGrabKey(
      dpy,
      mcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef STICK_KEY
    XGrabKey(
      dpy,
      scode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPLEFT_KEY
    XGrabKey(
      dpy,
      hcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPRIGHT_KEY
    XGrabKey(
      dpy,
      lcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPBOTTOM_KEY
    XGrabKey(
      dpy,
      jcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPTOP_KEY
    XGrabKey(
      dpy,
      kcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPTOPLEFT_KEY
    XGrabKey(
      dpy,
      qcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPBOTTOMLEFT_KEY
    XGrabKey(
      dpy,
      wcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPBOTTOMRIGHT_KEY
    XGrabKey(
      dpy,
      ocode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPTOPRIGHT_KEY
    XGrabKey(
      dpy,
      pcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef SNAPCENTER_KEY
    XGrabKey(
      dpy,
      ccode,
      ShiftMask | SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
    XGrabKey(
      dpy,
      ccode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef PREVVIRT_KEY
    XGrabKey(
      dpy,
      leftcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef NEXTVIRT_KEY
    XGrabKey(
      dpy,
      rightcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef LAUNCH_KEY
    XGrabKey(
      dpy,
      slcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
#ifdef ZOOM_KEY
    XGrabKey(
      dpy,
      zcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
    XGrabKey(
      dpy,
      tabcode,
      Mod1Mask | ShiftMask,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#ifdef DEVEL
    XGrabKey(
      dpy,
      tcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
    XGrabKey(
      dpy,
      bcode,
      SHORTCUTMOD,
      screens[i].root,
      0,
      GrabModeSync,
      GrabModeAsync);
#endif
    /*	XGrabKey(dpy, pgupcode, Mod1Mask, screens[i].root, 0, GrabModeSync,
     * GrabModeAsync); */
    /*	XGrabKey(dpy, pgdowncode, Mod1Mask, screens[i].root, 0, GrabModeSync,
     * GrabModeAsync); */
    /*	XGrabKey(dpy, altcode, 0, screens[i].root, 0, GrabModeSync,
     * GrabModeAsync); */
  }
}

void keypress(XKeyEvent* e) {
  /*
   * process key press here
   */
  int tabcode = XKeysymToKeycode(dpy, XK_Tab);
#ifdef DESTROY_KEY
  int dcode = XKeysymToKeycode(dpy, DESTROY_KEY);
#endif
#ifdef ICON_KEY
  int icode = XKeysymToKeycode(dpy, ICON_KEY);
#endif
#ifdef UNHIDE_KEY
  int ucode = XKeysymToKeycode(dpy, UNHIDE_KEY);
#endif
#ifdef MAX_KEY
  int mcode = XKeysymToKeycode(dpy, MAX_KEY);
#endif
#ifdef MOVE_KEY
  int vcode = XKeysymToKeycode(dpy, MOVE_KEY);
#endif
#ifdef STICK_KEY
  int scode = XKeysymToKeycode(dpy, STICK_KEY);
#endif
#ifdef RESIZE_KEY
  int rcode = XKeysymToKeycode(dpy, RESIZE_KEY);
#endif
#ifdef LAUNCH_KEY
  int slcode = XKeysymToKeycode(dpy, LAUNCH_KEY);
#endif
#ifdef ZOOM_KEY
  int zcode = XKeysymToKeycode(dpy, ZOOM_KEY);
#endif
#ifdef SNAPLEFT_KEY
  int hcode = XKeysymToKeycode(dpy, SNAPLEFT_KEY);
#endif
#ifdef SNAPRIGHT_KEY
  int lcode = XKeysymToKeycode(dpy, SNAPRIGHT_KEY);
#endif
#ifdef SNAPBOTTOM_KEY
  int jcode = XKeysymToKeycode(dpy, SNAPBOTTOM_KEY);
#endif
#ifdef SNAPTOP_KEY
  int kcode = XKeysymToKeycode(dpy, SNAPTOP_KEY);
#endif
#ifdef SNAPTOPLEFT_KEY
  int qcode = XKeysymToKeycode(dpy, SNAPTOPLEFT_KEY);
#endif
#ifdef SNAPBOTTOMLEFT_KEY
  int wcode = XKeysymToKeycode(dpy, SNAPBOTTOMLEFT_KEY);
#endif
#ifdef SNAPBOTTOMRIGHT_KEY
  int ocode = XKeysymToKeycode(dpy, SNAPBOTTOMRIGHT_KEY);
#endif
#ifdef SNAPTOPRIGHT_KEY
  int pcode = XKeysymToKeycode(dpy, SNAPTOPRIGHT_KEY);
#endif
#ifdef SNAPCENTER_KEY
  int ccode = XKeysymToKeycode(dpy, SNAPCENTER_KEY);
#endif
#ifdef NEXTVIRT_KEY
  int rightcode = XKeysymToKeycode(dpy, NEXTVIRT_KEY);
#endif
#ifdef PREVVIRT_KEY
  int leftcode = XKeysymToKeycode(dpy, PREVVIRT_KEY);
#endif
#ifdef DEVEL
  int tcode = XKeysymToKeycode(dpy, STICKYTOFRONT_KEY);
  int bcode = XKeysymToKeycode(dpy, STICKYTOBACK_KEY);
#endif

  static XWindowAttributes ra;
  XGetWindowAttributes(dpy, DefaultRootWindow(dpy), &ra);

  /* basic wm functionality */
  if (e->keycode == tabcode && (e->state & Mod1Mask) == (1 << 3))
    alttab(e->state & ShiftMask);
#ifdef DESTROY_KEY
  else if (e->keycode == dcode && (e->state & SHORTCUTMOD) == (MODBITS))
    delete (current, 0);
#endif
#ifdef ICON_KEY
  else if (e->keycode == icode && (e->state & SHORTCUTMOD) == (MODBITS))
    hide(current);
#endif
#ifdef UNHIDE_KEY
  else if (e->keycode == ucode && (e->state & SHORTCUTMOD) == (MODBITS))
    unhide(0, 1);
#endif
#ifdef MOVE_KEY
  else if (e->keycode == vcode && (e->state & SHORTCUTMOD) == (MODBITS))
    move(current, Button3);
#endif
#ifdef RESIZE_KEY
  else if (e->keycode == rcode && (e->state & SHORTCUTMOD) == (MODBITS))
    reshape(current, Button3, sweep, 0);
#endif
#ifdef MAX_KEY
  else if (e->keycode == mcode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      -BORDER,
      -BORDER,
      ra.width + 2 * BORDER,
      ra.height + 2 * BORDER);
#endif
#ifdef STICK_KEY
  else if (e->keycode == scode && (e->state & SHORTCUTMOD) == (MODBITS))
    stick(current);
#endif
#ifdef ZOOM_KEY
  else if (e->keycode == zcode && (e->state & SHORTCUTMOD) == (MODBITS))
    zoom = !zoom;
#endif

  /* half snap */
#ifdef SNAPLEFT_KEY
  else if (e->keycode == hcode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      GAPSZ,
      GAPSZ,
      ra.width / 2 - 1.5 * GAPSZ,
      ra.height - 2 * GAPSZ);
#endif
#ifdef SNAPRIGHT_KEY
  else if (e->keycode == lcode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      ra.width / 2 + 0.5 * GAPSZ,
      GAPSZ,
      ra.width / 2 - 1.5 * GAPSZ,
      ra.height - 2 * GAPSZ);
#endif
#ifdef SNAPBOTTOM_KEY
  else if (e->keycode == jcode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      GAPSZ,
      ra.height / 2 + 0.5 * GAPSZ,
      ra.width - 2 * GAPSZ,
      ra.height / 2 - 1.5 * GAPSZ);
#endif
#ifdef SNAPTOP_KEY
  else if (e->keycode == kcode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      GAPSZ,
      GAPSZ,
      ra.width - 2 * GAPSZ,
      ra.height / 2 - 1.5 * GAPSZ);
#endif

  /* quarter snap */
#ifdef SNAPTOPLEFT_KEY
  else if (e->keycode == qcode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      GAPSZ,
      GAPSZ,
      ra.width / 2 - 1.5 * GAPSZ,
      ra.height / 2 - 1.5 * GAPSZ);
#endif
#ifdef SNAPBOTTOMLEFT_KEY
  else if (e->keycode == wcode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      GAPSZ,
      ra.height / 2 + 0.5 * GAPSZ,
      ra.width / 2 - 1.5 * GAPSZ,
      ra.height / 2 - 1.5 * GAPSZ);
#endif
#ifdef SNAPBOTTOMRIGHT_KEY
  else if (e->keycode == ocode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      ra.width / 2 + 0.5 * GAPSZ,
      ra.height / 2 + 0.5 * GAPSZ,
      ra.width / 2 - 1.5 * GAPSZ,
      ra.height / 2 - 1.5 * GAPSZ);
#endif
#ifdef SNAPTOPRIGHT_KEY
  else if (e->keycode == pcode && (e->state & SHORTCUTMOD) == (MODBITS))
    quickreshape(
      current,
      ra.width / 2 + 0.5 * GAPSZ,
      GAPSZ,
      ra.width / 2 - 1.5 * GAPSZ,
      ra.height / 2 - 1.5 * GAPSZ);
#endif

  /* center snap */
#ifdef CENTERSNAP_KEY
  else if (e->keycode == ccode && (e->state & SHORTCUTMOD) == (MODBITS)) {
    if ((e->state & ShiftMask) == (1 << 0)) {
#ifdef CENTERVMAX
      centerclient(current, ra, 0);
#else
      centerclient(current, ra, 1);
#endif
    } else {
#ifdef CENTERVMAX
      centerclient(current, ra, 1);
#else
      centerclient(current, ra, 0);
#endif
    }
  }
#endif

#ifdef DEVEL
  /* manage autostuck windows */
  if (e->keycode == tcode && (e->state & SHORTCUTMOD) == (MODBITS))
    stickystack(1);
  if (e->keycode == bcode && (e->state & SHORTCUTMOD) == (MODBITS))
    stickystack(0);
#endif

  /* launch */
#ifdef LAUNCH_KEY
  else if (e->keycode == slcode && (e->state & SHORTCUTMOD) == (MODBITS)) {
    kbLaunch = 1;
    if (fork() == 0) {
      close(ConnectionNumber(dpy));
      if (dpy != '\0')
        putenv(dpy);
      signal(SIGINT, SIG_DFL);
      signal(SIGTERM, SIG_DFL);
      signal(SIGHUP, SIG_DFL);
      if (termprog != NULL) {
        execl(shell, shell, "-c", termprog, (char*)0);
        fprintf(stderr, "ryudo: exec %s", shell);
        perror(" failed");
      }
      execlp("urxvt", "urxvt", (char*)0);
      execlp("9term", "9term", scrolling ? "-ws" : "-w", (char*)0);
      execlp("xterm", "xterm", "-ut", (char*)0);
      perror("ryudo: exec urxvt/9term/xterm failed");
    }
  }
#endif

  /* switch virts */
#ifdef NEXTVIRT_KEY
  else if (e->keycode == rightcode && (e->state & SHORTCUTMOD) == (MODBITS))
    if (numvirtuals > 1)
      if (virt < numvirtuals - 1)
        switch_to(virt + 1);
      else
        switch_to(0);
#endif

#ifdef PREVVIRT_KEY
  else if (e->keycode == leftcode && (e->state & SHORTCUTMOD) == (MODBITS))
    if (numvirtuals > 1)
      if (virt > 0)
        switch_to(virt - 1);
      else
        switch_to(numvirtuals - 1);
#endif

  XAllowEvents(dpy, SyncKeyboard, e->time);
}

void keyrelease(XKeyEvent* e) { XAllowEvents(dpy, SyncKeyboard, e->time); }

void quickreshape(Client* c, int x, int y, int dx, int dy) {
  if (c == 0)
    return;
  XMoveResizeWindow(dpy, c->parent, x, y, dx, dy);
  c->x = x + BORDER;
  c->y = y + BORDER;
  c->dx = dx - 2 * BORDER;
  c->dy = dy - 2 * BORDER;
  XMoveResizeWindow(dpy, c->window, BORDER, BORDER, c->dx, c->dy);
  sendconfig(c);
}

void centerclient(Client* c, XWindowAttributes ra, int vmax) {
  static int centeroffsetnum =
    CENTERNUM % 2 == 0 ? CENTERDEN - CENTERNUM : (CENTERDEN - CENTERNUM) / 2;

  static int centeroffsetden = CENTERNUM % 2 == 0 ? CENTERDEN * 2 : CENTERDEN;

  if (vmax) {
    quickreshape(
      c,
      centeroffsetnum * ra.width / centeroffsetden,
      GAPSZ,
      CENTERNUM * ra.width / CENTERDEN,
      ra.height - 2 * GAPSZ);
  } else {
    quickreshape(
      c,
      centeroffsetnum * ra.width / centeroffsetden,
      centeroffsetnum * ra.height / centeroffsetden,
      CENTERNUM * ra.width / CENTERDEN,
      CENTERNUM * ra.height / CENTERDEN);
  }
}

static void alttab(int shift) {
  shuffle(shift);
  /*	fprintf(stderr, "%sTab\n", shift ? "Back" : ""); */
}

#ifdef DEVEL
void stickystack(int toTop) {
  Client* c;
  if (toTop) {
    for (c = clients; c->next; c = c->next) {
      if (c && isautostick(c))
        top(c);
    }
  } else {
    for (c = clients; c->next; c = c->next) {
      if (c && !isautostick(c))
        top(c);
    }
  }
}
#endif
