
struct menu *setmenu;
struct menu *grmenu;

char backsel[] = "Back";

#define MAINMENU_ITEMCOUNT 2

#if defined(SDLFUNCS) || defined(DOS)
char startgamestr[] = "Start game";
char newgamestr[] = "New game";
char quitgamestr[] = "Quit game";
char settingsstr[] = "Game settings";
char graphsetstr[] = "Graphics settings";
char depth1str[] = "Depth 1";
char depth2str[] = "Depth 2";
char blunderstr[] = "Blunders";
char whitestr[] = "White player";
char blackstr[] = "Black player";
char deadendstr[] = "Deadend limit *-16";
char deadendaddstr[] = "Deadend limit add *16";
char bookopeningstr[] = "Use opening book";
char slidepiecesstr[] = "Slide pieces";
char showclippystr[] = "Show Clippy";
char demostr[] = "Demo";
char enddemostr[] = "End demo";
char savegamestr[] = "Save game";
char loadgamestr[] = "Load game";
char boardrotationstr[] = "Board rotation";
char timecontrolstr[] = "Time control";
char timelimitstr[] = "Time limit";
char difficultystr[] = "Difficulty";
char showlegalstr[] = "Show legal moves";
char showthreatstr[] = "Show threatened pieces";
char screenscalingstr[] = "Window scaling";
char editboardstr[] = "Edit board";
char deepthinkingstr[] = "Deep thinking";

char *difficulties[] =
{
  "Beginner", "Easy", "Medium", "Hard", "Custom"
};

char *timecontrols[] =
{
  "Disabled", "Per player", "Per move"
};

char *rotations[] =
{
  "0", "90", "180", "270"
};

char *yesno[] =
{
  "No", "Yes"
};

char *humanclippy[] =
{
  "Human", "Clippy"
};

unsigned char maxrotation = 3;
unsigned char maxtimectrl = sizeof(timecontrols) / sizeof(char*) - 1;
unsigned char maxdifficulty = sizeof(difficulties) / sizeof(char*) - 2;
unsigned char maxlimit = 255;
//unsigned char deadend;
//unsigned char deadendadd;
unsigned char maxdepth2 = 128;
unsigned char mindepth = 1;
unsigned char blunderset = 0;
unsigned char minblunders = 0;
unsigned char maxblunders = 15;
signed char savegameid;
signed char editboardid;
#endif


void game(void);

void new_game()
{
  memset(game_status, 0, sizeof(struct game_status));
  turn_count = 0;
  init_board(&board);
#if defined(SDLFUNCS) || defined(DOS)
  if(setup.timecontrol == TIMECTRL_TIMEPERPLAYER)
    game_status->black_time = game_status->white_time = setup.timelimit;
#endif
}

int start_game(struct menuitem *menuitem);

int end_game()
{
  return 1;
}

int start_game(struct menuitem *menuitem)
{
#if defined (SDLFUNCS) || defined (DOS)
  if(graphics)
  {
    mainmenu->prev = 1;
    if(menuitem)
    {
      menuitem->str = newgamestr;
      menuitem->fnptr = (void*)end_game;
    }
    mainmenu->menuitems[savegameid].passive = 0;
    mainmenu->menuitems[editboardid].passive = 0;
  }
#endif

  do
  {
#if defined (SDLFUNCS) || defined (DOS)
    if(!prevgameloaded)
#endif
      new_game();
    game();
  }
  while(dontstop);

#if defined (SDLFUNCS) || defined (DOS)
  if(graphics)
  {
    if(menuitem)
    {
      menuitem->str = startgamestr;
      menuitem->fnptr = (void*)start_game;
    }
    mainmenu->prev = 0;
    mainmenu->menuitems[savegameid].passive = 1;
    mainmenu->menuitems[editboardid].passive = 1;
//    prevgameloaded = 0;
  }
#endif

  return 0;
}

#if defined(SDLFUNCS) || defined(DOS)
int save_game()
{
  unsigned char oldenclip = setup.enable_clippy;
  int rc;
  unsigned n;
  FILE *fp = fopen("game.sav", "wb");
  if(!fp) return 1;

  if(fwrite(game_status, sizeof(struct game_status), 1, fp) != 1)
    return 1;

  if(fwrite(&board, sizeof(struct board), 1, fp) != 1)
    return 1;

  if(fwrite(&turn_count, sizeof(unsigned), 1, fp) != 1)
    return 1;

  for(n=0;n<opening_count;n++)
    if(fwrite(&openings[n].moves_left, sizeof(openings[n].moves_left), 1, fp) != 1)
      return 1;

  setup.enable_clippy = 0;
  if(fclose(fp))
  {
    show_dialog_nokey("Error when trying to save game.");
    rc = 1;
  }
  else
  {
    show_dialog_nokey("Game saved.");
    rc = 0;
  }

  setup.enable_clippy = oldenclip;

  return rc;
}

int load_game()
{
  unsigned char oldenclip = setup.enable_clippy;
  unsigned n;
  FILE *fp = fopen("game.sav", "rb");
  if(!fp) return 1;

  if(fread(game_status, sizeof(struct game_status), 1, fp) != 1)
    return 1;

  if(fread(&board, sizeof(struct board), 1, fp) != 1)
    return 1;

  if(fread(&turn_count, sizeof(unsigned), 1, fp) != 1)
    return 1;

  for(n=0;n<opening_count;n++)
    if(fread(&openings[n].moves_left, sizeof(openings[n].moves_left), 1, fp) != 1)
      return 1;

  fclose(fp);

  setup.enable_clippy = 0;

  show_dialog_nokey("Saved game restored.");

  setup.enable_clippy = oldenclip;
  prevgameloaded = 1;

  return 0;
}

int demo(struct menuitem *menuitem)
{
  static char olddepth1;
  static char olddepth2;
  static char oldwhiteai;
  static char oldblackai;

  if(!dontstop)
  {
    flags |= 2;
    olddepth1 = setup.depth1;
    olddepth2 = setup.depth2;
    oldwhiteai = setup.white_ai;
    oldblackai = setup.black_ai;
    setup.white_ai = 1;
    setup.black_ai = 1;
    setup.depth1 = 2;
    setup.depth2 = 3;
    menuitem->str = enddemostr;
    start_game(0);
  }
  else
  {
    flags &= ~2;
    menuitem->str = demostr;
    setup.white_ai = oldwhiteai;
    setup.black_ai = oldblackai;
    setup.depth1 = olddepth1;
    setup.depth2 = olddepth2;
  }

  return 0;
}

int setcustomdiff()
{
  setup.difficulty = sizeof(difficulties) / sizeof(char*) - 1;
  return 0;
}

int set_blunders(void *ptr);
int setdeadend(void *ptr);

int setdifficulty()
{
  switch(setup.difficulty)
  {
    case 0:
      setup.depth1 = 1;
      setup.depth2 = 1;
      blunderset = 15;
      setup.deadend = 0;
      setup.deadendadd = QUEEN_VALUE>>4;
      break;
    case 1:
      setup.depth1 = 1;
      setup.depth2 = 1;
      blunderset = 4;
      setup.deadend = QUEEN_VALUE>>4;
      setup.deadendadd = ROOK_VALUE>>4;
      break;
    case 2:
      setup.depth1 = 3;
      setup.depth2 = 6;
      blunderset = 0;
      setup.deadend = QUEEN_VALUE>>4;
      setup.deadendadd = ROOK_VALUE>>4;
      break;
    case 3:
      setup.depth1 = 6;
      setup.depth2 = 12;
      blunderset = 0;
      setup.deadend = 2*QUEEN_VALUE>>4;
      setup.deadendadd = ROOK_VALUE>>4;
  }
  set_blunders(0);
  setdeadend(0);
  return 0;
}


int setdeadend(void *ptr)
{
  global_deadend_score = -(setup.deadend << 4);
  deadend_limit = setup.deadendadd << 4;

  if(ptr)
    setcustomdiff();

  return 0;
}

int quit_game()
{
  if(save_setup())
    puts("Couldn't save settings.");
  exit(0);
}

#define MENU_X 20
#define MENU_Y 20

int set_blunders(void *ptr)
{
  if(!blunderset)
    setup.blunders = 0;
  else
    setup.blunders = 16 - blunderset;

  if(ptr)
    setcustomdiff();

  return 0;
}

void get_blunders(void)
{
  if(!setup.blunders)
    blunderset = 0;
  else
    blunderset = 16 - setup.blunders;
}

int show_menu(struct menu *menu)
{
  signed char sel;
  unsigned char i;
  unsigned char color;
  char *str;
  char buf[16];
  int rc;

  if(menu->prev) sel = -1;
  else sel = 0;

  while(1)
  {
    fill_screen(0x11);

    if(menu->prev)
    {
      if(sel == -1)
        color = 0xEF;
      else
        color = 0x77;
      print_text(MENU_X, MENU_Y, color, backsel);
    }

    for(i=0;i<menu->menuitem_count;i++)
    {
      if(i == sel)
        color = 0xEF;
      else if(menu->menuitems[i].passive)
        color = 0x88;
      else
        color = 0x77;

      print_text(MENU_X, MENU_Y+12+i*12, color, menu->menuitems[i].str);
      if(menu->menuitems[i].value)
      {
        if(menu->menuitems[i].choices)
          str = menu->menuitems[i].choices[*menu->menuitems[i].value];
        else
        {
          sprintf(buf, "%u", *menu->menuitems[i].value);
          str = buf;
        }
      }
      else if(menu->menuitems[i].time)
        str = secondstotimestr(*menu->menuitems[i].time);
      else continue;
      print_text(MENU_X+200, MENU_Y+12+i*12, 0x22, str);
    }

    update_screen();

    switch(poll_keypress())
    {
      case KEY_UP:
        if(menu->prev ? sel>-1 : sel > 0) sel--;
        break;
      case KEY_DOWN:
        if(sel < menu->menuitem_count-1) sel++;
        break;
      case KEY_RIGHT:
        if(sel >= 0)
        {
          if(menu->menuitems[sel].value)
          {
            if(*menu->menuitems[sel].value < *menu->menuitems[sel].maxvalue)
              ++*menu->menuitems[sel].value;
          }
          else if(menu->menuitems[sel].time)
          {
            *menu->menuitems[sel].time += 10;
          }
          goto fn;
        }
      case KEY_LEFT:
        if(sel >= 0)
        {
          if(menu->menuitems[sel].value)
          {
            if(*menu->menuitems[sel].value > *menu->menuitems[sel].minvalue)
              --*menu->menuitems[sel].value;
          }
          else if(menu->menuitems[sel].time && *menu->menuitems[sel].time >= 10)
            *menu->menuitems[sel].time -= 10;
          goto fn;
        }
      case KEY_ENTER:
        if(sel == -1)
          return 0;
        else if(menu->menuitems[sel].passive)
          break;
        else fn:if(menu->menuitems[sel].fnptr)
        {
          if(rc = (menu->menuitems[sel].fnptr)(&menu->menuitems[sel]))
            return rc;
        }
        else if(menu->menuitems[sel].submenu)
          if(rc = show_menu(menu->menuitems[sel].submenu))
            return rc;
        break;
    }
  }

  return 0;
}

signed char add_menuitem(struct menu *menu, struct menuitem *menuitem)
{
  void *ptr;

  if(!menu->menuitem_count)
  {
    menu->menuitems = malloc(sizeof(struct menuitem));
    if(!menu->menuitems) return -1;
  }
  else
  {
    ptr = realloc(menu->menuitems, (menu->menuitem_count+1)*sizeof(struct menuitem));
    if(!ptr) return -1;
    menu->menuitems = ptr;
  }

  menu->menuitems[menu->menuitem_count] = *menuitem;

  return menu->menuitem_count++;
}

int init_menus(void)
{
  struct menuitem menuitem = { 0 };
  signed char r1, r2;
  void *ptr;

  setup.deadend = -global_deadend_score / 16;
  setup.deadendadd = deadend_limit / 16;

  mainmenu = calloc(1, sizeof(struct menu));
  if(!mainmenu) return 1;

  // start game
  menuitem.str = startgamestr;
  menuitem.fnptr = (void*)start_game;
  if(add_menuitem(mainmenu, &menuitem) == -1)
    return 1;

  menuitem.str = loadgamestr;
  menuitem.fnptr = (void*)load_game;
  if(add_menuitem(mainmenu, &menuitem) == -1)
    return 1;

  menuitem.str = savegamestr;
  menuitem.fnptr = (void*)save_game;
  menuitem.passive = 1;
  if((savegameid = add_menuitem(mainmenu, &menuitem)) == -1)
    return 1;

  memset(&menuitem, 0, sizeof(menuitem));

  menuitem.str = demostr;
  menuitem.fnptr = (void*)demo;
  if(add_menuitem(mainmenu, &menuitem) == -1)
    return 1;

  menuitem.str = editboardstr;
  menuitem.fnptr = (void*)edit_board;
  menuitem.passive = 1;
  if((editboardid = add_menuitem(mainmenu, &menuitem)) == -1)
    return 1;

  // game settings
  setmenu = calloc(1, sizeof(struct menu));
  if(!setmenu) return 1;

  setmenu->prev = 1;

  memset(&menuitem, 0, sizeof(menuitem));
  menuitem.str = settingsstr;
  menuitem.submenu = setmenu;
  if(add_menuitem(mainmenu, &menuitem) == -1)
    return 1;

  // graphic settings
  grmenu = calloc(1, sizeof(struct menu));
  if(!grmenu) return 1;

  grmenu->prev = 1;

  menuitem.str = graphsetstr;
  menuitem.submenu = grmenu;
  if(add_menuitem(mainmenu, &menuitem) == -1)
    return 1;

  memset(&menuitem, 0, sizeof(menuitem));

  menuitem.str = whitestr;
  menuitem.value = &setup.white_ai;
  menuitem.minvalue = &minblunders;
  menuitem.maxvalue = &mindepth;
  menuitem.choices = humanclippy;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

  menuitem.str = blackstr;
  menuitem.value = &setup.black_ai;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

  memset(&menuitem, 0, sizeof(menuitem));

  menuitem.str = difficultystr;
  menuitem.value = &setup.difficulty;
  menuitem.minvalue = &minblunders;
  menuitem.maxvalue = &maxdifficulty;
  menuitem.choices = difficulties;
  menuitem.fnptr = (void*)setdifficulty;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

  memset(&menuitem, 0, sizeof(menuitem));

  menuitem.str = depth1str;
  menuitem.value = &setup.depth1;
  menuitem.minvalue = &mindepth;
  menuitem.fnptr = (void*)setcustomdiff;
  if((r1 = add_menuitem(setmenu, &menuitem)) == -1)
    return 1;

  menuitem.str = depth2str;
  menuitem.value = &setup.depth2;
  menuitem.minvalue = setmenu->menuitems[r1].value;
  menuitem.maxvalue = &maxdepth2;
  if((r2 = add_menuitem(setmenu, &menuitem)) == -1)
    return 1;

  setmenu->menuitems[r1].maxvalue = setmenu->menuitems[r2].value;

  menuitem.str = blunderstr;
  menuitem.value = &blunderset;
  menuitem.minvalue = &minblunders;
  menuitem.maxvalue = &maxblunders;
  menuitem.fnptr = (void*)set_blunders;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

  menuitem.str = deadendstr;
  menuitem.value = &setup.deadend;
  menuitem.minvalue = &minblunders;
  menuitem.maxvalue = &maxlimit;
  menuitem.fnptr = (void*)setdeadend;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

  menuitem.str = deadendaddstr;
  menuitem.value = &setup.deadendadd;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

  memset(&menuitem, 0, sizeof(menuitem));
  menuitem.str = bookopeningstr;
  menuitem.value = &setup.book_openings;
  menuitem.minvalue = &minblunders;
  menuitem.maxvalue = &mindepth;
  menuitem.choices = yesno;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

  menuitem.str = showclippystr;
  menuitem.value = &setup.enable_clippy;
  if(add_menuitem(grmenu, &menuitem) == -1)
    return 1;

  menuitem.str = slidepiecesstr;
  menuitem.value = &setup.slide_pieces;
  if(add_menuitem(grmenu, &menuitem) == -1)
    return 1;

  menuitem.str = showlegalstr;
  menuitem.value = &setup.show_legal_moves;
  if(add_menuitem(grmenu, &menuitem) == -1)
    return 1;

  menuitem.str = showthreatstr;
  menuitem.value = &setup.show_threatened;
  if(add_menuitem(grmenu, &menuitem) == -1)
    return 1;

  menuitem.str = deepthinkingstr;
  menuitem.value = &setup.deep_thinking;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

#ifdef SDLFUNCS
  menuitem.str = screenscalingstr;
  menuitem.value = &setup.screen_scale;
  menuitem.minvalue = &mindepth;
  menuitem.maxvalue = &maxblunders;
  menuitem.choices = 0;
  if(add_menuitem(grmenu, &menuitem) == -1)
    return 1;
#endif

  menuitem.minvalue = &minblunders;
  menuitem.str = timecontrolstr;
  menuitem.value = &setup.timecontrol;
  menuitem.maxvalue = &maxtimectrl;
  menuitem.choices = timecontrols;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;

  menuitem.str = timelimitstr;
  menuitem.value = 0;
  menuitem.time = &setup.timelimit;
//  menuitem.choices = 0;
//  menuitem.maxvalue = 0;
  if(add_menuitem(setmenu, &menuitem) == -1)
    return 1;
//  menuitem.time = 0;


  menuitem.str = boardrotationstr;
  menuitem.value = &setup.board_rotation;
  menuitem.choices = rotations;
  menuitem.maxvalue = &maxrotation;
  if(add_menuitem(grmenu, &menuitem) == -1)
    return 1;

  // quit game
  memset(&menuitem, 0, sizeof(menuitem));
  menuitem.str = quitgamestr;
  menuitem.fnptr = (void*)quit_game;
  if(add_menuitem(mainmenu, &menuitem) == -1)
    return 1;

  return 0;
}

#endif
