#include <stdio.h>
#include <string.h>
#include <allegro.h>

#include "lab.h"
#include "title.h"

char map[WIDTH][HEIGHT][SIZE];
char explored[WIDTH][HEIGHT][SIZE];
int pkeys, px, py, pz = -1;
int skeys, sx, sy, sz = -1;

int dest_search;

//////////////////// DRAWING FUNCTIONS

void draw(int x, int y)
{
  int ch = (int)map[x][y][pz];
  int fg = makecol(0, 255, 0);
  int bg = makecol(0, 64, 0);

  switch(ch) {
    case ']': // wall
      bg = fg;
      fg = makecol(0, 0, 0);
      ch = ' ';
      break;

    case '=': // explore

    case '.': // free
      ch = ' ';
      bg = makecol(0, 64, 0);
      break;

    case ',': // key
      ch = '#';
      fg = makecol(0, 0, 255);
      break;

    case '/': // brick
      ch = ' ';
      fg = makecol(0, 0, 0);
      bg = makecol(0, 0, 255);
      break;

    case '`': // tnt
      ch = '*';
      fg = makecol(255, 0, 0);
      break;

    case ';': // exit
      fg = makecol(255, 255, 255);
      ch = 'E';
      break;

    default:
      if(ch >= 'a' && ch <= 'h') // stairs up go!
        ch = '^', fg = makecol(255, 255, 255);
      if(ch >= 'i' && ch <= 'p') // stairs down go!
        ch = 'v', fg = makecol(255, 255, 255);
      if(ch >= 'q' && ch <= 'u') // teleport go!
        ch = '*', fg = makecol(0, 0, 0), bg = makecol(0, 255, 255);
      if(ch >= 'v' && ch <= 'z') // hidden teleport go!
        ch = ' ';
      if(ch >= 'A' && ch <= 'Z') // teleport/stairs end
        ch = ' ';
      break;
  }

  if(!explored[x][y][pz]) {
    fg = bg = makecol(0, 0, 0);
    ch = ' ';
  }
  if(px == x && py == y && ch == ' ') {
    //  this smile creation code is pretty ugly
    BITMAP *b;
    int c = makecol(255, 255, 0);
    int g = makecol(0, 64, 0);
    b = create_bitmap(8, 8);
    clear_to_color(b, makecol(0, 0, 0));
    putpixel(b, 0, 0, g);
    putpixel(b, 1, 0, g);
    putpixel(b, 0, 1, g);
    putpixel(b, 6, 0, g);
    putpixel(b, 7, 0, g);
    putpixel(b, 7, 1, g);
    putpixel(b, 0, 6, g);
    putpixel(b, 0, 7, g);
    putpixel(b, 1, 7, g);
    putpixel(b, 6, 7, g);
    putpixel(b, 7, 7, g);
    putpixel(b, 7, 6, g);
    putpixel(b, 2, 2, c);
    putpixel(b, 5, 2, c);
    putpixel(b, 1, 4, c);
    putpixel(b, 6, 4, c);
    putpixel(b, 2, 5, c);
    putpixel(b, 5, 5, c);
    putpixel(b, 3, 6, c);
    putpixel(b, 4, 6, c);
    blit(b, screen, 0, 0, (x*8)+MAP_START_X, (y*8)+MAP_START_Y, 8, 8);
    destroy_bitmap(b);
    return;
  }
  else {
    char buf[2] = " ";
    buf[0] = ch;
    text_mode(bg);
    textout(screen, font, buf, (x*8)+MAP_START_X, (y*8)+MAP_START_Y, fg);
  }
}

#define STATUS_X (MAP_START_X+(WIDTH*8)+2)
#define STATUS_Y (MAP_START_Y+(HEIGHT*8)+2)

void clean_statusbar()
{
  rectfill(screen, 0, STATUS_Y, SCREEN_W-1, STATUS_Y+8, makecol(0,0,0));
}

void show_statusbar()
{
  clean_statusbar();
  text_mode(makecol(0, 0, 0));
  textprintf(screen, font, MAP_START_X, STATUS_Y, makecol(0, 255, 0), "Pozicia [%d,%d]", px, py);
  textprintf_right(screen, font, STATUS_X, STATUS_Y, makecol(0, 255, 0), "Kluce: %d", pkeys);
}

//////////////////// PLAYING FUNCTIONS

void explore(int x, int y)
{
  explored[x][y][pz] = 1;
  draw(x, y);
}

void redraw_all()
{
  int x, y;
  for(x = 0; x < WIDTH; x++)
    for(y = 0; y < HEIGHT; y++)
      draw(x, y);
}

// just helper function
void exploration_hlp(int x, int y)
{
  explore(x, y);
  if(map[x][y][pz] != ']') {
    explore(x-1, y);
    explore(x, y-1);
    explore(x+1, y);
    explore(x, y+1);
  }
}

void exploration()
{
  explore(px, py);
  if(map[px][py][pz] != ']') {
    exploration_hlp(px-1, py);
    exploration_hlp(px, py-1);
    exploration_hlp(px+1, py);
    exploration_hlp(px, py+1);
  }
}

int brick_can_move(int x, int y, int dx, int dy)
{
  int nx = x + dx, ny = y + dy;
  int nc = map[nx][ny][pz];
  if(nc == '.') return 1;
  if(nc == '`') return 1;
  if(nc != '/') return 0;
  return brick_can_move(nx, ny, dx, dy);
}

int can_move(int x, int y)
{
  int ch = map[x][y][pz];

  if(ch == ']')
    return 0;
  if(ch == '.')
    return 1;
  if(ch == ',')
    return 1;
  if(ch >= 'a' && ch <= 'z')
    return 1;
  if(ch >= 'A' && ch <= 'Z')
    return 1;
  if(ch == ';')
    return !pkeys;
  if(ch == '/') // brick
    return brick_can_move(x, y, x - px, y - py);
  if(ch == '`')
    return 0;
  if(ch == '=')
    return 1;
  return 0;
}

void brick_move(int x, int y, int dx, int dy)
{
  int mx = x, my = y;
  while(map[x][y][pz] == '/')
    x += dx, y += dy;

  if(map[x][y][pz] == '.')
    map[x][y][pz] = '/';
  if(map[x][y][pz] == '`')
    map[x][y][pz] = '.';
  map[mx][my][pz] = '.';
  draw(x, y);
  draw(mx, my);
}

int move(int nx, int ny)
{
  int ch = map[nx][ny][pz];
  int wx, wy, wz = -1; // for teleports

  if(ch >= 'a' && ch <= 'z') {
    int x, y, z;
    int search;
    // searching for teleport desintation
    clean_statusbar();
    textout_centre(screen, font, "searching for desintation... press Esc to quit", SCREEN_W/2, STATUS_Y, makecol(0, 255, 0));
    dest_search = search = ch - 'a' + 'A';
    for(x = 0; x < WIDTH; x++) {
      for(y = 0; y < HEIGHT; y++) {
        for(z = 0; z < SIZE; z++) {
          if(map[x][y][z] == search)
            wx = x, wy = y, wz = z;
          if(keypressed())
            if(readkey() >> 8 == KEY_ESC)
              return 1; // game aborted
        }
      }
    }
    if(wz == -1)
      return 3; // desintation not found
    nx = wx;
    ny = wy;
    pz = wz;
  }

  if(ch == '/')
    brick_move(nx, ny, nx - px, ny - py);

  if(ch == ',') {
    if(pkeys) pkeys--;
    map[nx][ny][pz] = '.';
  }

  if(ch == '=') {
    int x, y;
    for(x = 0; x < WIDTH; x++)
      for(y = 0; y < HEIGHT; y++)
        explored[x][y][pz] = 1;
  }

  px = nx;
  py = ny;

  show_statusbar();

  if(ch == ';')
    return 2; // game solved

  if(ch == '=' || wz != -1)
    return 4; // redraw all
  return 0; // all right
}

void play_game()
{
  int x, y, z;
  int end = 0;
  if(sz == -1) {
    clear_to_color(screen, makecol(0, 255, 255));
    text_mode(-1);
    textout_centre(screen, font, "cannot find the player... exit with Esc", SCREEN_W/2, SCREEN_H/2, makecol(255, 0, 0));
    rest(2000);
    return;
  }
  px = sx;
  py = sy;
  pz = sz;
  pkeys = skeys;
  for(z = 0; z < SIZE; z++)
    for(y = 0; y < HEIGHT; y++)
      for(x = 0; x < WIDTH; x++)
        explored[x][y][z] = 0;

  exploration();
  show_statusbar();

  while(!end) {
    if(keypressed()) {
      int scancode = readkey() >> 8;
      int nx = px, ny = py;
      int movekey = 1;
      switch(scancode) {
        case KEY_ESC:
          end = 1;
          movekey = 0;
          break;

        case KEY_LEFT:
          nx--;
          break;

        case KEY_UP:
          ny--;
          break;

        case KEY_RIGHT:
          nx++;
          break;

        case KEY_DOWN:
          ny++;
          break;

        default:
          movekey = 0;
          break;
      }
      if(movekey) {
        if(can_move(nx, ny)) {
          int r = move(nx, ny);
          switch(r) {
            case 0:
              exploration();
              break;

            case 1:
            case 2:
            case 3:
              exploration();
              end = r;
              break;

            case 4:
              exploration();
              redraw_all();
              break;
          }
        }
      }
    }
  }

  clean_statusbar();
  text_mode(makecol(0,0,0));
  switch(end) {
    case 1:
      textout_centre(screen, font, "GAME ABORTED", SCREEN_W/2, STATUS_Y, makecol(255, 0, 0));
      rest(500);
      return;

    case 2:
      textout_centre(screen, font, "*** CONGRATULATIONS!!! YOU WON! ***", SCREEN_W/2, STATUS_Y, makecol(255, 0, 0));
      rest(500);
      textout_centre(screen, font, "*** CONGRATULATIONS!!! YOU WON! ***", SCREEN_W/2, STATUS_Y, makecol(0, 255, 0));
      rest(500);
      textout_centre(screen, font, "*** CONGRATULATIONS!!! YOU WON! ***", SCREEN_W/2, STATUS_Y, makecol(255, 255, 0));
      rest(500);
      textout_centre(screen, font, "*** CONGRATULATIONS!!! YOU WON! ***", SCREEN_W/2, STATUS_Y, makecol(0, 0, 255));
      rest(500);
      return;

    case 3:
      textprintf_centre(screen, font, SCREEN_W/2, STATUS_Y, makecol(255, 0, 0), "FATAL ERROR: desintation %c not found!", dest_search-'a'+'A');
      rest(2000);
      return;
  }
}