/*
 * OpenKropki
 *
 * Copyright (C) 2014-2020 Mateusz Viste
 *
 * MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>           /* free() */
#include <string.h>           /* memset() */
#include <time.h>
#include <SDL2/SDL_thread.h>

#include "ai.h"
#include "drv_gra.h"
#include "lang.h"
#include "ui.h"
#include "kropki_core.h"
#include "prefs.h"
#include "version.h"
#include "media/data.h"       /* contains the font data, sprites etc */

/* set to non-zero to enable debug data on captured fields */
#ifndef DEBUG_CAPTURES
  #define DEBUG_CAPTURES 0
#endif

/* enable the experimental Karl AI (MCTS-based) */
#ifndef ENABLE_KARL
  #define ENABLE_KARL 0
#endif


enum playertype_t {
  PLAYER_HUMAN,
  PLAYER_DUMBO,
  PLAYER_KARL,
  PLAYER_TIGRAN
};

struct rozgrywka_t {
  int scrollvector[2];
  int endgamesfxplayed;
  int kratkasize;
  int points[2]; /* remember last state of points (to detect new wins and to draw screen without re-counting while AI operates) */
  SDL_Thread *ai_thread;
  int ai_in_progress; /* a flag that is set to zero whenever the AI finishes processing - this may be used as a semaphore for multithreaded operations */
  enum playertype_t p[2]; /* players definitions */
  struct kropki_game *game;
  int *aidebugfields; /* list of fields that AI wants to highlight (debug purposes), format: x1, y1, x2, y2, x3, y3, (...), -1 */
};


/* used to compute UI hints */
#define hintgen(a) gra_text2sprite(a, DroidSansBold_ttf, DroidSansBold_ttf_len, 15, 0x505050u, 0xf1f1f1u, 0xf1, getmenuwidth() / 3);


/* used to display localized type names of players during game */
static enum msgid_t playertype2name(enum playertype_t p) {
  switch (p) {
    case PLAYER_DUMBO: return(MSG_DUMBO);
    case PLAYER_KARL: return(MSG_KARL);
    case PLAYER_TIGRAN: return(MSG_TIGRAN);
    case PLAYER_HUMAN: return(MSG_HUMAN);
  }
  return(MSG_HUMAN); /* useless since I cover all possible enum cases... it's just to shut gcc warning */
}


/* translates a player type enum value into a text identifier that may be written into an OKP string */
static const char *playertype2txtid(enum playertype_t p) {
  switch (p) {
    case PLAYER_DUMBO: return("DUMBO");
    case PLAYER_KARL: return("KARL");
    case PLAYER_TIGRAN: return("TIGRAN");
    case PLAYER_HUMAN:
    default:           return("HUMAN");
  }
}


/* translates a player's OKP "name" into a player type enum */
static enum playertype_t txtid2playertype(const char *s) {
  if (strcmp(s, "DUMBO") == 0) return(PLAYER_DUMBO);
  if (strcmp(s, "KARL") == 0) return(PLAYER_KARL);
  if (strcmp(s, "TIGRAN") == 0) return(PLAYER_TIGRAN);
  return(PLAYER_HUMAN);
}


static void redraw_screen(struct rozgrywka_t *r, const char *msg, int msgplayercolor, int shadebases) {
  int x, y, firstx, firsty, lastx, lasty;
  const char *playernames[2];
  struct kropki_wallelement *wallsptr;
  int winw, winh;
  int i;
  /* find coordinates of the first and last visible points so rendering can be limited to visible area */
  gra_getwinsize(&winw, &winh);
  firstx = -1 - (r->scrollvector[0] / r->kratkasize);
  firsty = -1 - (r->scrollvector[1] / r->kratkasize);
  lastx = 1 - ((r->scrollvector[0] - winw) / r->kratkasize);
  lasty = 1 - ((r->scrollvector[1] - winh) / r->kratkasize);
  if (firstx < 0) firstx = 0;
  if (firsty < 0) firsty = 0;
  if (lastx < 0) lastx = 0;
  if (lasty < 0) lasty = 0;
  if (lastx >= r->game->field_width) lastx = r->game->field_width - 1;
  if (lasty >= r->game->field_height) lasty = r->game->field_height - 1;
  /* draw the (empty) playfield */
  ui_drawKartka(r->game->field_width, r->game->field_height, r->scrollvector);
  /* draw walls */
  for (wallsptr = r->game->walls; wallsptr != NULL; wallsptr = wallsptr->next) {
    if ((wallsptr->x1 < firstx) || (wallsptr->x1 > lastx) || (wallsptr->y1 < firsty) || (wallsptr->y1 > lasty)) continue; /* skip walls outside the currently visible area */
    graph_drawWall(wallsptr->x1, wallsptr->y1, wallsptr->x2, wallsptr->y2, wallsptr->player, r->scrollvector);
  }
  /* draw dots */
  for (y = firsty; y <= lasty; y++) {
    for (x = firstx; x <= lastx; x++) {
      int fieldval;
      fieldval = KROPKI_GETPOS(r->game, x, y);
      if ((fieldval & 3) != 0) {
        int activeflag = 0;
        if ((r->game->lastmovex == x) && (r->game->lastmovey == y)) activeflag = 1;
        graph_drawKropka(x, y, fieldval & 3, activeflag, r->scrollvector);
      }
#if DEBUG_CAPTURES != 0
      /* --- DEBUG START --- */
      if ((fieldval >> 2) & 3) { /* draw a circle around field if captured */
        uint32_t col = 0x1010ffu;
        if (((fieldval >> 2) & 3) == 2) col = 0xff0000u;
        gra_circle((x + 1) * r->kratkasize + r->scrollvector[0], (y + 1) * r->kratkasize + r->scrollvector[1], r->kratkasize * 4 / 7, col, 0xff);
        gra_circle((x + 1) * r->kratkasize + r->scrollvector[0], (y + 1) * r->kratkasize + r->scrollvector[1], r->kratkasize * 4 / 7 - 1, col, 0xff);
      }
      /* --- DEBUG STOP ---*/
#endif
    }
  }
  if (r->aidebugfields != NULL) {
    int i;
    for (i = 0;; i += 2) {
      x = r->aidebugfields[i];
      if (x < 0) break;
      y = r->aidebugfields[i+1];
      gra_circle((x + 1) * r->kratkasize + r->scrollvector[0], (y + 1) * r->kratkasize + r->scrollvector[1], r->kratkasize * 4 / 7 - 1, 0x10ff10, 0xff);
    }
  }
  /* draw shaded (captured) areas, if enabled */
  if (shadebases != 0) {
    for (y = firsty; y < lasty; y++) {
      for (x = firstx; x < lastx; x++) {
        int fieldval, capturedby;
        fieldval = KROPKI_GETPOS(r->game, x, y);
        capturedby = (fieldval >> 2) & 3;
        /* shading for captured dots */
        if (capturedby != 0) {
          /* start by drawing a patterned diamond around the dot */
          ui_shadediamond(x, y, capturedby, r->scrollvector);
          /* check right neighbor and draw a big rect there if neighbor captured */
          if ((kropki_getpos(r->game, x + 1, y) >> 2) & 3) {
            ui_shadeblock(x, y, 1, 2, capturedby, r->scrollvector);
          }
          /* check top neighbor */
          if ((kropki_getpos(r->game, x, y - 1) >> 2) & 3) {
            ui_shadeblock(x - 1, y, 2, 1, capturedby, r->scrollvector);
          }
        }
      }
    }
  }
  /* Draw scores */
  for (i = 0; i < 2; i++) {
    playernames[i] = lang_msg(playertype2name(r->p[i]));
  }
  ui_drawscores(r->points, msg, msgplayercolor, playernames);
  /* refresh the screen */
  gra_refresh();
}



#define WIN_WIDTH  900
#define WIN_HEIGHT 600
#define MENU_MAXWIDTH (WIN_WIDTH * 3 / 2)
#define MENU_MAXHEIGHT (WIN_HEIGHT * 3 / 2)
#define COLORPLAYER1 0x3030FF
#define COLORPLAYER2 0xFF0000
#define INITIAL_KRATKA_SIZE 36

static void centerscrollvector(struct rozgrywka_t *rozgrywka) {
  int wc, hc;
  int lmx, lmy;
  /* compute window's center */
  gra_getwinsize(&wc, &hc);
  wc /= 2;
  hc /= 2;
  /* compute where last move is on the playfield (proportionally) */
  if (rozgrywka->game->lastplayer != 0) {
    lmx = rozgrywka->game->lastmovex;
    lmy = rozgrywka->game->lastmovey;
  } else {  /* fall back to field center */
    lmx = rozgrywka->game->field_width / 2;
    lmy = rozgrywka->game->field_height / 2;
  }
  /* translate lmx,lmy to pixels from the top left edge of the playfield */
  lmx = (lmx + 1) * rozgrywka->kratkasize;
  lmy = (lmy + 1) * rozgrywka->kratkasize;
  /* center around the point */
  rozgrywka->scrollvector[0] = wc - lmx;
  rozgrywka->scrollvector[1] = hc - lmy;
}


/* a wrapper around ai_play() that can be called as a thread */
static int ai_caller(void *rozgrywka) {
  char *okps;
  int movex = -1, movey = -1;
  struct rozgrywka_t *r = rozgrywka;
  unsigned long aithinkuntil;
  /* preset a timer to wait at least 0.5s so AI feels more natural and avoid sfx sounds to queue on */
  aithinkuntil = ui_getticks() + 500;
  /* prepare the okps field description */
  okps = kropki_struct2okp(r->game);
  /* run A.I. */
  printf("calling AI: '%s'\n", okps);
  switch (r->p[r->game->nextplayer - 1]) {
    case PLAYER_DUMBO:
      ai_dumbo(okps, &movex, &movey);
      break;
    case PLAYER_KARL:
      ai_karl(okps, &movex, &movey);
      break;
    case PLAYER_TIGRAN:
      ai_tigran(okps, &movex, &movey, &(r->aidebugfields));
      break;
    default:
      printf("ERROR: UNEXPECTED PLAYER TYPE\n");
      break;
  }
  free(okps);
  /* artificial delay (if AI was too fast) */
  for (;;) {
    if (ui_getticks() >= aithinkuntil) break;
    if (ui_getticks() + 4000 < aithinkuntil) break; /* wrap protection */
    ui_delay(10);
  }
  /* check A.I. answer and apply its move */
  printf("AI answer: %d.%d\n", movex, movey);
  if ((movex >= 0) && (movey >= 0)) {
    kropki_setpos(r->game, movex, movey, r->game->nextplayer, 0);
  }
  r->ai_in_progress = 0;
  return(0);
}


static int getmenuwidth(void) {
  int r;
  gra_getwinsize(&r, NULL);
  if (r > MENU_MAXWIDTH) return(MENU_MAXWIDTH);
  return(r);
}


#define MAINMENU_NEWGAME 1u
#define MAINMENU_RESUME 2u
#define MAINMENU_SETTINGS 3u
#define MAINMENU_ABOUT 4u
#define MAINMENU_QUIT 40u

/* main menu */
static int menu_main(struct rozgrywka_t *rozgrywka, int sound) {
  struct ui_menu_description_t menudesc[16];
  int menucount = 0;
  int menupos;
  const int menustep = 10;
  const int menuystart = 12;

  /* zero out the menudesc array */
  memset(menudesc, 0, sizeof(menudesc));

  /* prepare the menu description */
  /* frontpic goes first, so it is never rendered over a label */
  menudesc[menucount].sprite[0] = gra_loadgzbmp(frontpic_bmp_gz, frontpic_bmp_gz_len);
  menudesc[menucount].propxpos = 0;
  menudesc[menucount].propypos = 100;
  menucount++;
  /* OpenKropki title */
  menudesc[menucount].sprite[0] = gra_text2sprite("OpenKropki v" PVER, DroidSansBold_ttf, DroidSansBold_ttf_len, 40, 0, -1, 0xff, -1);
  menudesc[menucount].propxpos = 50;
  menudesc[menucount].propypos = 16;
  menudesc[menucount].flags = UI_MENU_FLAG_XCENTER;
  menucount++;
  /* copyright subtitle */
  menudesc[menucount].sprite[0] = gra_text2sprite("Copyright © " PDATE " Mateusz Viste", DroidSansBold_ttf, DroidSansBold_ttf_len, 16, 0, -1, 0x60, -1);
  menudesc[menucount].propxpos = 50;
  menudesc[menucount].propypos = 0;
  menudesc[menucount].flags = UI_MENU_FLAG_XCENTER | UI_MENU_FLAG_YRELPOS;
  menucount++;
  /* */
  menudesc[menucount].sprite[0] = gra_text2sprite(lang_msg(MSG_NEW_GAME), DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 0xff, -1);
  menudesc[menucount].sprite[1] = gra_text2sprite(lang_msg(MSG_NEW_GAME), DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0x0000ffu, -1, 0xff, -1);
  menudesc[menucount].clickid = MAINMENU_NEWGAME;
  menudesc[menucount].propxpos = 50;
  menudesc[menucount].propypos = menuystart + (menustep * menucount);
  menudesc[menucount].flags = UI_MENU_FLAG_XCENTER;
  menucount++;
  /* */
  if (rozgrywka->game == NULL) {
    menudesc[menucount].sprite[0] = gra_text2sprite(lang_msg(MSG_RESUME_GAME),   DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 90, -1);
  } else {
    menudesc[menucount].sprite[0] = gra_text2sprite(lang_msg(MSG_RESUME_GAME),   DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 0xff, -1);
    menudesc[menucount].sprite[1] = gra_text2sprite(lang_msg(MSG_RESUME_GAME),   DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0x0000ffu, -1, 0xff, -1);
  }
  menudesc[menucount].clickid = MAINMENU_RESUME;
  menudesc[menucount].propxpos = 50;
  menudesc[menucount].propypos = menuystart + (menustep * menucount);
  menudesc[menucount].flags = UI_MENU_FLAG_XCENTER;
  menucount++;
  /* */
  menudesc[menucount].sprite[0] = gra_text2sprite(lang_msg(MSG_SETTINGS), DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 0xff, -1);
  menudesc[menucount].sprite[1] = gra_text2sprite(lang_msg(MSG_SETTINGS), DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0x0000ffu, -1, 0xff, -1);
  menudesc[menucount].clickid = MAINMENU_SETTINGS;
  menudesc[menucount].propxpos = 50;
  menudesc[menucount].propypos = menuystart + (menustep * menucount);
  menudesc[menucount].flags = UI_MENU_FLAG_XCENTER;
  menucount++;
  /* */
  menudesc[menucount].sprite[0] = gra_text2sprite(lang_msg(MSG_ABOUT), DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 0xff, -1);
  menudesc[menucount].sprite[1] = gra_text2sprite(lang_msg(MSG_ABOUT), DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0x0000ffu, -1, 0xff, -1);
  menudesc[menucount].clickid = MAINMENU_ABOUT;
  menudesc[menucount].propxpos = 50;
  menudesc[menucount].propypos = menuystart + (menustep * menucount);
  menudesc[menucount].flags = UI_MENU_FLAG_XCENTER;
  menucount++;
  /* */
  menudesc[menucount].sprite[0] = gra_text2sprite(lang_msg(MSG_EXIT_GAME), DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 0xff, -1);
  menudesc[menucount].sprite[1] = gra_text2sprite(lang_msg(MSG_EXIT_GAME), DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0x0000ffu, -1, 0xff, -1);
  menudesc[menucount].clickid = MAINMENU_QUIT;
  menudesc[menucount].propxpos = 50;
  menudesc[menucount].propypos = menuystart + (menustep * menucount);
  menudesc[menucount].flags = UI_MENU_FLAG_XCENTER;
  menucount++;

  /* compute menu */
  menupos = ui_menu(menudesc, menucount, 1, MENU_MAXWIDTH, MENU_MAXHEIGHT);
  if (sound) gra_playwavgz(sfx_click_wav_gz, sfx_click_wav_gz_len);

  return(menupos);
}


static int menu_lang(int sound) {
  int pos;
  struct ui_menu_description_t desc[16];
  int count = 0;
  const int menuystart = 10;
  const int menustep = 13;

  /* zero out the menudesc array */
  memset(desc, 0, sizeof(desc));

  /* frontpic goes first, so it is never rendered over a label */
  desc[count].sprite[0] = gra_loadgzbmp(frontpic_bmp_gz, frontpic_bmp_gz_len);
  desc[count].propxpos = 0;
  desc[count].propypos = 100;
  count++;
  /* CZ */
  desc[count].sprite[0] = gra_loadgzbmp(flag_cz_bmp_gz, flag_cz_bmp_gz_len);
  desc[count].sprite[1] = gra_loadgzbmp(flag_mouseover_bmp_gz, flag_mouseover_bmp_gz_len);
  desc[count].clickid = LANG_CZ;
  desc[count].flags = UI_MENU_FLAG_ORSPR | UI_MENU_FLAG_XCENTER;
  desc[count].propxpos = 50;
  desc[count].propypos = menuystart + (count * menustep);
  desc[count].hint = hintgen(lang_name(LANG_CZ));
  count++;
  /* EN */
  desc[count].sprite[0] = gra_loadgzbmp(flag_en_bmp_gz, flag_en_bmp_gz_len);
  desc[count].sprite[1] = gra_loadgzbmp(flag_mouseover_bmp_gz, flag_mouseover_bmp_gz_len);
  desc[count].clickid = LANG_EN;
  desc[count].flags = UI_MENU_FLAG_ORSPR | UI_MENU_FLAG_XCENTER;
  desc[count].propxpos = 50;
  desc[count].propypos = menuystart + (count * menustep);
  desc[count].hint = hintgen(lang_name(LANG_EN));
  count++;
  /* FR */
  desc[count].sprite[0] = gra_loadgzbmp(flag_fr_bmp_gz, flag_fr_bmp_gz_len);
  desc[count].sprite[1] = gra_loadgzbmp(flag_mouseover_bmp_gz, flag_mouseover_bmp_gz_len);
  desc[count].clickid = LANG_FR;
  desc[count].flags = UI_MENU_FLAG_ORSPR | UI_MENU_FLAG_XCENTER;
  desc[count].propxpos = 50;
  desc[count].propypos = menuystart + (count * menustep);
  desc[count].hint = hintgen(lang_name(LANG_FR));
  count++;
  /* PL */
  desc[count].sprite[0] = gra_loadgzbmp(flag_pl_bmp_gz, flag_pl_bmp_gz_len);
  desc[count].sprite[1] = gra_loadgzbmp(flag_mouseover_bmp_gz, flag_mouseover_bmp_gz_len);
  desc[count].clickid = LANG_PL;
  desc[count].flags = UI_MENU_FLAG_ORSPR | UI_MENU_FLAG_XCENTER;
  desc[count].propxpos = 50;
  desc[count].propypos = menuystart + (count * menustep);
  desc[count].hint = hintgen(lang_name(LANG_PL));
  count++;
  /* RU */
  desc[count].sprite[0] = gra_loadgzbmp(flag_ru_bmp_gz, flag_ru_bmp_gz_len);
  desc[count].sprite[1] = gra_loadgzbmp(flag_mouseover_bmp_gz, flag_mouseover_bmp_gz_len);
  desc[count].clickid = LANG_RU;
  desc[count].flags = UI_MENU_FLAG_ORSPR | UI_MENU_FLAG_XCENTER;
  desc[count].propxpos = 50;
  desc[count].propypos = menuystart + (count * menustep);
  desc[count].hint = hintgen(lang_name(LANG_RU));
  count++;
  /* back to main menu */
  desc[count].clickid = 0xff;
  desc[count].propxpos = 50;
  desc[count].propypos = 92;
  desc[count].flags = UI_MENU_FLAG_XCENTER;
  desc[count].sprite[0] = gra_text2sprite(lang_msg(MSG_BACK_TO_MAIN), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0, -1, 0xff, -1);
  desc[count].sprite[1] = gra_text2sprite(lang_msg(MSG_BACK_TO_MAIN), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0x0000ffu, -1, 0xff, -1);
  count++;

  /* compute menu */
  pos = ui_menu(desc, count, 1, MENU_MAXWIDTH, MENU_MAXHEIGHT);
  if (sound) gra_playwavgz(sfx_click_wav_gz, sfx_click_wav_gz_len);

  return(pos);
}


/* "playfield size" and other game configurations menu */
static int menu_playconf(int sound, int *playsize, enum playertype_t *p) {
  for (;;) {
    struct ui_menu_description_t menu[32];
    int count = 0;
    int menupos;
    int i;

    /* zero out the menudesc array */
    memset(menu, 0, sizeof(menu));

    /* playfield size */
    menu[count].propxpos = 5;
    menu[count].propypos = 12;
    menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_PLAYSIZE), DroidSans_ttf, DroidSans_ttf_len, 20, 0x3C3C3Cu, -1, 0xff, -1);
    count++;
    /* A7 */
    menu[count].clickid = 7;
    menu[count].propxpos = 32;
    menu[count].propypos = 12;
    if (*playsize == 7) {
      menu[count].sprite[0] = gra_text2sprite("A7 (20x14)", DroidSansBold_ttf, DroidSansBold_ttf_len, 20, 0xff8400u, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite("A7 (20x14)", DroidSansBold_ttf, DroidSansBold_ttf_len, 20, 0xff8400u, -1, 0xff, -1);
    } else {
      menu[count].sprite[0] = gra_text2sprite("A7 (20x14)", DroidSans_ttf, DroidSans_ttf_len, 20, 0, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite("A7 (20x14)", DroidSans_ttf, DroidSans_ttf_len, 20, 0x0000ffu, -1, 0xff, -1);
    }
    count++;
    /* A6 */
    menu[count].clickid = 6;
    menu[count].propxpos = 48;
    menu[count].propypos = 12;
    if (*playsize == 6) {
      menu[count].sprite[0] = gra_text2sprite("A6 (28x20)", DroidSansBold_ttf, DroidSansBold_ttf_len, 20, 0xff8400u, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite("A6 (28x20)", DroidSansBold_ttf, DroidSansBold_ttf_len, 20, 0xff8400u, -1, 0xff, -1);
    } else {
      menu[count].sprite[0] = gra_text2sprite("A6 (28x20)", DroidSans_ttf, DroidSans_ttf_len, 20, 0, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite("A6 (28x20)", DroidSans_ttf, DroidSans_ttf_len, 20, 0x0000ffu, -1, 0xff, -1);
    }
    count++;
    /* A5 */
    menu[count].clickid = 5;
    menu[count].propxpos = 64;
    menu[count].propypos = 12;
    if (*playsize == 5) {
      menu[count].sprite[0] = gra_text2sprite("A5 (42x28)", DroidSansBold_ttf, DroidSansBold_ttf_len, 20, 0xff8400u, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite("A5 (42x28)", DroidSansBold_ttf, DroidSansBold_ttf_len, 20, 0xff8400u, -1, 0xff, -1);
    } else {
      menu[count].sprite[0] = gra_text2sprite("A5 (42x28)", DroidSans_ttf, DroidSans_ttf_len, 20, 0, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite("A5 (42x28)", DroidSans_ttf, DroidSans_ttf_len, 20, 0x0000ffu, -1, 0xff, -1);
    }
    count++;
    /* A4 */
    menu[count].clickid = 4;
    menu[count].propxpos = 80;
    menu[count].propypos = 12;
    if (*playsize == 4) {
      menu[count].sprite[0] = gra_text2sprite("A4 (58x42)", DroidSansBold_ttf, DroidSansBold_ttf_len, 20, 0xff8400u, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite("A4 (58x42)", DroidSansBold_ttf, DroidSansBold_ttf_len, 20, 0xff8400u, -1, 0xff, -1);
    } else {
      menu[count].sprite[0] = gra_text2sprite("A4 (58x42)", DroidSans_ttf, DroidSans_ttf_len, 20, 0, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite("A4 (58x42)", DroidSans_ttf, DroidSans_ttf_len, 20, 0x0000ffu, -1, 0xff, -1);
    }
    count++;
    /* VS */
    menu[count].flags = UI_MENU_FLAG_XCENTER;
    menu[count].propxpos = 50;
    menu[count].propypos = 51;
    menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_VS), DroidSans_ttf, DroidSans_ttf_len, 27, 0x787878u, -1, 0xff, -1);
    count++;
    /* */
    for (i = 0; i < 2; i++) {
      int ai_player_pos = 44;
      /* Player A and B */
      menu[count].flags = UI_MENU_FLAG_XCENTER;
      menu[count].propypos = 35;
      if (i == 0) {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_PLAYER_A), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0x282828u, -1, 0xff, -1);
        menu[count].propxpos = 25;
      } else {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_PLAYER_B), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0x282828u, -1, 0xff, -1);
        menu[count].propxpos = 75;
      }
      count++;
      /* Player human */
      menu[count].flags = UI_MENU_FLAG_XCENTER;
      menu[count].propypos = ai_player_pos;
      ai_player_pos += 6;
      if (i == 0) {
        menu[count].clickid = 10;
        menu[count].propxpos = 25;
      } else {
        menu[count].clickid = 20;
        menu[count].propxpos = 75;
      }
      if (p[i] == 0) {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_HUMAN), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0xff8400u, -1, 0xff, -1);
        menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_HUMAN), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0xff8400u, -1, 0xff, -1);
      } else {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_HUMAN), DroidSans_ttf, DroidSans_ttf_len, 26, 0, -1, 0xff, -1);
        menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_HUMAN), DroidSans_ttf, DroidSans_ttf_len, 26, 0x0000ffu, -1, 0xff, -1);
      }
      count++;
      /* player Dumbo */
      menu[count].propypos = ai_player_pos;
      ai_player_pos += 6;
      menu[count].flags = UI_MENU_FLAG_XCENTER;
      menu[count].propxpos = 25 + (i * 50);
      menu[count].clickid = 11 + (i * 10);
      menu[count].hint = hintgen(lang_msg(MSG_DUMBO_HINT));
      if (p[i] == PLAYER_DUMBO) {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_DUMBO), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0xff8400u, -1, 0xff, -1);
        menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_DUMBO), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0xff8400u, -1, 0xff, -1);
      } else {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_DUMBO), DroidSans_ttf, DroidSans_ttf_len, 26, 0, -1, 0xff, -1);
        menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_DUMBO), DroidSans_ttf, DroidSans_ttf_len, 26, 0x0000ffu, -1, 0xff, -1);
      }
      count++;
      /* player Karl */
#if ENABLE_KARL > 0
      menu[count].propypos = ai_player_pos;
      ai_player_pos += 6;
      menu[count].flags = UI_MENU_FLAG_XCENTER;
      menu[count].propxpos = 25 + (i * 50);
      menu[count].clickid = 12 + (i * 10);
      menu[count].hint = hintgen(lang_msg(MSG_KARL_HINT));
      if (p[i] == PLAYER_KARL) {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_KARL), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0xff8400u, -1, 0xff, -1);
        menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_KARL), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0xff8400u, -1, 0xff, -1);
      } else {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_KARL), DroidSans_ttf, DroidSans_ttf_len, 26, 0, -1, 0xff, -1);
        menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_KARL), DroidSans_ttf, DroidSans_ttf_len, 26, 0x0000ffu, -1, 0xff, -1);
      }
      count++;
#endif
      /* player Tigran */
      menu[count].propypos = ai_player_pos;
      ai_player_pos += 6;
      menu[count].flags = UI_MENU_FLAG_XCENTER;
      menu[count].propxpos = 25 + (i * 50);
      menu[count].clickid = 13 + (i * 10);
      menu[count].hint = hintgen(lang_msg(MSG_TIGRAN_HINT));
      if (p[i] == PLAYER_TIGRAN) {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_TIGRAN), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0xff8400u, -1, 0xff, -1);
        menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_TIGRAN), DroidSansBold_ttf, DroidSansBold_ttf_len, 26, 0xff8400u, -1, 0xff, -1);
      } else {
        menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_TIGRAN), DroidSans_ttf, DroidSans_ttf_len, 26, 0, -1, 0xff, -1);
        menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_TIGRAN), DroidSans_ttf, DroidSans_ttf_len, 26, 0x0000ffu, -1, 0xff, -1);
      }
      count++;
      /* START GAME */
      if (i == 0) {
        menu[count].propxpos = 25;
        menu[count].clickid = 150;
      } else {
        menu[count].propxpos = 75;
        menu[count].clickid = 151;
      }
      menu[count].propypos = 72;
      menu[count].flags = UI_MENU_FLAG_XCENTER;
      menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_STARTGAME), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0, -1, 0xff, -1);
      menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_STARTGAME), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0x0000ffu, -1, 0xff, -1);
      count++;
    }
    /* back to main menu */
    menu[count].clickid = 200;
    menu[count].propxpos = 50;
    menu[count].propypos = 92;
    menu[count].flags = UI_MENU_FLAG_XCENTER;
    menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_BACK_TO_MAIN), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0, -1, 0xff, -1);
    menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_BACK_TO_MAIN), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0x0000ffu, -1, 0xff, -1);
    count++;
    /* compute menu */
    menupos = ui_menu(menu, count, 1, MENU_MAXWIDTH, MENU_MAXHEIGHT);
    if (sound) gra_playwavgz(sfx_click_wav_gz, sfx_click_wav_gz_len);

    /* interpret menu choice */
    if ((menupos == 200) || (menupos == UI_MENU_BACK)) return(UI_MENU_BACK);
    if (menupos == UI_MENU_EXIT) return(UI_MENU_EXIT);
    if ((menupos >= 4) && (menupos <= 7)) *playsize = menupos;
    if (menupos == 10) p[0] = PLAYER_HUMAN; /* p1 = human */
    if (menupos == 11) p[0] = PLAYER_DUMBO; /* p1 = dumbo */
    if (menupos == 12) p[0] = PLAYER_KARL;  /* p1 = karl */
    if (menupos == 13) p[0] = PLAYER_TIGRAN;/* p1 = tigran */
    if (menupos == 20) p[1] = PLAYER_HUMAN; /* p1 = human */
    if (menupos == 21) p[1] = PLAYER_DUMBO; /* p1 = dumbo */
    if (menupos == 22) p[1] = PLAYER_KARL;  /* p2 = karl */
    if (menupos == 23) p[1] = PLAYER_TIGRAN;/* p2 = tigran */
    if ((menupos == 150) || (menupos == 151)) return(menupos); /* PLAY GAME */
  }
}


static int menu_about(int sound) {
  struct ui_menu_description_t menudesc[8];
  int count = 0;
  int menupos;
  int menuwidth;
  const char *about = lang_msg(MSG_ABOUT_LONG);

  /* zero out the menudesc array */
  memset(menudesc, 0, sizeof(menudesc));

  /* get window's width */
  menuwidth = getmenuwidth();

  /* frontpic goes first, so it is never rendered over a label */
  menudesc[count].propxpos = 0;
  menudesc[count].propypos = 100;
  menudesc[count].sprite[0] = gra_loadgzbmp(frontpic_bmp_gz, frontpic_bmp_gz_len);
  count++;
  /* about content (follows the frontpic right edge) */
  menudesc[count].sprite[0] = gra_text2sprite(about, DroidSans_ttf, DroidSans_ttf_len, 21, 0, -1, 210, menuwidth - gra_getspritewidth(menudesc[count - 1].sprite[0]));
  menudesc[count].flags = UI_MENU_FLAG_XRELPOS;
  menudesc[count].propxpos = 0;
  menudesc[count].propypos = 59;
  count++;
  /* OpenKropki title */
  menudesc[count].sprite[0] = gra_text2sprite("OpenKropki v" PVER, DroidSansBold_ttf, DroidSansBold_ttf_len, 40, 0, -1, 0xff, -1);
  menudesc[count].propxpos = 50;
  menudesc[count].propypos = 16;
  menudesc[count].flags = UI_MENU_FLAG_XCENTER;
  count++;
  /* copyright subtitle */
  menudesc[count].sprite[0] = gra_text2sprite("Copyright © " PDATE " Mateusz Viste", DroidSansBold_ttf, DroidSansBold_ttf_len, 16, 0, -1, 0x60, -1);
  menudesc[count].propxpos = 50;
  menudesc[count].propypos = 0;
  menudesc[count].flags = UI_MENU_FLAG_XCENTER | UI_MENU_FLAG_YRELPOS;
  count++;
  /* */
  menudesc[count].clickid = 1;
  menudesc[count].propxpos = 50;
  menudesc[count].propypos = 92;
  menudesc[count].flags = UI_MENU_FLAG_XCENTER;
  menudesc[count].sprite[0] = gra_text2sprite(lang_msg(MSG_BACK_TO_MAIN), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0, -1, 0xff, -1);
  menudesc[count].sprite[1] = gra_text2sprite(lang_msg(MSG_BACK_TO_MAIN), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0x0000ffu, -1, 0xff, -1);
  count++;

  /* compute menu */
  menupos = ui_menu(menudesc, count, 1, MENU_MAXWIDTH, MENU_MAXHEIGHT);
  if (sound) gra_playwavgz(sfx_click_wav_gz, sfx_click_wav_gz_len);

  if (menupos == 1) menupos = UI_MENU_BACK;
  return(menupos);
}


#define SETMENU_LANG 10
#define SETMENU_SHADEBASES 20
#define SETMENU_SOUND 30

static int menu_settings(int sound, int shadebases, int lang) {
  int menupos;
  int count = 0;
  struct ui_menu_description_t menu[8];
  const int menuystart = 10;
  const int menustep = 13;
  char s[256];

  /* zero out the menudesc array */
  memset(menu, 0, sizeof(menu));

  /* frontpic goes first, so it is never rendered over a label */
  menu[count].propxpos = 0;
  menu[count].propypos = 100;
  menu[count].sprite[0] = gra_loadgzbmp(frontpic_bmp_gz, frontpic_bmp_gz_len);
  count++;
  /* OpenKropki title */
  menu[count].sprite[0] = gra_text2sprite("OpenKropki v" PVER, DroidSansBold_ttf, DroidSansBold_ttf_len, 40, 0, -1, 0xff, -1);
  menu[count].propxpos = 50;
  menu[count].propypos = 16;
  menu[count].flags = UI_MENU_FLAG_XCENTER;
  count++;
  /* copyright subtitle */
  menu[count].sprite[0] = gra_text2sprite("Copyright © " PDATE " Mateusz Viste", DroidSansBold_ttf, DroidSansBold_ttf_len, 16, 0, -1, 0x60, -1);
  menu[count].propxpos = 50;
  menu[count].propypos = 0;
  menu[count].flags = UI_MENU_FLAG_XCENTER | UI_MENU_FLAG_YRELPOS;
  count++;
  /* language */
  snprintf(s, sizeof(s), lang_msg(MSG_LANGUAGE), lang_name(lang));
  menu[count].sprite[0] = gra_text2sprite(s, DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 0xff, -1);
  menu[count].sprite[1] = gra_text2sprite(s, DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0x0000ffu, -1, 0xff, -1);
  menu[count].clickid = SETMENU_LANG;
  menu[count].propxpos = 50;
  menu[count].propypos = menuystart + (menustep * count);
  menu[count].flags = UI_MENU_FLAG_XCENTER;
  count++;
  /* render shaded bases */
  snprintf(s, sizeof(s), lang_msg(MSG_SHADEBASES), (shadebases)?lang_msg(MSG_YES):lang_msg(MSG_NO));
  menu[count].sprite[0] = gra_text2sprite(s, DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 0xff, -1);
  menu[count].sprite[1] = gra_text2sprite(s, DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0x0000ffu, -1, 0xff, -1);
  menu[count].clickid = SETMENU_SHADEBASES;
  menu[count].propxpos = 50;
  menu[count].propypos = menuystart + (menustep * count);
  menu[count].flags = UI_MENU_FLAG_XCENTER;
  count++;
  /* sound */
  snprintf(s, sizeof(s), lang_msg(MSG_SOUND), (sound)?lang_msg(MSG_YES):lang_msg(MSG_NO));
  menu[count].sprite[0] = gra_text2sprite(s, DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0, -1, 0xff, -1);
  menu[count].sprite[1] = gra_text2sprite(s, DroidSansBold_ttf, DroidSansBold_ttf_len, 30, 0x0000ffu, -1, 0xff, -1);
  menu[count].clickid = SETMENU_SOUND;
  menu[count].propxpos = 50;
  menu[count].propypos = menuystart + (menustep * count);
  menu[count].flags = UI_MENU_FLAG_XCENTER;
  count++;
  /* back to main menu */
  menu[count].clickid = 0xff;
  menu[count].propxpos = 50;
  menu[count].propypos = 92;
  menu[count].flags = UI_MENU_FLAG_XCENTER;
  menu[count].sprite[0] = gra_text2sprite(lang_msg(MSG_BACK_TO_MAIN), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0, -1, 0xff, -1);
  menu[count].sprite[1] = gra_text2sprite(lang_msg(MSG_BACK_TO_MAIN), DroidSansBold_ttf, DroidSansBold_ttf_len, 22, 0x0000ffu, -1, 0xff, -1);
  count++;

  /* compute menu */
  menupos = ui_menu(menu, count, 1, MENU_MAXWIDTH, MENU_MAXHEIGHT);
  if (sound) gra_playwavgz(sfx_click_wav_gz, sfx_click_wav_gz_len);

  if (menupos == 1) menupos = UI_MENU_BACK;
  return(menupos);
}


/* wait for AI thread to terminate, if it is pending and displays a message
 * about this, unless nomsg is non-zero. */
static void finish_ai_thread(struct rozgrywka_t *r) {
  if ((r != NULL) && (r->ai_thread != NULL)) {
    ui_changecursor(UI_CURSOR_WAIT);
    if (r->ai_in_progress != 0) ui_throwmsg(lang_msg(MSG_WAIT_FOR_AI_ENGINE));
    SDL_WaitThread(r->ai_thread, NULL);
    ui_changecursor(UI_CURSOR_NORMAL);
    r->ai_thread = NULL;
  }
}


static void rozgrywka_init(struct rozgrywka_t *r) {
  memset(r, 0, sizeof(struct rozgrywka_t));
  r->kratkasize = INITIAL_KRATKA_SIZE;
  /* reset AIs that need a reset */
  ai_karl_reset();
  ai_tigran_reset();
}


static void resume_save(const struct kropki_game *g) {
  char *okps;
  okps = kropki_struct2okp(g);
  if (okps == NULL) {
    printf("resume_save fail: kropki_struct2okp() returned NULL!\n");
  } else {
    prefs_save("resume.okp", okps);
    free(okps);
  }
}


static void resume_load(struct rozgrywka_t *r) {
  char *okps;
  if (r->game != NULL) {
    printf("ERROR: resume_load() called while r->game != NULL\n");
    return;
  }
  okps = prefs_load("resume.okp");
  if (okps != NULL) r->game = kropki_okp2struct(okps);
  free(okps);
  /* */
  if (r->game == NULL) return;
  printf("loaded a resume state of a %dx%d game, lastplayer=%d, nextplayer=%d, scores=%d:%d playablefields=%d\n", r->game->field_width, r->game->field_height, r->game->lastplayer, r->game->nextplayer, r->game->score[0], r->game->score[1], r->game->playablefields);
  /* ignore the resume state if game is no longer playable */
  if (r->game->nextplayer == 0) {
    printf("resume state appears to be an ended game -> ignoring it\n");
    kropki_freegame(r->game);
    r->game = NULL;
  } else { /* else copy the scores and resolve player types */
    int i;
    for (i = 0; i < 2; i++) {
      r->p[i] = txtid2playertype(r->game->playername[i]);
      r->points[i] = r->game->score[i];
    }
  }
}


int main(void) {
  struct rozgrywka_t rozgrywka;
  int exitflag = 0;
  int sound;
  int shadebases;
  int lang = LANG_EN;

  ui_init(WIN_WIDTH, WIN_HEIGHT);

  /* fetch preferences */
  {
    lang = prefs_loadint("language", LANG_EN);
    if ((lang >= LANG_LAST) || (lang < 0)) lang = LANG_EN;
    sound = prefs_loadint("sound", 1);
    shadebases = prefs_loadint("shadebases", 1);
  }

  /* init language module */
  lang_init(lang);

  /* seed the random generator, AIs often rely on rand() */
  srand(time(NULL));

  rozgrywka_init(&rozgrywka);

  /* load the last "resume" gamestate (if available) */
  resume_load(&rozgrywka);

  while (exitflag == 0) {
    int menupos;

    menupos = menu_main(&rozgrywka, sound);

    if ((menupos == MAINMENU_QUIT) || (menupos == UI_MENU_BACK) || (menupos == UI_MENU_EXIT)) {
      break;
    } else if (menupos == MAINMENU_NEWGAME) {
      int w, h, choice, playsize = 6;
      int i;
      enum playertype_t p[2] = {PLAYER_HUMAN, PLAYER_TIGRAN};
      /* playfield size menu */
      choice = menu_playconf(sound, &playsize, p);
      /* translate playfield size to width and height */
      if (playsize == 4) { /* A4 */
        w = 58;
        h = 42;
      } else if (playsize == 5) {
        w = 42;
        h = 28;
      } else if (playsize == 6) {
        w = 28;
        h = 20;
      } else if (playsize == 7) {
        w = 20;
        h = 14;
      }
      if (choice == UI_MENU_EXIT) {
        exitflag = 1;
        continue;
      } else if (choice == UI_MENU_BACK) {
        continue;
      } else if ((choice != 150) && (choice != 151)) { /* ignore anything that is not a play action */
        continue;
      }
      /* make sure no AI thread is running before freeing a previous game! */
      finish_ai_thread(&rozgrywka);
      kropki_freegame(rozgrywka.game);
      /* */
      rozgrywka_init(&rozgrywka);
      rozgrywka.game = kropki_newgame(w, h, 1 + choice - 150);
      for (i = 0; i < 2; i++) {
        rozgrywka.p[i] = p[i];
        kropki_setplayername(rozgrywka.game, i, playertype2txtid(p[i]));
      }
    } else if (menupos == MAINMENU_SETTINGS) {
      for (;;) {
        int choice = menu_settings(sound, shadebases, lang);
        if (choice == UI_MENU_EXIT) {
          exitflag = 1;
          break;
        } else if (choice == SETMENU_LANG) {
          int choice = menu_lang(sound);
          if (choice == UI_MENU_EXIT) {
            exitflag = 1;
          } else if (choice < LANG_LAST) {
            lang = choice;
            lang_init(lang);
            prefs_saveint("language", lang);
          }
          continue;
        } else if (choice == SETMENU_SOUND) {
          sound ^= 1;
          prefs_saveint("sound", sound);
          continue;
        } else if (choice == SETMENU_SHADEBASES) {
          shadebases ^= 1;
          prefs_saveint("shadebases", shadebases);
          continue;
        }
        break;
      }
      continue;
    } else if (menupos == MAINMENU_ABOUT) {
      int choice = menu_about(sound);
      if (choice == UI_MENU_EXIT) exitflag = 1;
      continue;
    } else if (menupos == MAINMENU_RESUME) {
      /* nothing to do */
    } else {
      /* abnormal choice, let's play it safe and ignore it */
      continue;
    }
    centerscrollvector(&rozgrywka);

    if (ui_setparams(rozgrywka.game->field_width, rozgrywka.game->field_height, rozgrywka.kratkasize, 2, 0xA0A0A0, COLORPLAYER1, COLORPLAYER2, 0xFEFEFE, rozgrywka.kratkasize/10, rozgrywka.kratkasize/4) != 0) return(0);

    while (exitflag == 0) {
      struct ui_event_t event;
      const char *msg = NULL;
      char msgcustom[256];
      int msgplayercolor = 0; /* 0=no player  1=player A  2=player B */

      /* if AI thinks, then check if results are available (but not too soon) */
      if ((rozgrywka.ai_thread != NULL) && (rozgrywka.ai_in_progress == 0)) {
        if (sound) {
          if (rozgrywka.game->lastplayer == 1) {
            gra_playwavgz(sfx_pop1_wav_gz, sfx_pop1_wav_gz_len);
          } else {
            gra_playwavgz(sfx_pop3_wav_gz, sfx_pop3_wav_gz_len);
          }
        }
        finish_ai_thread(&rozgrywka);
        /* save game for a possible "resume" in the future */
        resume_save(rozgrywka.game);
      }

      /* if points changed, play the 'collected' sound (check only if no AI processes the playfield) */
      if (rozgrywka.ai_thread == NULL) {
        if ((rozgrywka.game->score[0] != rozgrywka.points[0]) || (rozgrywka.game->score[1] != rozgrywka.points[1])) {
          if (sound) gra_playwavgz(sfx_collect_wav_gz, sfx_collect_wav_gz_len);
          rozgrywka.points[0] = rozgrywka.game->score[0];
          rozgrywka.points[1] = rozgrywka.game->score[1];
        }
      }
      /* check whether the game ended (do not check if AI thinks) */
      if ((rozgrywka.ai_thread == NULL) && (rozgrywka.game->nextplayer == 0)) {
        int winnerid = -1;
        if (rozgrywka.points[0] > rozgrywka.points[1]) {
          winnerid = 0;
        } else if (rozgrywka.points[0] < rozgrywka.points[1]) {
          winnerid = 1;
        } else { /* remis */
          if ((rozgrywka.endgamesfxplayed == 0) && (sound)) gra_playwavgz(sfx_death_wav_gz, sfx_death_wav_gz_len);
          msgplayercolor = 0;
          msg = lang_msg(MSG_NOBWINS);
        }
        if (winnerid >= 0) {
          snprintf(msgcustom, sizeof(msgcustom), lang_msg(MSG_HEWINS), lang_msg(playertype2name(rozgrywka.p[winnerid])));
          msg = msgcustom;
          if (rozgrywka.p[winnerid] == PLAYER_HUMAN) {
            if ((rozgrywka.endgamesfxplayed == 0) && (sound)) gra_playwavgz(sfx_winfantasia_wav_gz, sfx_winfantasia_wav_gz_len);
          } else {
            if ((rozgrywka.endgamesfxplayed == 0) && (sound)) gra_playwavgz(sfx_death_wav_gz, sfx_death_wav_gz_len);
          }
          msgplayercolor = winnerid + 1;
        }
        rozgrywka.endgamesfxplayed = 1; /* used to avoid playing 'win' sfx many times */
      } else if (rozgrywka.game->nextplayer > 0) { /* game is in progress: display who moves now */
        msgplayercolor = rozgrywka.game->nextplayer;
        if (rozgrywka.p[rozgrywka.game->nextplayer - 1] == PLAYER_HUMAN) {
          msg = lang_msg(MSG_YOURTURN);
        } else {
          msg = lang_msg(MSG_AITHINKS);
        }
      }
      redraw_screen(&rozgrywka, msg, msgplayercolor, shadebases);

      /* is it time for AI to play? */
      { int i;
        for (i = 0; i < 2; i++) {
          if ((rozgrywka.ai_thread == NULL) && (rozgrywka.p[rozgrywka.game->nextplayer - 1] != PLAYER_HUMAN)) { /* AI plays */
            rozgrywka.ai_in_progress = 1; /* this is reset by the thread once job is done */
            rozgrywka.ai_thread = SDL_CreateThread(ai_caller, "openkropki_ai", &rozgrywka);
            break;
          }
        }
      }

      /* wait for an event */
      ui_getevent(&event, 0);

      if (event.type == Event_QUIT) {
        exitflag = 1;
        break;
      } else if ((event.type == Event_CLICK) && (rozgrywka.ai_thread == NULL) && (rozgrywka.p[rozgrywka.game->nextplayer - 1] == PLAYER_HUMAN) && (event.button >= 1) && (event.button <= 2)) { /* check clicks only if it's my turn to play and only if the click was a left or right click (not middle) */
        if (event.button == 1) {
          int fieldx, fieldy;
          graph_mousemap(event.x, event.y, &fieldx, &fieldy, rozgrywka.scrollvector);
          /* does click points to a field? */
          if ((fieldx >= 0) && (fieldy >= 0)) {
            if (kropki_getpos(rozgrywka.game, fieldx, fieldy) != 0) { /* is this field actually empty? */
              if (sound) gra_playwavgz(sfx_wrong_wav_gz, sfx_wrong_wav_gz_len); /* bad move */
            } else {
              if (sound) {
                if (rozgrywka.game->nextplayer == 1) {
                  gra_playwavgz(sfx_pop1_wav_gz, sfx_pop1_wav_gz_len);
                } else {
                  gra_playwavgz(sfx_pop3_wav_gz, sfx_pop3_wav_gz_len);
                }
              }
              kropki_setpos(rozgrywka.game, fieldx, fieldy, rozgrywka.game->nextplayer, 0);
              /* save game for a possible "resume" in the future */
              resume_save(rozgrywka.game);
            }
          }
        } else if (event.button == 2) {   /* right click: center field around last point */
          centerscrollvector(&rozgrywka);
        }
      } else if (event.type == Event_KEYPRESS) {
        if (event.button == 0x1B) break; /* ESC */
      } else if (event.type == Event_SWEEP) {
        int winw, winh;
        gra_getwinsize(&winw, &winh);
        /* printf("SWEEP WITH BUTTON %d BY VECTOR (%d,%d)\n", z, x, y); */
        rozgrywka.scrollvector[0] -= event.x;
        rozgrywka.scrollvector[1] -= event.y;
        /* do not allow wandering too far: playfield must always stay visible */
        if (rozgrywka.scrollvector[0] > winw - 16) rozgrywka.scrollvector[0] = winw - 16;
        if (rozgrywka.scrollvector[1] > winh - 16) rozgrywka.scrollvector[1] = winh - 16;
        if (rozgrywka.scrollvector[0] < -16 - (rozgrywka.game->field_width * rozgrywka.kratkasize)) rozgrywka.scrollvector[0] = -16 - (rozgrywka.game->field_width * rozgrywka.kratkasize);
        if (rozgrywka.scrollvector[1] < 12 - (rozgrywka.game->field_height * rozgrywka.kratkasize)) rozgrywka.scrollvector[1] = 12 - (rozgrywka.game->field_height * rozgrywka.kratkasize);
      } else if ((event.type == Event_SCROLL) || ((event.type == Event_CLICK) && (event.button == 3))) {
        int mouseptrx, mouseptry, oldkratkasize;
        int step = 1 + (rozgrywka.kratkasize / 32);
        /* compute mouse pointer position relative to field, in kratkasize * 1000 units */
        ui_getmousepos(&mouseptrx, &mouseptry);
        mouseptrx -= rozgrywka.scrollvector[0];
        mouseptry -= rozgrywka.scrollvector[1];
        mouseptrx *= 1000;
        mouseptry *= 1000;
        oldkratkasize = rozgrywka.kratkasize; /* remember what the kratkasize unit is */
        /* change kratkasize accordingly to the zoom event */
        if (event.type == Event_SCROLL) {
          if (event.y > 0) rozgrywka.kratkasize += step;
          if (event.y < 0) rozgrywka.kratkasize -= step;
        } else { /* else it's a middle click (reset zoom) */
          rozgrywka.kratkasize = INITIAL_KRATKA_SIZE;
        }
        /* enforce upper and lower zoom boundaries */
        if (rozgrywka.kratkasize > 512) rozgrywka.kratkasize = 512;
        if (rozgrywka.kratkasize < 4) rozgrywka.kratkasize = 4;
        printf("new kratka size: %d px\n", rozgrywka.kratkasize);
        ui_setparams(rozgrywka.game->field_width, rozgrywka.game->field_height, rozgrywka.kratkasize, 2, 0xA0A0A0, COLORPLAYER1, COLORPLAYER2, 0xFEFEFE, rozgrywka.kratkasize/10, rozgrywka.kratkasize/4);
        /* adjust scroll vectors so the cursor is still at the same place it was before */
        rozgrywka.scrollvector[0] += (((mouseptrx / rozgrywka.kratkasize) - (mouseptrx / oldkratkasize)) * rozgrywka.kratkasize) / 1000;
        rozgrywka.scrollvector[1] += (((mouseptry / rozgrywka.kratkasize) - (mouseptry / oldkratkasize)) * rozgrywka.kratkasize) / 1000;
      } else if (event.type != Event_NONE) {
        /* puts("got an unknown event"); */
      }

    }
  }

  /* clean up and quit */
  finish_ai_thread(&rozgrywka);
  kropki_freegame(rozgrywka.game);
  gra_close();

  return(0);
}
