/** @file frontend_sdl.c
  X11 frontend for Licar.
*/

#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include <unistd.h> // for usleep

#define LCR_SETTING_LOG_LEVEL 2
#define LCR_SETTING_RESOLUTION_X 320
#define LCR_SETTING_RESOLUTION_Y 240
#define LCR_SETTING_MUSIC 0

#define DATA_FILE_NAME "data"

#include "game.h"

char framebuffer[LCR_SETTING_RESOLUTION_X * LCR_SETTING_RESOLUTION_Y * 4];
FILE *dataFile = 0;
uint8_t buttonStates[8];

void LCR_drawPixel(unsigned long index, uint16_t color)
{
  char *p = framebuffer + 4 * index;

  *p = (color << 3) & 0xff;
  p++;
  *p = (color >> 3) & 0xfc;
  p++;
  *p = (color >> 8) & 0xf8;
}

char LCR_getNextDataFileChar(void)
{
  if (!dataFile)
    return 0;

  int c = fgetc(dataFile);

  if (c == EOF)
  {
    rewind(dataFile);
    return 0;
  }

  return c;
}

void LCR_appendDataStr(const char *str)
{
  if (!dataFile)
    return;

  if (str == 0 || *str == 0)
    rewind(dataFile);
  else
  {
    fclose(dataFile);
    
    dataFile = fopen(DATA_FILE_NAME,"a");
    
    if (dataFile)
    {
      fprintf(dataFile,"%s",str);
      fclose(dataFile);
      dataFile = fopen(DATA_FILE_NAME,"r");
    }
  }
}

uint8_t LCR_keyPressed(uint8_t key)
{
  return buttonStates[key % 8];
}

void LCR_sleep(uint16_t timeMs)
{
  usleep(timeMs * 1000);
}

void LCR_log(const char *str)
{
  printf("LOG: %s\n",str);
}

int main(int argc, char **argv)
{
  int running = 1;
  struct timeval now;

  LCR_gameInit(argc,(const char **) argv);

  Display *display = XOpenDisplay(0);
  int screen = DefaultScreen(display);
  
  dataFile = fopen(DATA_FILE_NAME,"r");

  if (!dataFile)
    LCR_log("couldn't open data file");

  Window window = XCreateSimpleWindow(display,RootWindow(display,screen),10,10,
    LCR_SETTING_RESOLUTION_X,LCR_SETTING_RESOLUTION_Y,1,
    BlackPixel(display,screen),WhitePixel(display,screen));

  XSizeHints *sizeHints = XAllocSizeHints();
  sizeHints->flags = PMaxSize | PMinSize;
  sizeHints->max_width = LCR_SETTING_RESOLUTION_X;
  sizeHints->min_width = LCR_SETTING_RESOLUTION_X;
  sizeHints->max_height = LCR_SETTING_RESOLUTION_Y;
  sizeHints->min_height = LCR_SETTING_RESOLUTION_Y;
  XSetWMNormalHints(display,window,sizeHints);
  XFree(sizeHints);

  XMapWindow(display,window);

  XSelectInput(display,window,KeyPressMask | KeyReleaseMask);

  GC context = DefaultGC(display,screen);
          
  XStoreName(display,window,"Licar");

  /*
    Hardcoded constants here may perhaps cause trouble on some platforms, but
    doing X11 "the right way" would mean 1 billion lines of code, so fuck it.
  */
  XImage *image = XCreateImage(display,DefaultVisual(display,screen),
    /*DefaultDepth(display,screen)*/24,ZPixmap,0,framebuffer,
    LCR_SETTING_RESOLUTION_X,LCR_SETTING_RESOLUTION_Y,8,0);
 
  while (running) // main loop
  {
    XEvent event;

    gettimeofday(&now,NULL);
    running &= LCR_gameStep(now.tv_sec * 1000 + now.tv_usec / 1000);

    XPutImage(display,window,context,image,0,0,0,0,LCR_SETTING_RESOLUTION_X,
      LCR_SETTING_RESOLUTION_Y);

    while (XCheckWindowEvent(display,window,KeyPressMask |
      KeyReleaseMask,&event) != False)
    {
      uint8_t state = event.xkey.type == KeyPress;

      switch (XKeycodeToKeysym(display,event.xkey.keycode,0))
      {
        case XK_Up: case XK_w:
          buttonStates[LCR_KEY_UP % 8] = state; break;

        case XK_Left: case XK_a:
          buttonStates[LCR_KEY_LEFT % 8] = state; break;
          
        case XK_Right: case XK_d:
          buttonStates[LCR_KEY_RIGHT % 8] = state; break;

        case XK_Down: case XK_s:
          buttonStates[LCR_KEY_DOWN % 8] = state; break;

        case XK_j: case XK_Return:
          buttonStates[LCR_KEY_A % 8] = state; break;
        
        case XK_k: case XK_Escape:
          buttonStates[LCR_KEY_B % 8] = state; break;

        default: break;
      }
    }
  }

  XDestroyImage(image);
  XCloseDisplay(display); 

  if (dataFile)
    fclose(dataFile);

  LCR_gameEnd();

  return 0;
}
