#include "guru_meditation.h"
#include <stdlib.h>
#include <signal.h>
#include <SDL/SDL.h>

#define FONTWIDTH 6
#define FONTHEIGHT 13
#if FONTWIDTH > 8
#error "Font width > 8"
#endif
static const unsigned char font_ripped_from_sge[] = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xdf,
  0xff, 0xff, 0xff, 0xff, 0xaf, 0xaf, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0xaf, 0x07, 0xaf, 0x07, 0xaf,
  0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x87, 0x5f, 0x5f, 0x8f, 0xd7,
  0xd7, 0x0f, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xb7, 0x57, 0xaf, 0xef, 0xdf,
  0xbf, 0xaf, 0x57, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x5f, 0x5f, 0xbf,
  0x5f, 0x67, 0x6f, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xdf, 0xbf,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xdf,
  0xdf, 0xbf, 0xbf, 0xbf, 0xdf, 0xdf, 0xef, 0xff, 0xff, 0xff, 0xff, 0xbf,
  0xdf, 0xdf, 0xef, 0xef, 0xef, 0xdf, 0xdf, 0xbf, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xdf, 0x57, 0x07, 0x8f, 0x07, 0x57, 0xdf, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xdf, 0xdf, 0x07, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xdf, 0xbf,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf,
  0x8f, 0xdf, 0xff, 0xff, 0xff, 0xf7, 0xf7, 0xef, 0xef, 0xdf, 0xbf, 0xbf,
  0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xaf, 0x77, 0x77, 0x77, 0x77,
  0x77, 0xaf, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x9f, 0x5f, 0xdf, 0xdf,
  0xdf, 0xdf, 0xdf, 0x07, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x77, 0x77, 0xf7,
  0xef, 0xdf, 0xbf, 0x7f, 0x07, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf7, 0xef,
  0xdf, 0x8f, 0xf7, 0xf7, 0x77, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xef, 0xef,
  0xcf, 0xaf, 0xaf, 0x6f, 0x07, 0xef, 0xef, 0xff, 0xff, 0xff, 0xff, 0x07,
  0x7f, 0x7f, 0x4f, 0x37, 0xf7, 0xf7, 0x77, 0x8f, 0xff, 0xff, 0xff, 0xff,
  0x8f, 0x77, 0x7f, 0x7f, 0x0f, 0x77, 0x77, 0x77, 0x8f, 0xff, 0xff, 0xff,
  0xff, 0x07, 0xf7, 0xef, 0xef, 0xdf, 0xdf, 0xbf, 0xbf, 0xbf, 0xff, 0xff,
  0xff, 0xff, 0x8f, 0x77, 0x77, 0x77, 0x8f, 0x77, 0x77, 0x77, 0x8f, 0xff,
  0xff, 0xff, 0xff, 0x8f, 0x77, 0x77, 0x77, 0x87, 0xf7, 0xf7, 0x77, 0x8f,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x8f, 0xdf, 0xff, 0xff, 0xdf,
  0x8f, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x8f, 0xdf, 0xff, 0xff,
  0xcf, 0xdf, 0xbf, 0xff, 0xff, 0xff, 0xf7, 0xef, 0xdf, 0xbf, 0x7f, 0xbf,
  0xdf, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff,
  0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xbf, 0xdf, 0xef,
  0xf7, 0xef, 0xdf, 0xbf, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x77, 0x77,
  0xf7, 0xef, 0xdf, 0xdf, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x77,
  0x77, 0x67, 0x57, 0x57, 0x4f, 0x7f, 0x87, 0xff, 0xff, 0xff, 0xff, 0xdf,
  0xaf, 0x77, 0x77, 0x77, 0x07, 0x77, 0x77, 0x77, 0xff, 0xff, 0xff, 0xff,
  0x0f, 0xb7, 0xb7, 0xb7, 0x8f, 0xb7, 0xb7, 0xb7, 0x0f, 0xff, 0xff, 0xff,
  0xff, 0x8f, 0x77, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x77, 0x8f, 0xff, 0xff,
  0xff, 0xff, 0x0f, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0x0f, 0xff,
  0xff, 0xff, 0xff, 0x07, 0x7f, 0x7f, 0x7f, 0x0f, 0x7f, 0x7f, 0x7f, 0x07,
  0xff, 0xff, 0xff, 0xff, 0x07, 0x7f, 0x7f, 0x7f, 0x0f, 0x7f, 0x7f, 0x7f,
  0x7f, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x77, 0x7f, 0x7f, 0x7f, 0x67, 0x77,
  0x77, 0x8f, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77, 0x77, 0x77, 0x07, 0x77,
  0x77, 0x77, 0x77, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xdf, 0xdf, 0xdf, 0xdf,
  0xdf, 0xdf, 0xdf, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xef, 0xef, 0xef,
  0xef, 0xef, 0xef, 0x6f, 0x9f, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77, 0x6f,
  0x5f, 0x3f, 0x5f, 0x6f, 0x77, 0x77, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x7f,
  0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x07, 0xff, 0xff, 0xff, 0xff, 0x77,
  0x77, 0x27, 0x57, 0x57, 0x77, 0x77, 0x77, 0x77, 0xff, 0xff, 0xff, 0xff,
  0x77, 0x37, 0x37, 0x57, 0x57, 0x67, 0x67, 0x77, 0x77, 0xff, 0xff, 0xff,
  0xff, 0x8f, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x8f, 0xff, 0xff,
  0xff, 0xff, 0x0f, 0x77, 0x77, 0x77, 0x0f, 0x7f, 0x7f, 0x7f, 0x7f, 0xff,
  0xff, 0xff, 0xff, 0x8f, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x57, 0x8f,
  0xf7, 0xff, 0xff, 0xff, 0x0f, 0x77, 0x77, 0x77, 0x0f, 0x5f, 0x6f, 0x77,
  0x77, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x77, 0x7f, 0x7f, 0x8f, 0xf7, 0xf7,
  0x77, 0x8f, 0xff, 0xff, 0xff, 0xff, 0x07, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
  0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77, 0x77, 0x77, 0x77,
  0x77, 0x77, 0x77, 0x8f, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77, 0x77, 0x77,
  0xaf, 0xaf, 0xaf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77, 0x77,
  0x77, 0x57, 0x57, 0x57, 0x27, 0x77, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77,
  0xaf, 0xaf, 0xdf, 0xaf, 0xaf, 0x77, 0x77, 0xff, 0xff, 0xff, 0xff, 0x77,
  0x77, 0xaf, 0xaf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0xff,
  0x07, 0xf7, 0xef, 0xef, 0xdf, 0xbf, 0xbf, 0x7f, 0x07, 0xff, 0xff, 0xff,
  0xff, 0x8f, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x8f, 0xff, 0xff,
  0xff, 0xff, 0x7f, 0x7f, 0xbf, 0xbf, 0xdf, 0xef, 0xef, 0xf7, 0xf7, 0xff,
  0xff, 0xff, 0xff, 0x8f, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0x8f,
  0xff, 0xff, 0xff, 0xff, 0xdf, 0xaf, 0x77, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xcf, 0xef, 0xf7, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf7,
  0x87, 0x77, 0x77, 0x87, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x7f, 0x7f, 0x0f,
  0x77, 0x77, 0x77, 0x77, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0x8f, 0x77, 0x7f, 0x7f, 0x77, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf7,
  0xf7, 0x87, 0x77, 0x77, 0x77, 0x77, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0x8f, 0x77, 0x07, 0x7f, 0x77, 0x8f, 0xff, 0xff, 0xff, 0xff,
  0xcf, 0xb7, 0xbf, 0xbf, 0x0f, 0xbf, 0xbf, 0xbf, 0xbf, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0x8f, 0x77, 0x77, 0x77, 0x87, 0xf7, 0x77, 0x8f,
  0xff, 0xff, 0x7f, 0x7f, 0x7f, 0x4f, 0x37, 0x77, 0x77, 0x77, 0x77, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0x9f, 0xdf, 0xdf, 0xdf, 0xdf, 0x8f,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xcf, 0xef, 0xef, 0xef, 0xef,
  0x6f, 0x6f, 0x9f, 0xff, 0xff, 0x7f, 0x7f, 0x7f, 0x6f, 0x5f, 0x3f, 0x5f,
  0x6f, 0x77, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
  0xdf, 0xdf, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2f, 0x57,
  0x57, 0x57, 0x57, 0x77, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4f,
  0x37, 0x77, 0x77, 0x77, 0x77, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0x8f, 0x77, 0x77, 0x77, 0x77, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0x0f, 0x77, 0x77, 0x77, 0x0f, 0x7f, 0x7f, 0x7f, 0xff, 0xff, 0xff,
  0xff, 0xff, 0x87, 0x77, 0x77, 0x77, 0x87, 0xf7, 0xf7, 0xf7, 0xff, 0xff,
  0xff, 0xff, 0xff, 0x4f, 0x37, 0x7f, 0x7f, 0x7f, 0x7f, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0x8f, 0x77, 0x9f, 0xef, 0x77, 0x8f, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xbf, 0xbf, 0x0f, 0xbf, 0xbf, 0xbf, 0xb7, 0xcf, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77, 0x77, 0x77, 0x67, 0x97,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77, 0x77, 0xaf, 0xaf,
  0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77, 0x57, 0x57,
  0x57, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0xaf, 0xdf,
  0xdf, 0xaf, 0x77, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0x77,
  0x77, 0x67, 0x97, 0xf7, 0x77, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07,
  0xef, 0xdf, 0xbf, 0x7f, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xdf, 0xdf,
  0xdf, 0x3f, 0xdf, 0xdf, 0xdf, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xdf,
  0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x3f,
  0xdf, 0xdf, 0xdf, 0xe7, 0xdf, 0xdf, 0xdf, 0x3f, 0xff, 0xff, 0xff, 0xff,
  0xb7, 0x57, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a
};

static void put_char(SDL_Surface *ts, Uint32 col, int x, int y, char c) {
  const unsigned char *fptr;
  int l, i;
  int bpp = ts->format->BytesPerPixel;

  if(c >= 0x20 && c <= 0x7f) {
    fptr = font_ripped_from_sge + (c - 0x20) * FONTHEIGHT; //Point to first byte of the character.
    for(l = 0; l < FONTHEIGHT; ++l) {
      int shift = 0x80;
      for(i = 0; i < FONTWIDTH; i++) {
	if(!(*fptr & shift)) {
	  Uint8 *p = (Uint8 *)ts->pixels + (y + l) * ts->pitch + (x + i) * bpp;
	  switch(bpp) {
	  case 1:
	    *p = col;
	    break;
	  case 2:
	    *(Uint16 *)p = col;
	    break;
	  case 3:
	    if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
	      p[0] = (col >> 16) & 0xff;
	      p[1] = (col >> 8) & 0xff;
	      p[2] = col & 0xff;
	    } else {
	      p[0] = col & 0xff;
	      p[1] = (col >> 8) & 0xff;
	      p[2] = (col >> 16) & 0xff;
	    }
	    break;
	  case 4:
	    *(Uint32 *)p = col;
	    break;
	  default:
	    abort();
	  }
	}
	shift >>= 1;
      }
      ++fptr;
    }
  }
}

static void center_text(SDL_Surface *ts, Uint32 col, int y, const char *cptr) {
  int i;
  size_t slen = strlen(cptr);
  int x = (ts->w - slen * FONTWIDTH) / 2;
  for(i = 0; i < slen; ++i) {
    put_char(ts, col, x, y, *cptr++);
    x += FONTWIDTH;
  }
}

static int do_delay() {
  SDL_Event event;
  int i;
  int button = -1;

  for(i = 0; button == -1 && i < 15; ++i) {
    while(SDL_PollEvent(&event) == 1) {
      switch(event.type) {
      case SDL_QUIT:
	abort();
	break;
      case SDL_MOUSEBUTTONDOWN:
	//case SDL_MOUSEBUTTONUP:
	button = event.button.button;
	break;
      case SDL_KEYDOWN:
      case SDL_KEYUP:
	if(event.key.keysym.sym == SDLK_ESCAPE) button = 1;
	break;
      default:
	break;
      }
    }
    SDL_Delay(100);
  }
  return button;
}

int guru_display_lowlevel(SDL_Surface *ts, unsigned short flags, const char **atext0) {
  int i, button;
  Uint32 fg_col;
  Uint32 black = SDL_MapRGB(ts->format, 0xb, 0, 0);
  int borderflag = 0;
  int lines = 0;
  const int border = 6;
  const char **txtptr = atext0;
  int max_border_flashes = 19; //Flash border that many times before continuing...

  while(*(txtptr++) != NULL) lines++;
  switch(flags & 0x03) {
  case 0:
    fg_col = SDL_MapRGB(ts->format, 0xef, 0xed, 0x3);
    break;
  case 2:
    fg_col = SDL_MapRGB(ts->format, 0xb, 0xfb, 0x3);
    break;
  case 3:
    fg_col = SDL_MapRGB(ts->format, 0x63, 0x6b, 0xfb);
    break;
  default: //Red on all other occasions. Mostly to make the compiler happy...
    fg_col = SDL_MapRGB(ts->format, 0xfb, 0xb, 0x3);
    break;
  }
  do {
    int y = 20;
    SDL_Rect rect = { 0, 0, ts->w, y + FONTHEIGHT * (lines + 1) };
    borderflag = ~borderflag;
    if(borderflag) {
      SDL_FillRect(ts, &rect, fg_col);
      rect.x += border;
      rect.y += border;
      rect.w -= 2 * border;
      rect.h -= 2 * border;
    }
    SDL_FillRect(ts, &rect, black);
    SDL_LockSurface(ts);
    for(i = 0; i < lines; ++i) {
      center_text(ts, fg_col, y, atext0[i]);
      y += FONTHEIGHT;
    }
    SDL_UnlockSurface(ts);
    SDL_Flip(ts);
    button = do_delay();
    if(flags & GM_FLAGS_AUTOTIMEOUT && --max_border_flashes < 0) {
      button = 1; //Left button was pressed...
    }
  } while(button == -1);
  return button;
}


int guru_display_gp(SDL_Surface *ts, unsigned short flags, Uint32 subsystem, void *address, const char *atext) {
  int button;
  int i;
  char buf[2][80];
  /* char tmpbuf[20]; */
  const char* cptrs[4] = { buf[0], atext, buf[1], NULL };

  sprintf(buf[0], "Software Failure. Press left mouse button to %s.", flags & GM_FLAGS_CHOICE ? "retry, right for abort." : "continue");
  i = sprintf(buf[1], "Guru Meditation #%08X.", subsystem);
  /* sprintf(tmpbuf, "%p", address); */
  /* strcpy(buf[1] + i, tmpbuf + 2); */
  if(sizeof(void*) > 4) { //TODO: Check for static asserts to do this correctly!
    i = sprintf(buf[1] + i, "%016lX", (unsigned long int)address);
  } else {
    i = sprintf(buf[1] + i, "%08lX", (unsigned long int)address);
  }
  button = guru_display_lowlevel(ts, flags, cptrs);
  return button;
}

int guru_alert(unsigned short flags, Uint32 subsystem, void *address) {
  int button;
  SDL_Surface *ts;

  ts = SDL_GetVideoSurface();
  button = guru_display_gp(ts, flags, subsystem, address, "");
  return button;
}

int guru_meditation(unsigned short flags, Uint32 subsystem, void *address) {
  int button = guru_alert(flags, subsystem, address);
  if(button == 3 && (flags & GM_FLAGS_ABORTIFY)) abort();
  if(flags & GM_FLAGS_DEADEND) {
    if(flags & GM_FLAGS_EXIT_VIA_TERM) {
      raise(SIGTERM);
    } else {
      exit(EXIT_FAILURE);
    }
    raise(SIGILL);
  }
  return button;
}

int guru_display_alert2(unsigned short flags, const char *file, unsigned long line) {
  //Guru Medidation @guru_meditation.c:296.
  return -1;
}

/*
 * Guru Meditation 0000:0000:00000000.00000000 oder so...
 */
