/* Nertz: multi-player card game
 * by Jonathan Cooper
 *
 * Actual game routines
 */

#include "DeskLib:WimpSWIs.h"
#include "DeskLib:Error.h"
#include "DeskLib:Event.h"
#include "DeskLib:Window.h"
#include "DeskLib:Wimp.h"
#include "DeskLib:Dialog.h"
#include "DeskLib:Template.h"
#include <stdlib.h>
#include <string.h>
#include "nertz.h"
#include "rand.h"

/* Prototyping */
icon_handle nertz_createicon(void);

/* Global vars */
char sc_title[30];

/* Shuffle a pack of 52 cards */
void shuffle_pack(card *pack) {
  int i, j;
  card swap;

  for (i=0; i<52; i++) {
    j = rand_int(52);
    if (i != j) {
      swap.suit = pack[i].suit;
      swap.number = pack[i].number;
      pack[i].suit = pack[j].suit;
      pack[i].number = pack[j].number;
      pack[j].suit = swap.suit;
      pack[j].number = swap.number;
    }
  }
}

/* Begin a single-player game */
void begin_game(void) {
  int i, j;
  card pack[52];

  /* Allocate memory for players */
  game_data.player = calloc(game_data.ai_players+1, sizeof(player_data));
  if (game_data.player == NULL) {
    Error_Report(0, "Unable to allocate memory for players.");
    return;
  }

  /* Allocate memory for centre piles */
  game_data.centre_pile = calloc((game_data.ai_players+1)*4, sizeof(centre_pile_str));
  if (game_data.centre_pile == NULL) {
    free(game_data.player);
    Error_Report(0, "Unable to allocate memory for card piles.");
    return;
  }
  game_data.centre_piles = -1;

  /* Build pack */
  for (i=0; i<4; i++) {
    for (j=0; j<13; j++) {
      pack[j + i*13].suit = i+1;
      pack[j + i*13].number = j+1;
    }
  }

  /* Deal pack */
  for (i=0; i<game_data.ai_players+1; i++) {
    shuffle_pack(pack);
    for (j=0; j<13; j++) {
      /* Nertz pile */
      game_data.player[i].nertz_pile[j].suit = pack[j].suit;
      game_data.player[i].nertz_pile[j].number = pack[j].number;
      game_data.player[i].nertz_cards = 12;
    }
    for (j=0; j<4; j++) {
      /* Patience piles */
      game_data.player[i].pile[0][j].suit = pack[13+j].suit;
      game_data.player[i].pile[0][j].number = pack[13+j].number;
      game_data.player[i].cards[j] = 0;
    }
    for (j=0; j<35; j++) {
      /* Turnover pile */
      game_data.player[i].turnover_pile[j].suit = pack[17+j].suit;
      game_data.player[i].turnover_pile[j].number = pack[17+j].number;
      game_data.player[i].turnover_cards = 35;
      game_data.player[i].turnover_pos = 0;
    }
  }

  /* Create window */
  gamewin = nertz_createwindow();
  if (gamewin == NULL) {
    /* Error_Report(0, "Unable to create main window."); */
    free(game_data.player);
    free(game_data.centre_pile);
    return;
  }

  /* Set up event handlers for window */
  Event_Claim(event_REDRAW, gamewin, event_ANY, gamewin_redraw, 0);
  Event_Claim(event_CLOSE, gamewin, event_ANY, gamewin_close, 0);
  Event_Claim(event_CLICK, gamewin, event_ANY, gamewin_click, 0);
  Event_Claim(event_USERDRAG, event_ANY, event_ANY, gamewin_drag, 0);
  Event_Claim(event_SCROLL, gamewin, event_ANY, gamewin_scroll, 0);
  /* Handle AI turns */
  if (game_data.ai_players > 0)
    Event_Claim(event_NULL, event_ANY, event_ANY, gamewin_null, 0);

  /* Show window */
  Window_Show(gamewin, open_CENTERED);

  playing = TRUE;
}

window_handle nertz_createwindow(void) {
  /* Create main game window */
  window_handle w;
  window_block blk;
  BOOL failed;

  /* Window height: 16 gap at top, 96 per row of centre cards, 128 gap,
     80 for nertz pile, etc., 12*40 for rest of patience piles, 16 gap. */
  gamewin_h = (game_data.ai_players+1) / 3 * 96 + 16 + 128 + 80 + 12*40 + 16;
  blk.screenrect.min.x = 0;
  blk.screenrect.min.y = -gamewin_h;
  blk.screenrect.max.x = 824;
  blk.screenrect.max.y = 0;
  blk.scroll.x = 0;
  blk.scroll.y = 0;
  blk.behind = -1;
  blk.flags.value = 15; /* bits 0,1,2,3 */
  blk.colours[0] = 7;   /* Title foreground */
  blk.colours[1] = 2;   /* Title background */
  blk.colours[2] = 7;   /* Work area foreground */
  blk.colours[3] = 10;  /* Work area background */
  blk.colours[4] = 3;   /* Scrollbar outer */
  blk.colours[5] = 1;   /* Scrollbar inner */
  blk.colours[6] = 12;  /* Title background (input) */
  blk.colours[7] = 0;   /* Extra flags byte */
  blk.workarearect.min.x = 0;
  blk.workarearect.min.y = -gamewin_h;
  blk.workarearect.max.x = 824;
  blk.workarearect.max.y = 0;
  blk.titleflags.value = 45;  /* bits 0,2,3,5 */
  /* blk.titleflags.data.indirected = 1; */
  blk.workflags.value = 0x3000; /* bits 12,13 */
  blk.spritearea = NULL;
  blk.minsize.x = 0;
  blk.minsize.y = 0;
  strcpy(blk.title.text, "Nertz");
  blk.numicons = 0;
  failed = Error_Check(Wimp_CreateWindow(&blk, &w));
  if (failed) return NULL;
  return w;
}

icon_handle nertz_createicon(void) {
  icon_createblock icn;
  icon_handle i;
  int failed;

  icn.window = gamewin;
  icn.icondata.workarearect.min.x = 816 - 142;
  icn.icondata.workarearect.min.y = -gamewin_h + 8 + 66;
  icn.icondata.workarearect.max.x = 816;
  icn.icondata.workarearect.max.y = -gamewin_h + 8;
  icn.icondata.flags.value = 192944445;
  icn.icondata.data.indirecttext.buffer = "NERTZ";
  icn.icondata.data.indirecttext.validstring = "R6";
  icn.icondata.data.indirecttext.bufflen = 6;
  failed = Error_Check(Wimp_CreateIcon(&icn, &i));
  if (failed) return NULL;
  return i;
}

/* AI turn */
/* returns TRUE if player has won, FALSE otherwise */
int ai_turn(player_data *pl) {
  int i, j;

  /* Check if player has won */
  if (pl->nertz_cards == -1) return TRUE;

  /* Check if nertz card is an ace */
  if (pl->nertz_pile[pl->nertz_cards].number == 1) {
    /* Start a new centre pile */
    i = ++game_data.centre_piles;
    game_data.centre_pile[i].suit = pl->nertz_pile[pl->nertz_cards].suit;
    game_data.centre_pile[i].top = 1;
    pl->nertz_cards--;
    mark_for_redraw(pile_CENTRE, i);
    return FALSE;
  }

  /* Check if a nertz card can be moved to the centre */
  for (i=0; i<=game_data.centre_piles; i++) {
    if (game_data.centre_pile[i].suit == pl->nertz_pile[pl->nertz_cards].suit && game_data.centre_pile[i].top == pl->nertz_pile[pl->nertz_cards].number - 1) {
      /* Place nertz card on centre pile & leave fn */
      game_data.centre_pile[i].top++;
      pl->nertz_cards--;
      mark_for_redraw(pile_CENTRE, i);
      return FALSE;
    }
  }

  /* Check if nertz card can be moved to one of patience piles */
  for (i=0; i<4; i++) {
    if ((pl->cards[i] == -1) || ((pl->nertz_pile[pl->nertz_cards].suit % 2) != (pl->pile[pl->cards[i]][i].suit % 2) && pl->nertz_pile[pl->nertz_cards].number == pl->pile[pl->cards[i]][i].number - 1)) {
      /* Place nertz card on patience pile and leave fn */
      pl->cards[i]++;
      pl->pile[pl->cards[i]][i].suit = pl->nertz_pile[pl->nertz_cards].suit;
      pl->pile[pl->cards[i]][i].number = pl->nertz_pile[pl->nertz_cards].number;
      pl->nertz_cards--;
      return FALSE;
    }
  }

  /* Check if a card from a patience pile can be moved to the centre */
  for (i=0; i<4; i++) {
    /* Is card an ace? */
    if (pl->pile[pl->cards[i]][i].number == 1) {
      /* Create new centre pile */
      j = ++game_data.centre_piles;
      game_data.centre_pile[j].suit = pl->pile[pl->cards[i]][i].suit;
      game_data.centre_pile[j].top = 1;
      pl->cards[i]--;
      mark_for_redraw(pile_CENTRE, j);
      return FALSE;
    }
    /* Check for place on existing piles */
    for (j=0; j<=game_data.centre_piles; j++) {
      if (game_data.centre_pile[j].suit == pl->pile[pl->cards[i]][i].suit && game_data.centre_pile[j].top == pl->pile[pl->cards[i]][i].number - 1) {
        /* Move card and leave fn */
        game_data.centre_pile[j].top++;
        pl->cards[i]--;
        mark_for_redraw(pile_CENTRE, j);
        return FALSE;
      }
    }
  }

  /* Check if card from turnover pile can be moved to centre */
  for (i=0; i<game_data.centre_piles; i++) {
    if (pl->turnover_pile[pl->turnover_pos].suit == game_data.centre_pile[i].suit && pl->turnover_pile[pl->turnover_pos].number == game_data.centre_pile[i].top + 1) {
      /* Move card and leave fn */
      game_data.centre_pile[i].top++;
      pl->turnover_cards--;
      pl->turnover_pos--;
      if (pl->turnover_pos != pl->turnover_cards) {
        for (j=pl->turnover_pos+1; j<pl->turnover_cards; j++) {
          pl->turnover_pile[j].suit = pl->turnover_pile[j+1].suit;
          pl->turnover_pile[j].number = pl->turnover_pile[j+1].number;
        }
      }
      mark_for_redraw(pile_CENTRE, i);
      return FALSE;
    }
  }

  /* Turn over turnover pile */
  pl->turnover_pos = (pl->turnover_pos + 3) % pl->turnover_cards;

  return FALSE;
}

void game_over(player_data *pl) {
  dialog dbox;
  char *message;
  int *s;

  playing = FALSE;

  /* Free non-window specific event handlers */
  Event_Release(event_USERDRAG, event_ANY, event_ANY, gamewin_drag, 0);
  if (game_data.ai_players > 0) {
    Event_Release(event_NULL, event_ANY, event_ANY, gamewin_null, 0);
    /*-game_data.difficulty = 10000;-*/
  }
  /* Delete game window */
  Window_Delete(gamewin);

  /* Display scores */
  s = calculate_scores(pl);

  /* Display win message */
  if (pl == &game_data.player[0]) {
    message = "Congratulations! You won!";
  } else {
    message = "Commiserations. You have been beaten by the computer.";
  }
  dbox = Dialog_Create("Message", 0);
  Icon_SetText(dbox->window, 0, message);
  Dialog_Show(dbox);
  Dialog_WaitForClick(dbox);
  Dialog_Destroy(dbox);

  /* Free memory */
  free(game_data.player);
  free(game_data.centre_pile);

}

void load_scores_win(void) {
  /* Read in window & icon definitions from template file, so they *
   * can be used to dynamically create the scores window later     */
  int i;
  os_error *e;

  /* Create window from template */
  scoreswin = Window_Create("scores", template_TITLEMIN);
  if (scoreswin == NULL)
    Error_ReportFatal(0, "Failed to create scores window.");
  /* Take copy of window data */
  Window_GetInfo3(scoreswin, &scoresinfo);
  /* Copy title string */
  strcpy(sc_title, scoresinfo.block.title.indirecttext.buffer);
  /* Ensure correct number of icons */
  if (scoresinfo.block.numicons != 10)
    Error_ReportFatal(0, "Scores window has been fatally modified");
  /* Read in icon data */
  for (i=0; i<10; i++) {
    e = Wimp_GetIconState(scoreswin, i, &scicons[i]);
    if (e != NULL)
      Error_ReportFatal(0, "Error reading icon data: %s", e->errmess);
    strcpy(sctext[i], scicons[i].data.indirecttext.buffer);
    strcpy(scvalid[i], scicons[i].data.indirecttext.validstring);
  }
  /* Set some original values */
  scores_minwork = scoresinfo.block.workarearect;
  scores_minscreen = scoresinfo.block.screenrect;
  /* Delete window */
  Window_Delete(scoreswin);
}

void display_scores(int *round_scores) {
  /* Create scores window and fill in values */
  int pls = game_data.ai_players + 1;
  os_error *e;
  int i, p, icnwidth;
  icon_createblock icnblk;
  icon_handle icn;

  /* Reset window to original size */
  scoresinfo.block.workarearect = scores_minwork;
  scoresinfo.block.screenrect = scores_minscreen;
  /* Expand window to fit all players */
  icnwidth = scicons[SCICN_COMPUTER].workarearect.max.x - scicons[SCICN_COMPUTER].workarearect.min.x;
  if (pls > 2) {
    scoresinfo.block.workarearect.max.x += (pls - 2) * icnwidth;
  }
  scoresinfo.block.screenrect.max.x = scoresinfo.block.workarearect.max.x;
  /* Set as having no icons */
  scoresinfo.block.numicons = 0;
  /* Set scroll offset to 0 */
  scoresinfo.block.scroll.x = 0;
  scoresinfo.block.scroll.y = 0;
  /* Set correct title */
  scoresinfo.block.title.indirecttext.buffer = sc_title;
  scoresinfo.block.title.indirecttext.validstring = NULL;
  /* Create window */
  e = Wimp_CreateWindow(&scoresinfo.block, &scoreswin);
  if (e) {
    Error_ReportFatal(0, "Error creating scores window: %s", e->errmess);
  }
  /* Create basic icons */
  for (i=0; i<SCICN_COMPUTER; i++) {
    icnblk.icondata = scicons[i];
    icnblk.window = scoreswin;
    /* Make sure strings OK */
    icnblk.icondata.data.indirecttext.buffer = sctext[i];
    icnblk.icondata.data.indirecttext.validstring = scvalid[i];
    if (i == SCICN_H_MATCH) {
      /* Fill in round score */
      sprintf(icnblk.icondata.data.indirecttext.buffer, "%d", round_scores[0]);
    }
    if (i == SCICN_H_TOTAL) {
      /* Fill in total score */
      sprintf(icnblk.icondata.data.indirecttext.buffer, "%d", scores[0]);
    }
    e = Wimp_CreateIcon(&icnblk, &icn);
    if (e) {
      Error_ReportFatal(0, "Error creating icon %d: %s", i, e->errmess);
    }
    if (icn != i) { /* Unexpected icon number */
      Error_ReportFatal(0, "Error: unexpected icon number!");
    }
  }
  /* Allocate string pointer memory for computer player icons */
  sccsize = pls-1;
  scctext = calloc(pls-1,  sizeof(char*));
  if (scctext == NULL) {
    Error_ReportFatal(0, "Failed to allocate memory (1) for ind.text");
  }
  /* Create extra columns for computer players */
  if (pls > 1) {
    for (p=1; p<pls; p++) {
      for (i=0; i<3; i++) {
        icnblk.icondata = scicons[SCICN_COMPUTER+i];
        /* Alter x-position */
        icnblk.icondata.workarearect.min.x += icnwidth*(p-1);
        icnblk.icondata.workarearect.max.x += icnwidth*(p-1);
        /* Allocate memory for strings */
        scctext[p-1] = calloc(15, sizeof(char));
        if (scctext[p-1] == NULL) {
          Error_ReportFatal(0, "Failed to allocate memory (2) for ind.text");
        }
        icnblk.icondata.data.indirecttext.buffer = scctext[p-1];
        /* Set up correct validation string */
        icnblk.icondata.data.indirecttext.validstring = scvalid[SCICN_COMPUTER+i];
        /* Correct name / enter score */
        switch (i) {
          case 0: /* player name */
            sprintf(icnblk.icondata.data.indirecttext.buffer, "Computer %d", p);
            break;
          case 1: /* round score */
            sprintf(icnblk.icondata.data.indirecttext.buffer, "%d", round_scores[p]);
            break;
          case 2: /* total score */
            sprintf(icnblk.icondata.data.indirecttext.buffer, "%d", scores[p]);
            break;
        }
        /* Create */
        icnblk.window = scoreswin;
        e = Wimp_CreateIcon(&icnblk, &icn);
        if (e) {
          Error_ReportFatal(0, "Error creating icon %d.%d: %s", p, i, e->errmess);
        }
        if (icn != (SCICN_COMPUTER+(p-1)*3+i)) { /* Unexpected icon number */
          Error_ReportFatal(0, "Error: unexpected icon number!");
        }
      }
    }
  }
  /* Install event handlers */
  Event_Claim(event_CLICK, scoreswin, SCICN_CONTINUE, sc_continue, round_scores);
  Event_Claim(event_CLICK, scoreswin, SCICN_ABANDON, sc_abandon, round_scores);
  /* Show window */
  Window_Show(scoreswin, open_CENTERED);
}

int *calculate_scores(player_data *winpl) {
  /* Update match scores: 1 point per card in centre, -2 points per card
   * in nertz pile, 5 points for winning
   */
  int *round_scores;
  int i, j;
  int cards_left;
  char osclistr[256];

  /* Allocate memory for this round's scores */
  round_scores = calloc(game_data.ai_players+1, sizeof(int));
  if (round_scores == NULL) {
    Error_ReportFatal(0, "Unable to allocate memory for round scores");
  }
  /* Calculate scores */
  for (i=0; i<game_data.ai_players+1; i++) {
    sprintf(osclistr, "Set Nertz$OrigScore%d %d", i, round_scores[i]);
    system(osclistr);
    /* Did this player win? */
    if (&(game_data.player[i]) == winpl) {
      round_scores[i] = 5;
      sprintf(osclistr, "Set Nertz$Winner%d %d", i, i);
      system(osclistr);
    }
    /* Count nertz cards remaining */
    cards_left = game_data.player[i].nertz_cards + 1;
    sprintf(osclistr, "Set Nertz$NertzCards%d %d", i, cards_left);
    system(osclistr);
    round_scores[i] -= (cards_left * 2);
    /* Add on remaining patience cards & turnover cards */
    cards_left += game_data.player[i].turnover_cards;
    sprintf(osclistr, "Set Nertz$Turnover%d %d", i, game_data.player[i].turnover_cards);
    system(osclistr);
    for (j=0; j<4; j++) {
      cards_left += game_data.player[i].cards[j] + 1;
      sprintf(osclistr, "Set Nertz$Patience%d%d %d", i, j, game_data.player[i].cards[j] + 1);
      system(osclistr);
    }
    /* Subtract from 52 to find cards put out in centre */
    cards_left = 52 - cards_left;
    sprintf(osclistr, "Set Nertz$CardsLeft%d %d", i, cards_left);
    system(osclistr);
    round_scores[i] += cards_left;
    /* Update match scores */
    scores[i] += round_scores[i];
    sprintf(osclistr, "Set Nertz$FinScores%d \"%d %d\"", i, round_scores[i], scores[i]);
    system(osclistr);
  }
  /* Create scores window & show scores */
  display_scores(round_scores);
  return round_scores;
}
