#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "_ilb13/lib/imglib.h"

#define W 120
#define H 90
#define FPS 3

struct _controler {
  unsigned int x;
  unsigned int y;
  char ch;
};

struct _controler controlers[] = {
  { W+5,  4, 'p' },
  { W+5, 20, 'd' },
  { W+5, 36, 'n' },
  { W+5, 52, 'l' },
  { W+5, 68, 'w' },
};

enum _draw_mode {
  POINT,
  DRAW,
  LINE
};

struct _clic {
  int x;
  int y;
};

int
collide(int x1, int y1, int w, int h, int x, int y)
{
  if (x < x1) return 0;
  if (y < y1) return 0;
  if (x >= x1 + w) return 0;
  if (y >= y1 + h) return 0;
  return 1;
}

int main(void)
{
  Display *dpy = XOpenDisplay(NULL);
  if (!dpy) {
    fprintf(stderr, "XOpenDisplay failed\n");
    return 1;
  }

  int screen = DefaultScreen(dpy);
  Window win = XCreateSimpleWindow(
    dpy,
    RootWindow(dpy, screen),
    100, 100,
    W+20, H,
    1,
    BlackPixel(dpy, screen),
    WhitePixel(dpy, screen)
  );

  Atom wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  XSetWMProtocols(dpy, win, &wm_delete, 1);

  XSelectInput(dpy, win,
    ExposureMask |
    ClientMessage |
    ButtonPressMask |
    ButtonReleaseMask |
    PointerMotionMask |
    StructureNotifyMask);

  XStoreName(dpy, win, "120x90 BW");

  XSizeHints hints;
  hints.flags = PMinSize | PMaxSize;

  hints.min_width  = W+20;
  hints.min_height = H;
  hints.max_width  = W+20;
  hints.max_height = H;

  XSetWMNormalHints(dpy, win, &hints);

  XMapWindow(dpy, win);

  GC gc = DefaultGC(dpy, screen);

  int depth = DefaultDepth(dpy, screen);
  Visual *visual = DefaultVisual(dpy, screen);

  /* Buffer for XImage */
  char *xdata = calloc(W * H, 4);

  XImage *ximg = XCreateImage(
    dpy, visual, depth, ZPixmap, 0,
    xdata, W, H, 32, 0
  );

  unsigned long black = BlackPixel(dpy, screen);
  unsigned long white = WhitePixel(dpy, screen);

  struct _clic clic = { -1, -1 };
  struct _clic motion = { -1, -1 };
  struct _clic prev = { -1, -1 };
  int pressed = 0;
  int p = 0;
  unsigned char v = 1;

  unsigned int w = W;
  unsigned int h = H;
  struct _img *img = create_img(w, h);

  enum _draw_mode draw_mode = POINT;

  struct timespec ts = {0, 1000000000 / FPS};
  int running = 1;

  /* Main-loop */

  while (running) {
    /* Event's */
    XEvent ev;
    XNextEvent(dpy, &ev);

    switch (ev.type) {
      case Expose:
        //printf("redraw()\n");
        break;
      case ButtonPress:
        //printf("ButtonPress\n");
        clic.x = ev.xbutton.x;
        clic.y = ev.xbutton.y;
        pressed = 1;
        break;
      case ButtonRelease:
        //printf("ButtonRelease\n");
        pressed = 0;
        break;
      case MotionNotify:
        //printf(".m");
        //fflush(stdout);
        motion.x = ev.xmotion.x;
        motion.y = ev.xmotion.y;
        break;
      case KeyPress:
      { XKeyEvent *kev = &ev.xkey;
        KeySym keysym = XLookupKeysym(&ev.xkey, 0);
        if (keysym == XK_Escape || keysym == XK_q) {
          running = 0;
        }
      } break;
      case ClientMessage:
        if ((Atom)ev.xclient.data.l[0] == wm_delete) {
          running = 0;
          //printf("\n");
        }
        break;
    }

    switch (draw_mode) {
      case POINT:
        if (clic.x != -1 && clic.y != -1) {
          if (collide(0, 0, W, H, clic.x, clic.y)) {
            put_px(img, clic.x, clic.y, v);
            clic.x = -1;
            clic.y = -1;
          }
        }
        break;
      case DRAW:
        if (pressed == 1) {
          if (collide(0, 0, W, H, motion.x, motion.y)) {
            put_px(img, motion.x, motion.y, v);
          }
        }
        break;
      case LINE:
        switch (p) {
          case 0:
            if (clic.x != -1 && clic.y != -1) {
              if (collide(0, 0, W, H, clic.x, clic.y)) {
                prev.x = clic.x;
                prev.y = clic.y;
                clic.x = -1;
                clic.y = -1;
                p = 1;
              }
            }
            break;
          case 1:
            if (clic.x != -1 && clic.y != -1) {
              if (collide(0, 0, W, H, clic.x, clic.y)) {
                struct _pnt p1;
                struct _pnt p2;
                p1.x = prev.x;
                p1.y = prev.y;
                p2.x = clic.x;
                p2.y = clic.y;
                draw_line(img, &p1, &p2, v);
                clic.x = -1;
                clic.y = -1;
                p = 0;
              }
            }
            break;
        }
        break;
    }

    /* Create a XImage from img[] */
    for (int y = 0; y < H; y++) {
      for (int x = 0; x < W; x++) {
        unsigned long px =
            img->ds[y * W + x] ? black : white;
        XPutPixel(ximg, x, y, px);
        /*
         create_img(), initializes with 0.
         0, white
         1, black
        */
      }
    }

    /* Draw the XImage */
    XPutImage(dpy, win, DefaultGC(dpy, screen),
              ximg, 0, 0, 0, 0, W, H);

    XSetForeground(dpy, gc, black);
    XDrawLine(dpy, win, gc, W, 0, W, H);

    /* For each Controler */
    for (int d = 0; d < sizeof(controlers) / sizeof(struct _controler); d++) {
      struct _controler *ctr = &controlers[d];
      char ltr[2];
      ltr[0] = ctr->ch;
      ltr[1] = 0;
      XDrawRectangle(dpy, win, gc, ctr->x, ctr->y, 11, 11);
      XDrawString(dpy, win, gc, ctr->x + 3, ctr->y + 10, ltr, 1);
      if (collide(ctr->x, ctr->y, 11, 11, clic.x, clic.y)) {
        clic.x = -1;
        clic.y = -1;
        switch (ctr->ch) {
          case 'p':
            draw_mode = POINT;
            break;
          case 'd':
            draw_mode = DRAW;
            break;
          case 'a':
          case 'm':
          case 'n':
            XSetForeground(dpy, gc, white);
            XFillRectangle(dpy, win, gc, ctr->x, ctr->y, 11, 11);
            XSetForeground(dpy, gc, black);
            switch (ctr->ch) {
              case 'a':
                ctr->ch = 'm';
                v = 1;
                break;
              case 'm':
                ctr->ch = 'n';
                v = 0;
                break;
              case 'n':
                ctr->ch = 'a';
                v = 3;
                break;
            }
            break;
          case 'l':
            draw_mode = LINE;
            break;
          case 'w':
            print_img(img);
            break;
        }
      }
    }

    XFlush(dpy);
  }

  XDestroyImage(ximg);
  XDestroyWindow(dpy, win);
  XCloseDisplay(dpy);

  return 0;
}

