unsigned opening_count = 0;
unsigned char longest_opening = 0;
struct opening *openings;

int ai_opening(struct board *brd, struct move *move, unsigned id, signed char *dontcommit)
{
  unsigned n;
  struct opening *opening = 0;
#ifdef DEBUG
  unsigned char piece;
  struct coord moves[MAX_MOVES];
  unsigned char move_count;
  struct coord king_pos = find_king(brd, !(turn_count & 1));

#endif

  if(!setup.book_openings) return 0;

  move->resign = 0;

  if(id == -1)
  {
    if(turn_count > longest_opening)
      return 0;

    for(n=0;n<opening_count;n++)
    {
      if(!(openings[n].moves_left & 0x80) && (!opening || !(rand()&0x1F)))
      {
        opening = &openings[n];
      }
    }
  }
  else
  {
    opening = &openings[id];
  }

  if(opening)
  {
    move->coords = opening->moves[turn_count-1];
#ifdef DEBUG
    if(!(piece = piece_in_tile(brd, move->coords.src.row, move->coords.src.col)))
    {
      errorinopening:printf("Error in opening %s!\n", opening->name);
      exit(1);
    }
    if((!!(piece & PC_BLACK)) == (turn_count&1))
      goto errorinopening;

    move_count = legal_moves(brd, move->coords.src, &king_pos, moves);
    if(!move_in_list(moves, move_count, &move->coords.dst))
      goto errorinopening;

#endif
    if(!dontcommit)
      move_piece(brd, move->coords.src, move->coords.dst);
    else
      dontcommit = 0;
    return 1;
  }

  return 0;
}

void prntjo(unsigned s, unsigned l)
{
  printf("Combining opening %s to %s to save memory.\n", openings[s].name, openings[l].name);
}

void join_openings(unsigned s, unsigned l)
{
  unsigned n;
  for(n=0;n<opening_count;n++)
  {
    if(openings[n].moves == openings[s].moves)
    {
      if(openings[n].move_count > openings[s].move_count)
        return;
    }
  }

  free(openings[s].moves);

  for(n=0;n<opening_count;n++)
  {
    if(n == l || n == s) continue;
    if(openings[n].moves == openings[s].moves)
    {
      openings[n].moves = openings[l].moves;
      prntjo(n, l);
    }
  }

  openings[s].moves = openings[l].moves;
  prntjo(s, l);
}

void free_duplicate_openings(void)
{
  unsigned n, i, l, m;

  for(n=0;n<opening_count;n++)
  {
    m = openings[n].move_count;
    for(i=0;i<opening_count;i++)
    {
      if(i == n) continue;
      if(openings[i].moves == openings[n].moves) continue;

      l = openings[i].move_count;
      if(l < m && !memcmp(openings[n].moves, openings[i].moves, l*sizeof(openings[0].moves[0])))
        join_openings(i, n);
      else if(l >= m && !memcmp(openings[n].moves, openings[i].moves, m*sizeof(openings[0].moves[0])))
        join_openings(n, i);
    }
  }
}

void play_opening(unsigned opening)
{
  struct board *brd = &openings[opening].pos;
  struct move move;
  unsigned moves = openings[opening].move_count;
#ifdef DEBUG
  unsigned n;
#endif

  init_board(brd);

  do
  {
    turn_count++;
    ai_opening(brd, &move, opening, 0);
  }
  while(turn_count < moves);

  turn_count = 0;

#ifdef DEBUG
  for(n=0;n<opening_count;n++)
  {
    if(n == opening) continue;
    if(!memcmp(&openings[n].pos, &openings[opening].pos, sizeof(struct board)))
      printf("Warning: Openings %s and %s lead to identical positions!\n", openings[n].name, openings[opening].name);
  }
#endif
}

int add_opening(struct opening *opening)
{
  void *ptr;

  if(!opening_count)
  {
    openings = malloc(sizeof(struct opening));
    if(!openings)
      return 1;
  }
  else
  {
    ptr = realloc(openings, (opening_count+1)*sizeof(struct opening));
    if(!ptr) return 1;
    openings = ptr;
  }
  openings[opening_count] = *opening;

  if(longest_opening < opening->move_count)
    longest_opening = opening->move_count;
  printf("Initialized opening: %s\n", opening->name);

  play_opening(opening_count);

  opening_count++;

  free_duplicate_openings();

  return 0;
}

int add_move_to_opening(struct opening *opening, char *move)
{
  void *ptr;

  if(!opening->move_count)
  {
    opening->moves = malloc(sizeof(struct movecoords));
    if(!opening->moves) return 1;
  }
  else
  {
    ptr = realloc(opening->moves, (opening->move_count+1)*sizeof(struct movecoords));
    if(!ptr) return 1;
    opening->moves = ptr;
  }
  opening->moves[opening->move_count] = str_to_move(move, opening->move_count&1);
  opening->move_count++;

  return 0;
}

int init_openings(void)
{
  struct opening opening = { 0 };
  char *ptr;
  char buf[64];
  FILE *fp = fopen("openings.dat", "rt");

  if(!fp)
  {
    puts("Could not find opening book.");
    return 1;
  }

  while(fgets(buf, sizeof(buf), fp))
  {
    ptr = strchr(buf, '\n');
    if(ptr) *ptr = 0;

    if(buf[0] == '>')
    {
      strcpy(opening.name, buf+1);
      if(add_opening(&opening))
        outofmemory();
      memset(&opening, 0, sizeof(opening));
    }
    else
    {
      if(add_move_to_opening(&opening, buf))
        outofmemory();
    }
  }

  fclose(fp);

  printf("Initialized %u openings.\n", opening_count);

  return 0;
}

void reset_openings(void)
{
  unsigned n;

  for(n=0;n<opening_count;n++)
    openings[n].moves_left = openings[n].move_count;
}

#define MAX_OPENING_TURNS 64

void check_opening(struct movecoords *move)
{
  unsigned n;
  unsigned opening = -1;

  if(turn_count > MAX_OPENING_TURNS)
    for(n=0;n<opening_count;n++)
      if(openings[n].moves_left == 0xFF && !memcmp(&board, &openings[n].pos, sizeof(board)))
      {
        // the player found an alternate route to this opening...
        opening = n;
        break;
      }

  if(opening == -1 && turn_count > longest_opening)
    return;

  for(n=0;n<opening_count;n++)
  {
    if(turn_count <= openings[n].move_count
       && !memcmp(&openings[n].moves[turn_count-1], move, sizeof(struct movecoords)))
    {
      if(!--openings[n].moves_left)
      {
        show_dialog_nokey(openings[n].name);
        opening = -1;
        goto setff;
      }
    }
    else
      setff:openings[n].moves_left = 0xFF;
  }

  if(opening != -1)
    show_dialog_nokey(openings[opening].name);
}
