//#define LANG_RUS
#define NDEBUG
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

// sprintf is too heavy
int itoa(char* dst, int number)
{
	int result = 0;
	if (number < 0) {
		result++;
		*dst++ = '-';
		number = -number;
	}
	char digits[16];
	int ndigits = 0;
	do {
		digits[ndigits++] = number % 10;
		number /= 10;
	} while (number);
	result += ndigits;
	for (; ndigits--; )
		*dst++ = digits[ndigits] + '0';
	return result;
}

#include "gr-draw.h"
#include "board.h"
#include "player.h"
#include "buttons.h"
#include "sysproc.h"

char *strskipref(char *s1, char *s2)
{
  int L = strlen(s2);
  if (strncmp(s1, s2, L) == 0) return s1 + L;
  else return 0;
}

class TPlayArray
{
public:
  TPlayArray(int d = 10) : play(0), nplay(0), mplay(0) {}
  ~TPlayArray() {Clear();}

  void Clear();
  void Add(const PlayWrite &pl);
  PlayWrite &operator[](int i) {return play[i];}
  int GetNPlay() const {return nplay;}
#ifndef NO_FILES
  int OpenFile(const char *name, int kind);
  int MsgOpenFile(const char *name, int kind);
  int SaveFile(const char *name, int num, int kind);
  int MsgSaveFile(const char *name, int num, int kind);
#endif
  void Del(int n);
protected:
  void Extend(int n = -1);

  PlayWrite *play;
  int nplay, mplay;
protected:
#ifndef NO_FILES
  static const char *const search[];
  static int AnalizeKind(FILE *f, const int w[], const char *smb = 0);
  static int ReadFileTo(FILE *f, char c);
  static int ReadFileTo(FILE *f, const char *c);
  static int ReadTheText(FILE *f, const char *s);
#endif
};

#ifdef LANG_RUS //Pending Russian Translations
    #define CHECKERS_CANT_OPEN_STR "\n��誨: �� ���� ������ 䠩� \"%s\".\n"
    #define CHECKERS_FILE_EMPTY_STR "\n��誨: ���� \"%s\" ����.\n"
    #define CHECKERS_INVALID_FILE_STR "\n��誨: ���� \"%s\" �訡���.\n"
    #define FILE_WRONG_TYPE_STR "\n��誨: ���� \"%s\" ����� ������ ⨯.\n"
    #define ERROR_OPENING_FILE_STR "\n��誨: �訡�� ������ 䠩�� \"%s\".\n"
    #define FILE_HAS_NO_GAMES_STR "\n��誨: ���� \"%s\" �� ᮤ�ন� ���.\n"
    #define CANT_OPEN_FILE_STR "\n��誨: �� ���� ������ 䠩� \"%s\" �� ������.\n"
    #define CHECKERS_STR "��誨"
    #define FILE_SAVED_STR "\n��誨: ���� \"%s\" ��࠭�.\n"
    #define ERROR_SAVING_FILE_STR "\n��誨: �訡�� ��࠭���� 䠩�� \"%s\".\n"
    #define NOT_ENOUGH_MEM_STR "\n��誨: �� �����筮 ����� ��� ��࠭���� 䠩�� \"%s\".\n"
    #define KIND_EQ2 "kind = 2\n"
    #define END_STR "[end]"
    #define PLAYV11_STR "[play_v1.1]"
    #define CHECKERS_PLAY_STR "checkers-play:text\n"
    #define CHECKERS_BIN_STR "checkers-play:bin_1.0\n"
    #define BLUE_STR "blue: "
    #define RED_STR "red: "
    #define INPUT_STR "input"
    #define COMPUTER_STR "computer"
    #define NEW_GAME_STR "new game"
    #define LIST_STR "list"
    #define DELETE_STR "delete"
    #define CLEAR_STR "clear"
    #define SAVE_STR "save"
    #define ROTATE_BOARD_STR "rotate board"
    #define EXIT_STR "exit"
    #define CHECKERS_INVALID_STR "Checkers: Invalid key %s\n"

#ifndef NO_FILES
const char *const TPlayArray::search[] =
  {"checkers-", "play:", "text", "bin_1.0\n", "history_", "1.0", "1.1"};
#endif

#else /*For all other languages except RUS*/
    #define CHECKERS_CANT_OPEN_STR "\nCheckers: Can't open the file \"%s\".\n"
    #define CHECKERS_FILE_EMPTY_STR "\nCheckers: The file \"%s\" is empty.\n"
    #define CHECKERS_INVALID_FILE_STR "\nCheckers: Invalid file \"%s\".\n"
    #define FILE_WRONG_TYPE_STR "\nCheckers: The file \"%s\" has the wrong type.\n"
    #define ERROR_OPENING_FILE_STR "\nCheckers: Error opening the file \"%s\".\n"
    #define FILE_HAS_NO_GAMES_STR "\nCheckers: File \"%s\" doesn't contain games.\n"
    #define CANT_OPEN_FILE_STR "\nCheckers: Can't open the file \"%s\" to write.\n"
    #define NOT_ENOUGH_MEM_STR "\nCheckers: Not enough memory to save the file \"%s\".\n"
    #define ERROR_SAVING_FILE_STR "\nCheckers: Error saving the file \"%s\".\n"
    #define FILE_SAVED_STR "\nCheckers: File \"%s\" saved.\n"
    #define CHECKERS_STR "Checkers"
    #define KIND_EQ2 "kind = 2\n"
    #define END_STR "[end]"
    #define PLAYV11_STR "[Play_v1.1]"
    #define CHECKERS_PLAY_STR "checkers-play:text\n"
    #define CHECKERS_BIN_STR "checkers-play:bin_1.0\n"
    #define BLUE_STR "blue: "
    #define RED_STR "red: "
    #define INPUT_STR "input"
    #define COMPUTER_STR "computer"
    #define NEW_GAME_STR "new game"
    #define LIST_STR "list"
    #define DELETE_STR "delete"
    #define CLEAR_STR "clear"
    #define SAVE_STR "save"
    #define ROTATE_BOARD_STR "rotate board"
    #define EXIT_STR "exit"
    #define CHECKERS_INVALID_STR "Checkers: Invalid key %s\n"

#ifndef NO_FILES
const char *const TPlayArray::search[] =
     {"checkers-", "play:", "text", "bin_1.0\n", "history_", "1.0", "1.1"};
#endif

#endif
void TPlayArray::Clear()
{
  if (play) delete[] play;
  play = 0; nplay = 0; mplay = 0;
}

void TPlayArray::Extend(int n)
{
  if (n < 0) n = nplay + 1;
  if (mplay < n)
  {
    PlayWrite *p_old = play;
    int m_old = mplay;
    mplay = n * 2;
    play = new PlayWrite[mplay];
    if (p_old)
    {
      for (int i = 0; i < m_old; i++) play[i] = p_old[i];
      delete[] p_old;
    }
  }
}

void TPlayArray::Add(const PlayWrite &pl)
{
  Extend();
  play[nplay] = pl;
  nplay++;
}

#ifndef NO_FILES
int TPlayArray::AnalizeKind(FILE *f, const int w[], const char *smb)
{
  int i, L, was1 = 1;
  unsigned char ch;
  int fnd[NELEM(search)];
  for (i = 0; i < NELEM(search); i++) fnd[i] = 1;
  for (L = 0; was1; L++)
  {
    was1 = 0;
    if (fread(&ch, 1, 1, f) != 1) return (L == 0) ? -2 : -1;
    for (i = 0; w[i] >= 0; i++) if (fnd[i])
    {
      if (tolower(ch) != tolower(search[w[i]][L])) fnd[i] = 0;
      else
      {
        was1 = 1;
        if (search[w[i]][L+1] == 0)
        {
          if (smb && smb[0] && fread(&ch, 1, 1, f) == 1)
          {
            ungetc(ch, f);
            if (strchr(smb, tolower(ch)) || isalpha(ch) && strchr(smb, toupper(ch)))
            {
              break;
            }
          }
          return i;
        }
      }
    }
  }
  return -10 - L;
}

int TPlayArray::ReadFileTo(FILE *f, char c)
{
  char ch;
  do
  {
    if (fread(&ch, 1, 1, f) != 1) return 0;
  }
  while(ch != c);
  return 1;
}

int TPlayArray::ReadFileTo(FILE *f, const char *c)
{
  char ch;
  do
  {
    if (fread(&ch, 1, 1, f) != 1) return 0;
  }
  while(!strchr(c, ch));
  return 1;
}

int TPlayArray::ReadTheText(FILE *f, const char *s)
{
  char ch;
  while (*s)
  {
    if (fread(&ch, 1, 1, f) != 1 || ch != *s) return 0;
    s++;
  }
  return 1;
}

int TPlayArray::OpenFile(const char *name, int kind)
{
  int k_fnd = 0;
  FILE *f = fopen(name, "rb");
  if (!f) return -1;
  if (kind == -1)
  {
    const int w[] = {0, -1};
    int r = AnalizeKind(f, w);
    if (r >= 0) {kind = w[r]; k_fnd = 1;}
    else if (r == -11) {kind = 3; k_fnd = 3;}
    else {fclose(f); return (kind == -2) ? -2 : -10;}
  }
  if (kind == 0)
  {
    if (k_fnd < 1 && !ReadFileTo(f, '-')) {fclose(f); return -10;}
    k_fnd = 1;
    const int w[] = {1, 4, -1};
    int r = AnalizeKind(f, w);
    if (r >= 0) {kind = w[r]; k_fnd = 2;}
    else {fclose(f); return -10;}
  }
  if (kind == 1)
  {
    if (k_fnd < 2 && !ReadFileTo(f, ':')) {fclose(f); return -10;}
    k_fnd = 2;
    const int w[] = {2, 3, -1};
    int r = AnalizeKind(f, w);
    if (r >= 0) {kind = w[r]; k_fnd = 3;}
    else {fclose(f); return -10;}
  }
  if (kind == 4)
  {
    if (k_fnd < 2 && !ReadFileTo(f, '_')) {fclose(f); return -10;}
    k_fnd = 2;
    const int w[] = {5, 6, -1};
    int r = AnalizeKind(f, w, ".1234567890");
    if (r >= 0) {kind = w[r]; k_fnd = 3;}
    else {fclose(f); return -10;}
  }
  if (kind == 5) {kind = 3; k_fnd = 0;}
  if (kind == 6)
  {
    if (!ReadFileTo(f, "\n\r")) {fclose(f); return -4;}
    k_fnd = 3;
    PlayWrite *pl = 0;
    int np = THistory::HRead(f, pl);
    if (np > 0 && pl)
    {
      int i;
      Extend(nplay + np);
      for (i = 0; i < np; i++)
      {
        if (pl[i].GetN() >= 3) play[nplay++] = pl[i];
      }
    }
    if (pl) delete[] pl;
    fclose(f);
    return 1;
  }
  if (kind == 2)
  {
    printf(KIND_EQ2);
    unsigned char ch;
    do
    {
      if (fread(&ch, 1, 1, f) != 1) {fclose(f); return -10;}
    }
    while(!isspace(ch));
    PlayWrite pl;
    char word[101];
    int i, kind = 0;
    for (;;)
    {
      do
      {
        if (fread(&ch, 1, 1, f) != 1) break;
      }
      while(ch == 0 || isspace(ch));
      if (feof(f)) strcpy(word, END_STR);
      else
      {
        i = 0;
        while(ch != 0 && !isspace(ch))
        {
          if (i < 100) word[i++] = ch;
          if (fread(&ch, 1, 1, f) != 1) break;
        }
        word[i] = 0;
      }
      if (word[0] != '[')
      {
        if (kind == 1)
        {
          if (word[0] != '#' && word[0] != '$' && word[0] != '%')
          {
            Position pos;
            pos.Read(word, 1);
            pl.Clear();
            pl.Add(0, pos);
            kind = 2;
          }
        }
        else if (kind == 2)
        {
          if (word[0] != '#' && word[0] != '$' && word[0] != '%')
          {
            for (i = 0; word[i] && word[i] != '.' && word[i] != ','; i++)
            {
              if (!isdigit((unsigned char)word[i])) {i = -1; break;}
            }
            if (i == -1)
            {
              PlayWrite::PMv pmv;
              if (pl.GetPosL(pmv.pos) < 0) kind = 3;
              else if (!pmv.pos.ReadMv(pmv.mv, word, 1)) kind = 3;
              else if (pl.Add(pmv.mv) != 0) kind = 3;
            }
          }
        }
      }
      else
      {
	char end_literal[] = END_STR;
	char playv11_literal[] = PLAYV11_STR;

        if (kind == 2 || kind == 3)
        {
          if (pl.GetN() > 0) Add(pl);
          pl.Clear();
        }
        kind = 0;
        for (i = 0; word[i]; i++)
        {
          word[i] = (char)tolower((unsigned char)word[i]);
        }
        if (strskipref(word, end_literal)) break;
        else if (strskipref(word, playv11_literal)) kind = 1;
      }
    }
    fclose(f);
    return 1;
  }
  if (kind == 3)
  {
    char ch[LEN_WPOS];
    if (k_fnd < 3 && !ReadFileTo(f, '\n')) {fclose(f); return -10;}
    k_fnd = 3;
    do
    {
      PlayWrite pl;
      for (;;)
      {
        int i;
        for (i = 0; i < LEN_WPOS; i++)
        {
          if (fread(ch + i, 1, 1, f) != 1 || ch[i] < 0) break;
        }
        if (i < LEN_WPOS) break;
        PlayWrite::PMv pmv0, pmv1;
        pmv1.pos.Read(ch);
        pmv1.pos.Reverse();
        if (pl.GetPMvL(pmv0) >= 0)
        {
          TComputerPlayer::Z z;
          z.FindAllMoves(pmv0);
          int r;
          for (r = 0; r < z.narr; r++)
          {
            if (memcmp(z.array[r].pos.SH, pmv1.pos.SH, sizeof(pmv1.pos.SH)) == 0)
            {
              pmv1 = z.array[r];
              break;
            }
          }
          if (r < z.narr) pl.Add(pmv1);
          else {fclose(f); return -3;}
        }
        else pl.Add(0, pmv1.pos);
      }
      if (pl.GetN() > 0) Add(pl);
    }
    while(!feof(f));
    fclose(f);
    return 1;
  }
  fclose(f);
  return -10;
}

int TPlayArray::MsgOpenFile(const char *name, int kind)
{
  int n0 = nplay, no_games = 0;
  int r = OpenFile(name, kind);
  if (r <= 0)
  {
    if (r == -1)
    {
      printf(CHECKERS_CANT_OPEN_STR, name);
      return 0;
    }
    else if (r == -2)
    {
      printf(CHECKERS_FILE_EMPTY_STR, name);
      return 0;
    }
    else if (r == -3)
    {
      printf(CHECKERS_INVALID_FILE_STR, name);
      return 0;
    }
    else if (r == -4) no_games = 1;
    else if (r == -10)
    {
      printf(FILE_WRONG_TYPE_STR, name);
      return 0;
    }
    else
    {
      printf(ERROR_OPENING_FILE_STR, name);
      return 0;
    }
  }
  if (!no_games && nplay > n0) return 1;
  else
  {
    printf(FILE_HAS_NO_GAMES_STR, name);
    return 0;
  }
}

int TPlayArray::SaveFile(const char *name, int num, int kind)
{
  FILE *f = 0;
  if (kind == 0 || kind == 1 || kind == 2)
  {
    f = fopen(name, "wt");
    if (!f) return -1;

    fprintf(f, CHECKERS_PLAY_STR);
    int i0 = num, i;
    if (num < 0) {i0 = 0; num = nplay-1;}
    for (i = i0; i <= num; i++) if (play[i].GetN() > 0)
    {
      PlayWrite::PMv pmv;
      if (play[i].GetPos(pmv.pos, 0) < 0) return -9;
      char *str = new char[10 + NUM_CELL];
      if (!str) return -5;
      pmv.pos.Write(str, 1);
      fprintf(f, "\n"PLAYV11_STR"#%d  %s\n", i - i0 + 1, str);
      delete[] str;
      int j;
      for (j = 1; j < play[i].GetN(); j++)
      {
        if (play[i].GetPos(pmv.pos, j - 1) < 0) return -9;
        if (play[i].GetMove(pmv.mv, j) < 0) return -9;
        str = new char[pmv.pos.GetLenMvEx(pmv.mv, 11)];
        if (!str) return -5;
        pmv.pos.WriteMvEx(pmv.mv, str, 11);
        if (j % 2 == 1)
        {
          int nbytes = fprintf(f, "%d. ", (j + 1) / 2);
          while(nbytes++ < 5) fprintf(f, " ");
        }
        fprintf(f, "%s", str);
        if (j % 2 == 0 || j == play[i].GetN() - 1) fprintf(f, "\n");
        else fprintf(f, " ,\t");
        delete[] str;
      }
    }
    fclose(f);
    return 1;
  }
  else if (kind == -1 || kind == 3)
  {
    f = fopen(name, "wb");
    if (!f) return -1;
    if (kind == 3) fprintf(f, CHECKERS_BIN_STR);

    int i = num;
    if (num < 0) {i = 0; num = nplay-1;}
    for (; i <= num; i++)
    {
      char ch[LEN_WPOS];
      Position pos;
      play[i].GetPosL(pos);
      if (!pos.AllCanEat() && !pos.AllCanMove())
      {
        ch[0] = (pos.wmove == 0) ? char(-3) : char(-1);
      }
      else ch[0] = char(-2);
      fwrite(ch, 1, 1, f);
      int j;
      for (j = 0; j < play[i].GetN(); j++)
      {
        Position pos;
        play[i].GetPos(pos, j);
        pos.Reverse();
        pos.Write(ch);
        fwrite(ch, LEN_WPOS, 1, f);
      }
    }
    fclose(f);
    return 1;
  }
  if (f) fclose(f);
  return -10;
}

int TPlayArray::MsgSaveFile(const char *name, int num, int kind)
{
  int r = SaveFile(name, num, kind);
  if (r <= 0)
  {
    if (r == -1)
    {
      printf(CANT_OPEN_FILE_STR, name);
    }
    else if (r == -5)
    {
      printf(NOT_ENOUGH_MEM_STR, name);
    }
    else if (r == -10)
    {
      printf(FILE_WRONG_TYPE_STR, name);
    }
    else
    {
      printf(ERROR_SAVING_FILE_STR, name);
    }
    return 0;
  }
  else
  {
    printf(FILE_SAVED_STR, name);
    return 1;
  }
}
#endif

void TPlayArray::Del(int n)
{
  if (!play || n < 0 || n >= nplay) return;
  else if (nplay <= 1) {Clear(); return;}
  int i;
  for (i = n; i < nplay - 1; i++) play[i] = play[i+1];
  play[nplay - 1].Clear();
  nplay--;
}


void SetPlayerString(char *str, int c, TChPlayer *p)
{
  strcpy(str, c ? BLUE_STR : RED_STR);
  if (!p) strcat(str, INPUT_STR);
  else strcat(str, COMPUTER_STR);
}

class TMainDraw : public TSomeDraw
{
public:
  
  TMainDraw() : undo_redo(0), ur_type(-1), cur_play(0),
                def_savefile("save.che"), def_savekind(2) {InitButton();}
  
  virtual void Draw(TGraphDraw *drw, int w, int h)
  {
    int d = button.GetDelt();
    int hh = button.GetHeight(w - 2*d);
    if (hh != 0) hh += d;
    drw->SetColor(drw->GetBlackColor());
    button.Draw(drw, d, h - hh, w - 2*d);
  }

  virtual void DrawB(TGraphDraw *drw, TChBoard &board)
  {
    int urt = (board.GetCurMoveN() <= 0) +
            2*(board.GetCurMoveN() >= board.GetNumMove());
    if (ur_type != urt) SetButtonKind(board);
    Draw(drw, board.GetW(), board.GetH());
  }

  virtual TIntPoint GetDSize(int w, int h)
  {
    int d = button.GetDelt();
    int hh = button.GetHeight(w - 2*d);
    if (hh != 0) hh += d;
    return TIntPoint(0, hh);
  }

  virtual int ButtonPnt(int xp, int yp, int w, int h, int &n, int k = 0)
  {
    int d = button.GetDelt();
    int hh = button.GetHeight(w - 2*d);
    if (hh != 0) hh += d;
    return button.ButtonPnt(xp - d, yp - (h - hh), w - 2*d, n, k);
  }

  void InitButton();
  void SetButtonKind(const TChBoard &board);
  void PressUR(int n, TChBoard &board, TGraphDraw *drw);
  void PressLS(int n, TChBoard &board, TGraphDraw *drw);
  void CurPlayNorm()
  {
    if (cur_play < 0 || cur_play >= play.GetNPlay()) cur_play = 0;
  }

  TXButtonArray button;
  TMultiButton *undo_redo, *play_list;
  char player_str[2][50], play_num[2][10];
  TPlayArray play;
  int cur_play;
  char *def_savefile;
  int def_savekind;
protected:
  int ur_type;
};

void TMainDraw::InitButton()
{
  static char newgame_literal[] = NEW_GAME_STR;
  static char list_literal[] = LIST_STR;
  static char delete_literal[] = DELETE_STR;
  static char clear_literal[] = CLEAR_STR;
  static char save_literal[] = SAVE_STR;
  static char rotateboard_literal[] = ROTATE_BOARD_STR;
  static char exit_literal[] = EXIT_STR;

  static char one_literal[] = "1";
  static char dash_literal[] = "-";
  static char plus_literal[] = "+";
  static char leftshift_literal[] = "<<";
  static char rightshift_literal[] = ">>";
  static char lessthan_literal[] = "<";
  static char greaterthan_literal[] = ">";
  static char xor_literal[] = "^";

  button.Add(1, 80, 22, newgame_literal);
  button.Add(6, 60, 22, list_literal);
  button.Add(7, 60, 22, delete_literal);
  play_list = new TMultiButton(20);
  play_list->a.Add(26, 20, 22, one_literal);
  play_list->a.Add(21, 20, 22, dash_literal);
  play_list->a.Add(23, 37, 22, play_num[0], -2);
  play_list->a.Add(22, 20, 22, plus_literal);
  play_list->a.Add(27, 37, 22, play_num[1]);
  play_list->SetDefW();
  button.Add(play_list);
  button.Add(24, 50, 22, clear_literal);
#ifndef NO_FILES
  button.Add(25, 50, 22, save_literal);
#endif
  button.Add(2, 120, 22, player_str[0]);
  button.Add(3, 120, 22, player_str[1]);
  button.Add(4, 110, 22, rotateboard_literal);
  undo_redo = new TMultiButton(10);
  undo_redo->a.Add(11, 27, 22, leftshift_literal);
  undo_redo->a.Add(12, 20, 22, lessthan_literal);
  undo_redo->a.Add(15, 20, 22, xor_literal);
  undo_redo->a.Add(13, 20, 22, greaterthan_literal);
  undo_redo->a.Add(14, 27, 22, rightshift_literal);
  undo_redo->SetDefW();
  button.Add(undo_redo);
  button.Add(5, 60, 22, exit_literal);
}

void TMainDraw::SetButtonKind(const TChBoard &board)
{
  int thick;
  TextButton *txb;
  TXButton *bt1;
  ur_type = 0;
  SetPlayerString(player_str[0], 0, board.GetPlayer(0));
  SetPlayerString(player_str[1], 1, board.GetPlayer(1));
  int is_drw = !board.GetPViewStatus();
  bt1 = button.GetButton(2);
  if (bt1) bt1->drw = is_drw;
  bt1 = button.GetButton(3);
  if (bt1) bt1->drw = is_drw;
  if (board.GetCurMoveN() <= 0) {ur_type++; thick = 0;}
  else thick =  3;
//  txb = dynamic_cast<TextButton*>(undo_redo->a.GetButton(11));
//  if (txb) txb->thick = thick;
//  txb = dynamic_cast<TextButton*>(undo_redo->a.GetButton(12));
//  if (txb) txb->thick = thick;
// we can use simple static cast
	((TextButton*)(undo_redo->a.GetButton(11)))->thick = thick;
	((TextButton*)(undo_redo->a.GetButton(12)))->thick = thick;
  if (board.GetCurMoveN() >= board.GetNumMove()) {ur_type += 2; thick = 0;}
  else thick = 3;
//  txb = dynamic_cast<TextButton*>(undo_redo->a.GetButton(13));
//  if (txb) txb->thick = thick;
//  txb = dynamic_cast<TextButton*>(undo_redo->a.GetButton(14));
//  if (txb) txb->thick = thick;
	((TextButton*)(undo_redo->a.GetButton(13)))->thick = thick;
	((TextButton*)(undo_redo->a.GetButton(14)))->thick = thick;
  if (board.GetCurMoveN() < board.GetNumMove() ||
     (board.GetPViewStatus() && board.GetGameEnd() <= 0))
  {
    thick = 3;
  }
  else thick = 0;
//  txb = dynamic_cast<TextButton*>(undo_redo->a.GetButton(15));
//  if (txb) txb->thick = thick;
	((TextButton*)(undo_redo->a.GetButton(15)))->thick = thick;
  if (play.GetNPlay() == 0) is_drw = 1;
  bt1 = button.GetButton(6);
  if (bt1) bt1->drw = is_drw;
  bt1 = button.GetButton(7);
  if (bt1) bt1->drw = !is_drw;
#ifndef NO_FILES
  bt1 = button.GetButton(25);
  if (bt1) bt1->drw = !is_drw;
#endif
  is_drw = board.GetPViewStatus() && play.GetNPlay() > 1;
  bt1 = button.GetButton(20);
  if (bt1) bt1->drw = is_drw;
  bt1 = button.GetButton(24);
  if (bt1) bt1->drw = is_drw;
  if (is_drw)
  {
    play_num[0][0] = 0; play_num[1][0] = 0;
    if (cur_play >= 0) itoa(play_num[0], cur_play + 1);
    itoa(play_num[1], play.GetNPlay());
    thick = (cur_play <= 0) ? 0 : 3;
    txb = static_cast<TextButton*>(play_list->a.GetButton(21));
    if (txb) txb->thick = thick;
    txb = static_cast<TextButton*>(play_list->a.GetButton(26));
    if (txb) txb->thick = thick;
    thick = (cur_play >= play.GetNPlay() - 1) ? 0 : 3;
    txb = static_cast<TextButton*>(play_list->a.GetButton(22));
    if (txb) txb->thick = thick;
    txb = static_cast<TextButton*>(play_list->a.GetButton(27));
    if (txb) txb->thick = thick;
  }
}

void TMainDraw::PressUR(int n, TChBoard &board, TGraphDraw *drw)
{
  int mv;
  if (n == 11) mv = 0;
  else if (n == 12) mv = board.GetCurMoveN() - 1;
  else if (n == 13) mv = board.GetCurMoveN() + 1;
  else mv = board.GetNumMove();
  if (board.SetCurMoveN(mv))
  {
    SetButtonKind(board);
    if (drw && drw->IsDraw())
    {
      drw->DrawClear();
      board.Draw(drw);
    }
  }
  else Draw(drw, board.GetW(), board.GetH());
}

void TMainDraw::PressLS(int n, TChBoard &board, TGraphDraw *drw)
{
  int need_redraw = 0;
  if (n == 6)
  {
    if (!board.GetPViewStatus() || play.GetNPlay() == 0)
    {
      PlayWrite cur_pw = board.GetPlay();
      if (cur_pw.GetN() > 2 || play.GetNPlay() == 0) play.Add(board.GetPlay());
      cur_play = play.GetNPlay() - 1;
      board.SetPlay(play[cur_play]);
      need_redraw = 1;
    }
  }
  else if (n == 7)
  {
    if (board.GetPViewStatus() && play.GetNPlay() != 0)
    {
      play.Del(cur_play);
      if (play.GetNPlay() >= 1)
      {
        if (cur_play >= play.GetNPlay()) cur_play--;
        board.SetPlay(play[cur_play]);
      }
      need_redraw = 1;
    }
  }
  else if (n == 21)
  {
    if (cur_play > 0) {board.SetPlay(play[--cur_play]); need_redraw = 1;}
  }
  else if (n == 22)
  {
    if (cur_play < play.GetNPlay() - 1)
    {
      board.SetPlay(play[++cur_play]); need_redraw = 1;
    }
  }
  else if (n == 26)
  {
    if (cur_play > 0)
    {
      cur_play = 0;
      board.SetPlay(play[cur_play]); need_redraw = 1;
    }
  }
  else if (n == 27)
  {
    if (cur_play < play.GetNPlay() - 1)
    {
      cur_play = play.GetNPlay() - 1;
      board.SetPlay(play[cur_play]); need_redraw = 1;
    }
  }
  else if (n == 24) {play.Clear(); cur_play = 0; need_redraw = 1;}
#ifndef NO_FILES
  else if (n == 25)
  {
    if (play.GetNPlay() > 0) play.MsgSaveFile(def_savefile, -1, def_savekind);
  }
  else if (n == 28)
  {
    if (play.GetNPlay() > 0) play.MsgSaveFile(def_savefile, cur_play, def_savekind);
  }
#endif
  if (need_redraw)
  {
    SetButtonKind(board);
    if (drw && drw->IsDraw())
    {
      drw->DrawClear();
      board.Draw(drw);
    }
  }
  else Draw(drw, board.GetW(), board.GetH());
}

struct TTimerDraw
{
  TTimerDraw(TChBoard *brd, TGraphDraw *drw) : brd(brd), drw(drw) {}

  TChBoard *brd;
  clock_t st, ut, dt;
  double x0;
  TGraphDraw *drw;

  static void draw(void *v, int k = 0);
};

void TTimerDraw::draw(void *v, int k)
{
  TTimerDraw &d = *(TTimerDraw*)v;
  clock_t t = clock();
  if (k == 0 && t - d.ut < CLOCKS_PER_SEC * 0.01) return;
  if (k > 0)
  {
    d.st = t;
    if (!d.drw || !d.drw->IsDraw()) return;
    d.drw->DrawClear();
    d.brd->Draw(d.drw);
    d.dt = t;
  }
  else if (!d.drw || !d.drw->IsDraw()) return;
  double xold = d.x0;
  if (k >= 0)
  {
    d.x0 = (1 - cos(2.0 * (t - d.st) / CLOCKS_PER_SEC)) / 2;
    d.brd->DrawTimer(d.drw, d.x0, 0);
    d.ut = t;
    if (k == 0 && t - d.dt > CLOCKS_PER_SEC * 0.5)
    {
      d.brd->Draw(d.drw);
      d.dt = t;
    }
  }
  if (k <= 0) d.brd->DrawTimer(d.drw, xold, 1);
}


struct TMainData
{
  TChBoard board;
  TComputerPlayer player;
  TMainDraw main_draw;

  TMainData(int id = 0);
  void InitDef();
  static int EventFunction(const TGraphDraw::event &ev);
  void NewGame(TGraphDraw *drw);
  void RotateBoard(TGraphDraw *drw);
  void PlayerPress(int np, TGraphDraw *drw);
  void GoToCurMove(TGraphDraw *drw);
};

TMainData::TMainData(int id) : board(id)
{
  board.SetCheckResize(1);
  board.SetPlayer(1, &player);
  board.SetBottomColor(0);
  board.SetSomeDraw(&main_draw);
  board.SetMinWSize(90, 140);
}

void TMainData::InitDef()
{
  if (main_draw.play.GetNPlay() > 0)
  {
    main_draw.CurPlayNorm();
    board.SetPlay(main_draw.play[main_draw.cur_play]);
  }
  main_draw.SetButtonKind(board);
}

void TMainData::NewGame(TGraphDraw *drw)
{
  board.NewGame();
  main_draw.SetButtonKind(board);
  if (drw && drw->IsDraw()) {drw->DrawClear(); board.Draw(drw);}
}

void TMainData::RotateBoard(TGraphDraw *drw)
{
  board.SetBottomColor(3 - board.GetBottomColor());
  if (drw && drw->IsDraw()) {drw->DrawClear(); board.Draw(drw);}
}

void TMainData::PlayerPress(int np, TGraphDraw *drw)
{
  if (np != 0 && np != 1) return;
  if (board.GetPlayer(np)) board.SetPlayer(np, 0);
  else board.SetPlayer(np, &player);
  if (board.GetPlayer(0) && !board.GetPlayer(1))
  {
    board.SetBottomColor(1);
  }
  if (board.GetPlayer(1) && !board.GetPlayer(0))
  {
    board.SetBottomColor(0);
  }
  main_draw.SetButtonKind(board);
  if (drw && drw->IsDraw()) {drw->DrawClear(); board.Draw(drw);}
}

void TMainData::GoToCurMove(TGraphDraw *drw)
{
  board.GoToCurMove();
  main_draw.SetButtonKind(board);
  if (drw && drw->IsDraw()) {drw->DrawClear(); board.Draw(drw);}
}

int TMainData::EventFunction(const TGraphDraw::event &ev)
{
  if (!ev.any.drw->data) return -100;
  TMainData &data = *(TMainData*)ev.any.drw->data;
  int nbutton, ret = 0;
  switch(ev.type)
  {
  case TGraphDraw::event::button_down:
    if (ev.button.n != 1) break;
    ev.button.drw->OpenDraw();
    if (data.main_draw.ButtonPnt(ev.button.x, ev.button.y,
             data.board.GetW(), data.board.GetH(), nbutton, 1) > 0)
    {
      data.main_draw.Draw(ev.button.drw, data.board.GetW(), data.board.GetH());
      ret |= TGraphDraw::ret_setcapture;
    }
    else data.board.MouseClick(ev.button.drw, ev.button.x, ev.button.y);
    ev.button.drw->CloseDraw();
    break;
  case TGraphDraw::event::mouse_move:
    if (ev.button.n >= 0 && ev.button.n != 1) break;
    ev.button.drw->OpenDraw();
    if (data.main_draw.ButtonPnt(ev.button.x, ev.button.y,
             data.board.GetW(), data.board.GetH(), nbutton, 2) >= 1000)
    {
      data.main_draw.Draw(ev.button.drw, data.board.GetW(), data.board.GetH());
    }
    ev.button.drw->CloseDraw();
    break;
  case TGraphDraw::event::button_up:
    if (ev.button.n != 1) break;
    ev.button.drw->OpenDraw();
    if (data.main_draw.ButtonPnt(ev.button.x, ev.button.y,
             data.board.GetW(), data.board.GetH(), nbutton, 3) > 0)
    {
      switch(nbutton)
      {
      case 1:
        data.NewGame(ev.button.drw);
        break;
      case 2:
      case 3:
        data.PlayerPress(nbutton - 2, ev.button.drw);
        break;
      case 4:
        data.RotateBoard(ev.button.drw);
        break;
      case 5:
        data.main_draw.Draw(ev.button.drw, data.board.GetW(), data.board.GetH());
        ev.button.drw->Quit();
        break;
      case 11:
      case 12:
      case 13:
      case 14:
        data.main_draw.PressUR(nbutton, data.board, ev.button.drw);
        break;
      case 15:
        data.GoToCurMove(ev.button.drw);
        break;
      case 6:
      case 7:
      case 21:
      case 22:
      case 23:
      case 24:
      case 25:
      case 26:
      case 27:
        data.main_draw.PressLS(nbutton, data.board, ev.button.drw);
        break;
      default:
        data.main_draw.Draw(ev.button.drw, data.board.GetW(), data.board.GetH());
        break;
      }
    }
    ev.button.drw->CloseDraw();
    break;
  case TGraphDraw::event::draw:
    ev.button.drw->OpenDraw();
    data.board.Draw(ev.button.drw);
    ev.button.drw->CloseDraw();
    break;
  case TGraphDraw::event::key_down:
    ev.button.drw->OpenDraw();
    if (ev.key.k == XK_Left) data.board.PKeyEvent(ev.button.drw, TChBoard::PLeft);
    else if (ev.key.k == XK_Right) data.board.PKeyEvent(ev.button.drw, TChBoard::PRight);
    else if (ev.key.k == XK_Up) data.board.PKeyEvent(ev.button.drw, TChBoard::PUp);
    else if (ev.key.k == XK_Down) data.board.PKeyEvent(ev.button.drw, TChBoard::PDown);
    else if (ev.key.k == XK_Return || ev.key.k == XK_space)
    {
      data.board.PKeyEvent(ev.button.drw, TChBoard::PEnter);
    }
    else if (ev.key.k == XK_Escape) ev.button.drw->Quit();
    else if (ev.key.k == XK_less) data.main_draw.PressUR(11, data.board, ev.button.drw);
    else if (ev.key.k == XK_comma) data.main_draw.PressUR(12, data.board, ev.button.drw);
    else if (ev.key.k == XK_period) data.main_draw.PressUR(13, data.board, ev.button.drw);
    else if (ev.key.k == XK_greater) data.main_draw.PressUR(14, data.board, ev.button.drw);
    else if (ev.key.k == XK_minus) data.main_draw.PressLS(21, data.board, ev.button.drw);
    else if (ev.key.k == XK_equal) data.main_draw.PressLS(22, data.board, ev.button.drw);
    else if (ev.key.k == XK_underscore) data.main_draw.PressLS(26, data.board, ev.button.drw);
    else if (ev.key.k == XK_plus) data.main_draw.PressLS(27, data.board, ev.button.drw);
    else if (ev.key.k == XK_Delete) data.main_draw.PressLS(7, data.board, ev.button.drw);
    else if (ev.key.k == XK_F8) data.main_draw.PressLS(24, data.board, ev.button.drw);
    else if (ev.key.k == XK_l || ev.key.k == XK_L) data.main_draw.PressLS(6, data.board, ev.button.drw);
#ifndef NO_FILES
    else if (ev.key.k == XK_F2) data.main_draw.PressLS(25, data.board, ev.button.drw);
#endif
    else if (ev.key.k == XK_s || ev.key.k == XK_S) data.main_draw.PressLS(28, data.board, ev.button.drw);
    else if (ev.key.k == XK_slash || ev.key.k == XK_question) data.GoToCurMove(ev.button.drw);
    else if (ev.key.k == XK_n || ev.key.k == XK_N) data.NewGame(ev.button.drw);
    else if (ev.key.k == XK_t || ev.key.k == XK_T) data.RotateBoard(ev.button.drw);
    else if (ev.key.k == XK_r || ev.key.k == XK_R) data.PlayerPress(0, ev.button.drw);
    else if (ev.key.k == XK_b || ev.key.k == XK_B) data.PlayerPress(1, ev.button.drw);
    else if (ev.key.k == XK_f || ev.key.k == XK_F)
    {
      int w, h;
      ev.button.drw->GetSize(w, h);
      ev.button.drw->CloseDraw();
      if (DuplicateProcess() == 0)
      {
        ev.button.drw->ResReinit(w, h);
        data.board.EraseHistory();
      }
    }
    ev.button.drw->CloseDraw();
    break;
  case TGraphDraw::event::close:
    ret = 1;
    break;
  }
  return ret;
}

int main(int argc, char **argv)
{
  randomize();
#ifndef NO_FILES
  THistory::InitHFile(argv[0]);
#endif
  TMainData data(-1);
  if (argv && argc >= 2)
  {
    int i, kx = 1;
    for (i = 1; i < argc; i++)
    {
      if (kx == 1 && argv[i][0] == '-')
      {
        if (strcmp(argv[i], "--") == 0) kx = 0;
        else if (strcmp(argv[i], "-ssf") == 0) ssf = 1;
        else if (strncmp(argv[i], "-save", 5) == 0)
        {
          int j = 5;
          if (argv[i][j])
          {
            if (argv[i][j] != '=' || !argv[i][j+1])
            {
              data.main_draw.def_savekind = atoi(argv[i] + j);
              while (argv[i][j] && argv[i][j] != '=') j++;
              if (argv[i][j] != '=' || !argv[i][j+1]) continue;
            }
            data.main_draw.def_savefile = argv[i] + j + 1;
          }
        }

        else printf(CHECKERS_INVALID_STR, argv[i]);
      }
      else if (kx == 0 || kx == 1)
      {
#ifndef NO_FILES
        data.main_draw.play.MsgOpenFile(argv[i], -1);
#endif
      }
    }
  }
  data.InitDef();
  TMainGraphDraw graph(CHECKERS_STR);
  TTimerDraw timer_draw(&data.board, &graph);
  data.player.draw = TTimerDraw::draw; data.player.data = &timer_draw;
  graph.evfunc = TMainData::EventFunction; graph.data = &data;
  graph.SetAboutInfo(1);
  graph.Run(TGraphDraw::button_down_mask | TGraphDraw::button_up_mask |
            TGraphDraw::key_down_mask | TGraphDraw::mouse_drag_mask,
            450 + 100 * ssf, 528);
  return 0;
}