/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2006 Tom Ross
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "plugin.h"
#include "pluginbitmaps/card_deck.h"
#include "pluginbitmaps/card_back.h"
#include "lib/display_text.h"
#include "lib/highscore.h"
#include "lib/playback_control.h"

PLUGIN_HEADER

/* save files */
#define SCORE_FILE PLUGIN_GAMES_DIR "/blackjack.score"
#define SAVE_FILE  PLUGIN_GAMES_DIR "/blackjack.save"
#define NUM_SCORES 5

/* final game return status */
enum {
    BJ_LOSE,
    BJ_QUIT_WITHOUT_SAVING,
    BJ_QUIT,
    BJ_USB,
    BJ_END,
};

#if CONFIG_KEYPAD == RECORDER_PAD
#define BJACK_SELECT_NAME   "PLAY"
#define BJACK_STAY_NAME     "F1"
#define BJACK_QUIT_NAME     "OFF"
#define BJACK_DOUBLE_NAME   "F2"
#define BJACK_SELECT        BUTTON_PLAY
#define BJACK_QUIT          BUTTON_OFF
#define BJACK_MAX           (BUTTON_ON|BUTTON_UP)
#define BJACK_MIN           (BUTTON_ON|BUTTON_DOWN)
#define BJACK_STAY          BUTTON_F1
#define BJACK_DOUBLEDOWN    BUTTON_F2
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == ONDIO_PAD
#define BJACK_SELECT_NAME    "MENU"
#define BJACK_STAY_NAME     "RIGHT"
#define BJACK_QUIT_NAME     "OFF"
#define BJACK_DOUBLE_NAME   "UP"
#define BJACK_SELECT        BUTTON_MENU
#define BJACK_QUIT          BUTTON_OFF
#define BJACK_STAY          BUTTON_RIGHT
#define BJACK_DOUBLEDOWN    BUTTON_UP
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == IRIVER_H10_PAD
#define BJACK_SELECT_NAME    "PLAY"
#define BJACK_STAY_NAME     ">>|"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "|<<"
#define BJACK_SELECT        BUTTON_PLAY
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_STAY          BUTTON_FF
#define BJACK_DOUBLEDOWN    BUTTON_REW
#define BJACK_UP            BUTTON_SCROLL_UP
#define BJACK_DOWN          BUTTON_SCROLL_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
      (CONFIG_KEYPAD == IRIVER_H300_PAD)
#define BJACK_SELECT_NAME    "ON"
#define BJACK_STAY_NAME     "REC"
#define BJACK_QUIT_NAME     "OFF"
#define BJACK_DOUBLE_NAME   "SELECT"
#define BJACK_SELECT        BUTTON_ON
#define BJACK_QUIT          BUTTON_OFF
#define BJACK_STAY          BUTTON_REC
#define BJACK_DOUBLEDOWN    BUTTON_SELECT
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
      (CONFIG_KEYPAD == IPOD_3G_PAD) || \
      (CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "RIGHT"
#define BJACK_RESUME_NAME   "PLAY"
#define BJACK_QUIT_NAME     "MENU"
#define BJACK_DOUBLE_NAME   "LEFT"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_MENU
#define BJACK_STAY          BUTTON_RIGHT
#define BJACK_DOUBLEDOWN    BUTTON_LEFT
#define BJACK_UP            BUTTON_SCROLL_FWD
#define BJACK_DOWN          BUTTON_SCROLL_BACK
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "REC"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "PLAY"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           (BUTTON_PLAY|BUTTON_UP)
#define BJACK_MIN           (BUTTON_PLAY|BUTTON_DOWN)
#define BJACK_STAY          BUTTON_REC
#define BJACK_DOUBLEDOWN    BUTTON_PLAY
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
#define BJACK_SELECT_NAME    "MODE"
#define BJACK_STAY_NAME     "MODE"
#define BJACK_QUIT_NAME     "PLAY"
#define BJACK_DOUBLE_NAME   "SELECT"
#define BJACK_SELECT        BUTTON_MODE
#define BJACK_QUIT          BUTTON_PLAY
#define BJACK_MAX           (BUTTON_EQ|BUTTON_UP)
#define BJACK_MIN           (BUTTON_EQ|BUTTON_DOWN)
#define BJACK_STAY          BUTTON_MODE
#define BJACK_DOUBLEDOWN    BUTTON_SELECT
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == GIGABEAT_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "VOL-"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "A"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           BUTTON_VOL_UP
#define BJACK_MIN           BUTTON_VOL_DOWN
#define BJACK_STAY          BUTTON_VOL_DOWN
#define BJACK_DOUBLEDOWN    BUTTON_A
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == SANSA_E200_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "RIGHT"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "LEFT"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           BUTTON_UP
#define BJACK_MIN           BUTTON_DOWN
#define BJACK_STAY          BUTTON_RIGHT
#define BJACK_DOUBLEDOWN    BUTTON_LEFT
#define BJACK_UP            BUTTON_SCROLL_FWD
#define BJACK_DOWN          BUTTON_SCROLL_BACK
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == SANSA_FUZE_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "RIGHT"
#define BJACK_QUIT_NAME     "HOME"
#define BJACK_DOUBLE_NAME   "LEFT"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          (BUTTON_HOME|BUTTON_REPEAT)
#define BJACK_MAX           BUTTON_UP
#define BJACK_MIN           BUTTON_DOWN
#define BJACK_STAY          BUTTON_RIGHT
#define BJACK_DOUBLEDOWN    BUTTON_LEFT
#define BJACK_UP            BUTTON_SCROLL_FWD
#define BJACK_DOWN          BUTTON_SCROLL_BACK
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == SANSA_C200_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "RIGHT"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "LEFT"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           BUTTON_VOL_UP
#define BJACK_MIN           BUTTON_VOL_DOWN
#define BJACK_STAY          BUTTON_RIGHT
#define BJACK_DOUBLEDOWN    BUTTON_LEFT
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == SANSA_CLIP_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "RIGHT"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "LEFT"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           BUTTON_VOL_UP
#define BJACK_MIN           BUTTON_VOL_DOWN
#define BJACK_STAY          BUTTON_RIGHT
#define BJACK_DOUBLEDOWN    BUTTON_LEFT
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == SANSA_M200_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "RIGHT"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "LEFT"
#define BJACK_SELECT        (BUTTON_SELECT | BUTTON_REL)
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           BUTTON_VOL_UP
#define BJACK_MIN           BUTTON_VOL_DOWN
#define BJACK_STAY          BUTTON_RIGHT
#define BJACK_DOUBLEDOWN    BUTTON_LEFT
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == TATUNG_TPJ1022_PAD
#define BJACK_SELECT_NAME    "MAIN"
#define BJACK_STAY_NAME     "MENU"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "DOWN"
#define BJACK_SELECT        BUTTON_MAIN
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           (BUTTON_REC|BUTTON_UP)
#define BJACK_MIN           (BUTTON_REC|BUTTON_DOWN)
#define BJACK_STAY          BUTTON_MENU
#define BJACK_DOUBLEDOWN    BUTTON_DOWN
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
#define BJACK_SELECT_NAME    "PLAY"
#define BJACK_STAY_NAME     "VOL-"
#define BJACK_QUIT_NAME     "BACK"
#define BJACK_DOUBLE_NAME   "SELECT"
#define BJACK_SELECT        BUTTON_PLAY
#define BJACK_QUIT          BUTTON_BACK
#define BJACK_MAX           BUTTON_VOL_UP
#define BJACK_MIN           BUTTON_VOL_DOWN
#define BJACK_STAY          BUTTON_VOL_DOWN
#define BJACK_DOUBLEDOWN    BUTTON_SELECT
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == MROBE100_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "DISPLAY"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "DOWN"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           BUTTON_MENU
#define BJACK_MIN           BUTTON_DISPLAY
#define BJACK_STAY          BUTTON_DISPLAY
#define BJACK_DOUBLEDOWN    BUTTON_DOWN
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
#define BJACK_SELECT_NAME    "RC","PLAY"
#define BJACK_STAY_NAME     "RC", ">>|"
#define BJACK_QUIT_NAME     "RC_REC"
#define BJACK_DOUBLE_NAME   "RC_REW"
#define BJACK_SELECT        BUTTON_RC_PLAY
#define BJACK_QUIT          BUTTON_RC_REC
#define BJACK_STAY          BUTTON_RC_FF
#define BJACK_DOUBLEDOWN    BUTTON_RC_REW
#define BJACK_UP            BUTTON_RC_VOL_UP
#define BJACK_DOWN          BUTTON_RC_VOL_DOWN
#define BJACK_RIGHT         BUTTON_RC_FF
#define BJACK_LEFT          BUTTON_RC_REW

#elif CONFIG_KEYPAD == COWON_D2_PAD
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "-"
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_DOUBLEDOWN    BUTTON_MINUS

#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "PLAY"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "CUSTOM"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_STAY          BUTTON_PLAY
#define BJACK_MAX           (BUTTON_CUSTOM|BUTTON_UP)
#define BJACK_MIN           (BUTTON_CUSTOM|BUTTON_DOWN)
#define BJACK_DOUBLEDOWN    BUTTON_CUSTOM
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "VOL-"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "MENU"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           BUTTON_VOL_UP
#define BJACK_MIN           BUTTON_VOL_DOWN
#define BJACK_STAY          BUTTON_VOL_DOWN
#define BJACK_DOUBLEDOWN    BUTTON_MENU
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_RIGHT
#define BJACK_LEFT          BUTTON_LEFT

#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
#define BJACK_SELECT_NAME   "MENU"
#define BJACK_STAY_NAME     "VOL-"
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "PLAY"
#define BJACK_SELECT        BUTTON_MENU
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_MAX           BUTTON_VOL_UP
#define BJACK_MIN           BUTTON_VOL_DOWN
#define BJACK_STAY          BUTTON_VOL_DOWN
#define BJACK_DOUBLEDOWN    BUTTON_PLAY
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_NEXT
#define BJACK_LEFT          BUTTON_PREV

#elif CONFIG_KEYPAD == ONDAVX747_PAD
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_DOUBLE_NAME   "Vol-"
#define BJACK_QUIT          BUTTON_POWER
#define BJACK_DOUBLEDOWN    BUTTON_VOL_DOWN

#elif CONFIG_KEYPAD == ONDAVX777_PAD
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_QUIT          BUTTON_POWER

#elif CONFIG_KEYPAD == MROBE500_PAD
#define BJACK_QUIT_NAME     "POWER"
#define BJACK_QUIT          BUTTON_POWER

#elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
#define BJACK_SELECT_NAME  "PLAY"
#define BJACK_STAY_NAME    "RECORD"
#define BJACK_QUIT_NAME    "REW"
#define BJACK_DOUBLE_NAME  "FFWD"
#define BJACK_SELECT       BUTTON_PLAY
#define BJACK_QUIT         BUTTON_REW
#define BJACK_STAY         BUTTON_REC
#define BJACK_DOUBLEDOWN   BUTTON_FFWD
#define BJACK_UP           BUTTON_UP
#define BJACK_DOWN         BUTTON_DOWN
#define BJACK_RIGHT        BUTTON_RIGHT
#define BJACK_LEFT         BUTTON_LEFT

#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
#define BJACK_SELECT_NAME    "OK"
#define BJACK_STAY_NAME     "CANCEL"
#define BJACK_QUIT_NAME     "REC"
#define BJACK_DOUBLE_NAME   "MENU"
#define BJACK_SELECT        BUTTON_OK
#define BJACK_QUIT          BUTTON_REC
#define BJACK_STAY          BUTTON_CANCEL
#define BJACK_DOUBLEDOWN    BUTTON_MENU
#define BJACK_UP            BUTTON_UP
#define BJACK_DOWN          BUTTON_DOWN
#define BJACK_RIGHT         BUTTON_NEXT
#define BJACK_LEFT          BUTTON_PREV

#elif CONFIG_KEYPAD == MPIO_HD200_PAD
#define BJACK_SELECT_NAME    "SELECT"
#define BJACK_STAY_NAME     "RIGHT"
#define BJACK_RESUME_NAME   "PLAY"
#define BJACK_QUIT_NAME     "REC+PLAY"
#define BJACK_DOUBLE_NAME   "LEFT"
#define BJACK_SELECT        BUTTON_SELECT
#define BJACK_QUIT          (BUTTON_REC|BUTTON_PLAY)
#define BJACK_STAY          BUTTON_VOL_UP
#define BJACK_DOUBLEDOWN    BUTTON_VOL_DOWN
#define BJACK_UP            BUTTON_PREV
#define BJACK_DOWN          BUTTON_NEXT
#define BJACK_RIGHT         BUTTON_VOL_UP
#define BJACK_LEFT          BUTTON_VOL_DOWN

#else
#error No keymap defined!
#endif

#ifdef HAVE_TOUCHSCREEN
#ifndef BJACK_DOUBLEDOWN
#define BJACK_DOUBLEDOWN    BUTTON_MIDLEFT
#define BJACK_DOUBLE_NAME   "BUTTON_MIDLEFT"
#endif
#ifndef BJACK_SELECT
#define BJACK_SELECT        BUTTON_CENTER
#define BJACK_SELECT_NAME   "BUTTON_CENTER"
#endif
#ifndef BJACK_MAX
#define BJACK_MAX           BUTTON_TOPRIGHT
#endif
#ifndef BJACK_MIN
#define BJACK_MIN           BUTTON_TOPLEFT
#endif
#ifndef BJACK_STAY
#define BJACK_STAY          BUTTON_BOTTOMLEFT
#define BJACK_STAY_NAME     "BUTTON_BOTTOMLEFT"
#endif
#ifndef BJACK_UP
#define BJACK_UP            BUTTON_TOPMIDDLE
#endif
#ifndef BJACK_DOWN
#define BJACK_DOWN          BUTTON_BOTTOMMIDDLE
#endif
#ifndef BJACK_RIGHT
#define BJACK_RIGHT         BUTTON_MIDRIGHT
#endif
#ifndef BJACK_LEFT
#define BJACK_LEFT          BUTTON_MIDLEFT
#endif

#endif

#ifdef HAVE_LCD_COLOR
#define BG_COLOR LCD_RGBPACK(0,157,0)
#define FG_COLOR LCD_WHITE
#elif LCD_DEPTH > 1
#define BG_COLOR LCD_WHITE
#define FG_COLOR LCD_BLACK
#endif

#define CARD_WIDTH  BMPWIDTH_card_back
#define CARD_HEIGHT BMPHEIGHT_card_back

/* This is the max amount of cards onscreen before condensing */
#define MAX_CARDS LCD_WIDTH/(CARD_WIDTH+4)

extern const fb_data card_deck[];
extern const fb_data card_back[];

#define NEXT_CARD bj->player_cards[done][bj->num_player_cards[done]]

/* dealer and player card positions */
unsigned int dealer_x, dealer_y, player_x, player_y;

typedef struct card {
    unsigned int value; /* Card's value in Blackjack */
    unsigned int num;        /* Value on card face 0-12 (0=Ace, 1=2, 11=Q) */
    unsigned int suit;       /* 0:Spades, 1:Hearts, 2: Clubs; 3: Diamonds */
    bool is_soft_ace;
} card;

typedef struct game_context {
    struct card player_cards[2][22]; /* 22 Cards means the deal was all aces */
    struct card dealer_cards[22];    /* That is the worst-case scenario */
    unsigned int player_total;
    unsigned int dealer_total;
    signed int player_money;
    unsigned int num_player_cards[2];
    unsigned int num_dealer_cards;
    unsigned int current_bet;
    unsigned int split_status; /* 0 = split hasn't been asked,         *
                                * 1 = split did not occur              *
                                * 2 = split occurred                   *
                                * 3 = split occurred and 1st hand done */
    bool is_blackjack;
    bool end_hand;
    bool asked_insurance;
} game_context;

static bool resume = false;
static bool resume_file = false;
static struct highscore highscores[NUM_SCORES];

/*****************************************************************************
* blackjack_init() initializes blackjack data structures.
******************************************************************************/
static void blackjack_init(struct game_context* bj) {
    /* seed the rand generator */
    rb->srand(*rb->current_tick);

    /* reset card positions */
    dealer_x = 4;
    dealer_y = LCD_HEIGHT/4 - CARD_HEIGHT/2;
    player_x = 4;
    player_y = LCD_HEIGHT - LCD_HEIGHT/4 - CARD_HEIGHT/2;

    /* check for resumed game */
    if(resume) return;

    /* reset scoring */
    bj->player_total = 0;
    bj->dealer_total = 0;
    bj->num_player_cards[0] = 2;
    bj->num_player_cards[1] = 0;
    bj->num_dealer_cards = 2;
    bj->end_hand = false;
    bj->split_status = 0;
    bj->is_blackjack = false;
    bj->asked_insurance = false;
}

/*****************************************************************************
* blackjack_drawtable() draws the table and some text.
******************************************************************************/
static void blackjack_drawtable(struct game_context* bj) {
    unsigned int w, h, y_loc;
    char str[10];

#if LCD_HEIGHT <= 64
    rb->lcd_getstringsize("Bet", &w, &h);
    rb->lcd_putsxy(LCD_WIDTH - w, 2*h + 1, "Bet");
    rb->snprintf(str, 9, "$%d", bj->current_bet);
    rb->lcd_getstringsize(str, &w, &h);
    rb->lcd_putsxy(LCD_WIDTH - w, 3*h + 1, str);
    y_loc = LCD_HEIGHT/2;
#else
    rb->lcd_getstringsize("Bet", &w, &h);
    rb->lcd_putsxy(LCD_WIDTH - w, 5*h / 2, "Bet");
    rb->snprintf(str, 9, "$%d", bj->current_bet);
    rb->lcd_getstringsize(str, &w, &h);
    rb->lcd_putsxy(LCD_WIDTH - w, 7*h / 2, str);
    rb->lcd_hline(0, LCD_WIDTH, LCD_HEIGHT/2);
    y_loc = LCD_HEIGHT/2 + h;
#endif

    rb->lcd_putsxy(0,0, "Dealer");
    rb->lcd_getstringsize("Player", &w, &h);
    rb->lcd_putsxy(0, y_loc, "Player");
    rb->lcd_getstringsize("Total", &w, &h);
    rb->lcd_putsxy(LCD_WIDTH - w, y_loc, "Total");
    rb->lcd_getstringsize("Money", &w, &h);
    rb->lcd_putsxy(LCD_WIDTH - w, 0, "Money");
    rb->snprintf(str, 9, "$%d", bj->player_money - bj->current_bet);
    rb->lcd_getstringsize(str, &w, &h);
    rb->lcd_putsxy(LCD_WIDTH - w, h + 1, str);
    rb->snprintf(str, 3, "%d", bj->player_total);
    rb->lcd_getstringsize(str, &w, &h);
    rb->lcd_putsxy(LCD_WIDTH - w, y_loc + h, str);
}

/*****************************************************************************
* find_value() is passed a card and returns its blackjack value.
******************************************************************************/
static unsigned int find_value(unsigned int number) {
    unsigned int thisValue;
    if (number == 0)
        thisValue = 11;   /* Aces get a value of 11 at first */
    else if (number < 10)
        thisValue = number + 1;
    else
        thisValue = 10;   /* Anything 10 or higher gets a value of 10 */

    return thisValue;
}

/*****************************************************************************
* draw_card() draws a card to the screen.
******************************************************************************/
static void draw_card(struct card temp_card, bool shown,
                      unsigned int x, unsigned int y) {
    if(shown)
        rb->lcd_bitmap_part(card_deck, CARD_WIDTH*temp_card.num,
                            CARD_HEIGHT*temp_card.suit,
                            STRIDE( SCREEN_MAIN, BMPWIDTH_card_deck,
                                    BMPHEIGHT_card_deck),
                            x+1, y+1, CARD_WIDTH, CARD_HEIGHT);
    else
        rb->lcd_bitmap(card_back, x+1, y+1,CARD_WIDTH, CARD_HEIGHT);
#if LCD_DEPTH > 1
    rb->lcd_set_foreground(LCD_BLACK);
#endif

    /* Print outlines */
#if CARD_WIDTH >= 26
    rb->lcd_hline(x+2, x+CARD_WIDTH-1, y);
    rb->lcd_hline(x+2, x+CARD_WIDTH-1, y+CARD_HEIGHT+1);
    rb->lcd_vline(x, y+2, y+CARD_HEIGHT-3);
    rb->lcd_vline(x+CARD_WIDTH+1, y+2, y+CARD_HEIGHT-1);
    rb->lcd_drawpixel(x+1, y+1);
    rb->lcd_drawpixel(x+1, y+CARD_HEIGHT);
    rb->lcd_drawpixel(x+CARD_WIDTH, y+1);
    rb->lcd_drawpixel(x+CARD_WIDTH, y+CARD_HEIGHT);
#else
    rb->lcd_hline(x+1, x+CARD_WIDTH, y);
    rb->lcd_hline(x+1, x+CARD_WIDTH, y+CARD_HEIGHT+1);
    rb->lcd_vline(x, y+1, y+CARD_HEIGHT);
    rb->lcd_vline(x+CARD_WIDTH+1, y+1, y+CARD_HEIGHT);
#endif

#if LCD_DEPTH > 1
    rb->lcd_set_foreground(FG_COLOR);
#endif
}

/*****************************************************************************
* new_card() initializes a new card and gives it values.
******************************************************************************/
static struct card new_card(void) {
    struct card new_card;
    new_card.suit = rb->rand()%4; /* Random number 0-3 */
    new_card.num = rb->rand()%13; /* Random number 0-12 */
    new_card.value = find_value(new_card.num);
    new_card.is_soft_ace = (new_card.num == 0);
    return new_card;
}

/*****************************************************************************
* deal_init_card() deals and draws to the screen the player's and dealer's
* initial cards.
******************************************************************************/
static void deal_init_cards(struct game_context* bj) {
    bj->dealer_cards[0] = new_card();
    bj->dealer_total += bj->dealer_cards[0].value;

    draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);

    bj->dealer_cards[1] = new_card();
    bj->dealer_total += bj->dealer_cards[1].value;
    draw_card(bj->dealer_cards[1], true, dealer_x + CARD_WIDTH + 4, dealer_y);

    bj->player_cards[0][0] = new_card();
    bj->player_total += bj->player_cards[0][0].value;
    draw_card(bj->player_cards[0][0], true, player_x, player_y);
    player_x += CARD_WIDTH + 4;

    bj->player_cards[0][1] = new_card();
    bj->player_total += bj->player_cards[0][1].value;
    draw_card(bj->player_cards[0][1], true, player_x, player_y);
    player_x += CARD_WIDTH + 4;
}

/*****************************************************************************
* redraw_board() redraws all the cards and the board
******************************************************************************/
static void redraw_board(struct game_context* bj) {
    unsigned int i, n, upper_bound;
    rb->lcd_clear_display();

    blackjack_drawtable(bj);
    player_x = 4;
    dealer_x = 4;
    upper_bound = bj->split_status > 1 ? 2 : 1;

    for (i = 0; i < bj->num_dealer_cards; i++) {
        if (!bj->end_hand) {
            draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);

            /* increment i so the dealer's first card isn't displayed */
            i++;
            dealer_x += CARD_WIDTH + 4;
        }
        draw_card(bj->dealer_cards[i], true, dealer_x, dealer_y);

        if (bj->num_dealer_cards > MAX_CARDS-1)
            dealer_x += 10;
        else
            dealer_x += CARD_WIDTH + 4;
    }

    for (n = 0; n < upper_bound; n++) {
        for (i = 0; i < bj->num_player_cards[n]; i++) {
            draw_card(bj->player_cards[n][i], true, player_x, player_y);
            if (bj->split_status>1 || bj->num_player_cards[n]>MAX_CARDS)
                player_x += 10;
            else
                player_x += CARD_WIDTH + 4;
        }
        if (bj->split_status > 1)
            player_x = LCD_WIDTH/2 + 4;
    }
}

/*****************************************************************************
* update_total updates the player's total
******************************************************************************/
static void update_total(struct game_context* bj) {
    char total[3];
    unsigned int w, h;
    rb->snprintf(total, 3, "%d", bj->player_total);
    rb->lcd_getstringsize(total, &w, &h);
#if LCD_HEIGHT > 64
    h *= 2;
#endif
    rb->lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 + h, total);
    rb->lcd_update_rect(LCD_WIDTH - w, LCD_HEIGHT/2 + h, w, h);
}


/*****************************************************************************
* check_for_aces() is passed an array of cards and returns where an ace is
* located. Otherwise, returns -1.
******************************************************************************/
static signed int check_for_aces(struct card temp_cards[], unsigned int size) {
    unsigned int i;
    for(i = 0; i < size; i++) {
        if (temp_cards[i].is_soft_ace)
            return i;
    }
    return -1;
}

/*****************************************************************************
* check_totals() compares player and dealer totals.
* 0: bust 1: loss, 2: push, 3: win, 4: blackjack, 5: something's not right...
******************************************************************************/
static unsigned int check_totals(struct game_context* bj) {
    unsigned int temp;
    if (bj->player_total > 21)
        temp = 0;
    else if (bj->player_total == 21 && bj->is_blackjack) {
        if (bj->dealer_total == 21 && bj->num_dealer_cards == 2)
            temp = 2;
        else
            temp = 4;
    }
    else if (bj->player_total == bj->dealer_total)
        temp = 2;
    else if (bj->dealer_total > 21 && bj->player_total < 22)
        temp = 3;
    else if (bj->dealer_total > bj->player_total)
        temp = 1;
    else if (bj->player_total > bj->dealer_total)
        temp = 3;
    else
        temp = 5;

    return temp;
}

/*****************************************************************************
* finish_dealer() draws cards for the dealer until he has 17 or more.
******************************************************************************/
static void finish_dealer(struct game_context* bj) {
    signed int temp = 0;

    if (bj->dealer_total > 16 && bj->dealer_total < 22)
        return;

    while (bj->dealer_total < 17) {
        bj->dealer_cards[bj->num_dealer_cards] = new_card();
        bj->dealer_total += bj->dealer_cards[bj->num_dealer_cards].value;
        bj->num_dealer_cards++;
    }

    while (bj->dealer_total > 21) {
        temp = check_for_aces(bj->dealer_cards, bj->num_dealer_cards);
        if(temp != -1) {
            bj->dealer_cards[temp].is_soft_ace = false;
            bj->dealer_total -= 10;
        }
        else
            return;
    }
}

/*****************************************************************************
* finish_game() completes the game once player's turn is over.
******************************************************************************/
static void finish_game(struct game_context* bj) {
    unsigned int rValue, w, h;
    char str[19];

    do {
        finish_dealer(bj);
    } while (bj->dealer_total < 17);

    redraw_board(bj);
    rValue = check_totals(bj);

    if (rValue == 0) {
        rb->snprintf(str, sizeof(str), " Bust! ");
        bj->player_money -= bj->current_bet;
    }
    else if (rValue == 1) {
        rb->snprintf(str, sizeof(str), " Sorry, you lost. ");
        bj->player_money -= bj->current_bet;
    }
    else if (rValue == 2) {
        rb->snprintf(str, sizeof(str), " Push ");
    }
    else if (rValue == 3) {
        rb->snprintf(str, sizeof(str), " You won! ");
        bj->player_money+= bj->current_bet;
    }
    else {
        rb->snprintf(str, sizeof(str), " Blackjack! ");
        bj->player_money += bj->current_bet * 3 / 2;
    }
    rb->lcd_getstringsize(str, &w, &h);

#if LCD_HEIGHT <= 64
    rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
    rb->lcd_fillrect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
    rb->lcd_set_drawmode(DRMODE_SOLID);
    rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h, str);
    rb->snprintf(str, 12, "You have %d", bj->player_total);
    rb->lcd_getstringsize(str, &w, &h);
    rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, str);
#else
    rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, str);
#endif
    rb->lcd_update();
}

/*****************************************************************************
* blackjack_loadgame() loads the saved game and returns load success.
******************************************************************************/
static bool blackjack_loadgame(struct game_context* bj) {
    signed int fd;
    bool loaded = false;

    /* open game file */
    fd = rb->open(SAVE_FILE, O_RDONLY);
    if(fd < 0) return false;

    /* read in saved game */
    if(rb->read(fd, bj, sizeof(struct game_context))
            == (long)sizeof(struct game_context))
    {
        loaded = true;
    }

    rb->close(fd);

    return loaded;
}

/*****************************************************************************
* blackjack_savegame() saves the current game state.
******************************************************************************/
static void blackjack_savegame(struct game_context* bj) {
    int fd;

    if(!resume)
        return;
    /* write out the game state to the save file */
    fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
    if(fd < 0)
        return;
    rb->write(fd, bj, sizeof(struct game_context));
    rb->close(fd);
}

/*****************************************************************************
* blackjack_get_yes_no() gets a yes/no answer from the user
******************************************************************************/
static unsigned int blackjack_get_yes_no(char message[20]) {
    int button;
    unsigned int w, h, b, choice = 0;
    bool breakout = false;
    char message_yes[24], message_no[24];

    rb->strcpy(message_yes, message);
    rb->strcpy(message_no, message);
    rb->strcat(message_yes, " Yes");
    rb->strcat(message_no, "  No");
    rb->lcd_getstringsize(message_yes, &w, &h);
    const char *stg[] = {message_yes, message_no};

#if LCD_HEIGHT <= 64
    b = 2*h+1;
#else
    b = h-1;
#endif

#ifdef HAVE_LCD_COLOR
    rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
    rb->lcd_set_foreground(LCD_BLACK);
    rb->lcd_set_background(LCD_WHITE);
#else
    rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
    rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
    rb->lcd_set_drawmode(DRMODE_SOLID);
#endif
    rb->lcd_drawrect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b - 1, w+3, h+4);

    while(!breakout) {
        rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b +1, stg[choice]);
        rb->lcd_update_rect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b -1,
                            w+3, h+4);
        button = rb->button_get(true);

        switch(button) {
            case BJACK_LEFT:
            case (BJACK_LEFT|BUTTON_REPEAT):
            case BJACK_RIGHT:
            case (BJACK_RIGHT|BUTTON_REPEAT):
                choice ^= 1;
                break;
            case BJACK_SELECT: breakout = true;
                break;
            case BJACK_QUIT: breakout = true;
                choice = 1;
                break;
        }
    }

#if LCD_DEPTH > 1
    rb->lcd_set_foreground(FG_COLOR);
    rb->lcd_set_background(BG_COLOR);
#endif
    return choice;
}

/*****************************************************************************
* blackjack_get_amount() gets an amount from the player to be used
******************************************************************************/
static signed int blackjack_get_amount(char message[20], signed int lower_limit,
                                       signed int upper_limit,
                                       signed int start) {
    int button;
    char str[9];
    bool breakout = false, changed = false;
    unsigned int w, h;
    signed int amount;

    rb->lcd_getstringsize("A", &w, &h); /* find the size of one character */

    if (start > upper_limit)
        amount = upper_limit;
    else if (start < lower_limit)
        amount = lower_limit;
    else
        amount = start;

#if LCD_DEPTH > 1
    rb->lcd_set_background(LCD_WHITE);
    rb->lcd_set_foreground(LCD_BLACK);
#endif

#if LCD_HEIGHT <= 64
    rb->lcd_clear_display();
    rb->lcd_puts(0, 1, message);
    rb->snprintf(str, 9, "$%d", amount);
    rb->lcd_puts(0, 2, str);
    rb->lcd_puts(0, 3, "RIGHT: +1");
    rb->lcd_puts(0, 4, "LEFT:  -1");
    rb->lcd_puts(0, 5, "UP:   +10");
    rb->lcd_puts(0, 6, "DOWN: -10");
    rb->lcd_update();
#else
    rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
    rb->lcd_fillrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3,
                     37*w / 2, 8*h -3);
    rb->lcd_set_drawmode(DRMODE_SOLID);
    rb->lcd_drawrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3,
                     37*w / 2, 8*h -3);
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 4*h - 1, message);
    rb->snprintf(str, 9, "$%d", amount);
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
      (CONFIG_KEYPAD == IPOD_3G_PAD) || \
      (CONFIG_KEYPAD == IPOD_1G2G_PAD) || \
      (CONFIG_KEYPAD == SANSA_E200_PAD) || \
      (CONFIG_KEYPAD == SANSA_FUZE_PAD)
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, " >>|:     +1");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, " |<<:     -1");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT:    +1");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT:     -1");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
#else
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT:  -1");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "UP:   +10");
    rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "DOWN: -10");
#endif
    rb->lcd_update_rect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3,
                        37*w / 2, 8*h -3);
#endif

    while(!breakout) {
        button = rb->button_get(true);

        switch(button) {
            case BJACK_UP:
            case (BJACK_UP|BUTTON_REPEAT):
                if (amount + 10 < upper_limit + 1) {
                    amount += 10;
                    changed = true;
                }
                break;
            case BJACK_DOWN:
            case (BJACK_DOWN|BUTTON_REPEAT):
                if (amount - 10 > lower_limit - 1) {
                    amount -= 10;
                    changed = true;
                }
                break;
            case BJACK_RIGHT:
            case (BJACK_RIGHT|BUTTON_REPEAT):
                if (amount + 1 < upper_limit + 1) {
                    amount++;
                    changed = true;
                }
                break;
            case BJACK_LEFT:
            case (BJACK_LEFT|BUTTON_REPEAT):
                if (amount - 1 > lower_limit - 1) {
                    amount--;
                    changed = true;
                }
                break;
#ifdef BJACK_MAX
            case BJACK_MAX :
                amount = upper_limit;
                changed = true;
                break;
#endif
#ifdef BJACK_MIN
            case BJACK_MIN :
                amount = lower_limit;
                changed = true;
                break;
#endif
            case BJACK_QUIT:
                breakout = true;
                amount = 0;
                break;
            case BJACK_SELECT:
                breakout = true;
                break;
        }

        if(changed) {
            rb->snprintf(str, 9, "$%d", amount);
#if LCD_HEIGHT <= 64
            rb->lcd_puts(0, 2, str);
            rb->lcd_update();
#else
            rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
            rb->lcd_fillrect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
            rb->lcd_set_drawmode(DRMODE_SOLID);
            rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
            rb->lcd_update_rect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
#endif
            changed = false;
        }
    }

#if LCD_DEPTH > 1
    rb->lcd_set_foreground(FG_COLOR);
    rb->lcd_set_background(BG_COLOR);
#endif
    rb->lcd_clear_display();
    return amount;
}

/*****************************************************************************
* blackjack_get_bet() gets the player's bet.
******************************************************************************/
static void blackjack_get_bet(struct game_context* bj) {
    bj->current_bet = blackjack_get_amount("Please enter a bet", 10,
                                           bj->player_money, bj->current_bet);
}

/*****************************************************************************
* double_down() returns one final card then finishes the game
******************************************************************************/
static void double_down(struct game_context* bj) {
    bj->current_bet *= 2;
    bj->player_cards[0][bj->num_player_cards[0]] = new_card();
    bj->player_total += bj->player_cards[0][bj->num_player_cards[0]].value;
    bj->num_player_cards[0]++;
}

/*****************************************************************************
* split() checks if the player wants to split and acts accordingly.
* When bj->split_status is 1, no split occurred. 2 means the player split and 3
* means a split has already occurred and the first hand is done.
******************************************************************************/
static void split(struct game_context* bj) {
    if (blackjack_get_yes_no("Split?") != 0)
        bj->split_status = 1;
    else {
        bj->split_status = 2;
        bj->current_bet *= 2;
        bj->num_player_cards[0] = 1;
        bj->num_player_cards[1] = 1;
        bj->player_cards[1][0] = bj->player_cards[0][1];
        bj->player_total = bj->player_cards[0][0].value;
    }
}

/*****************************************************************************
* insurance() see if the player wants to buy insurance and how much.
******************************************************************************/
static unsigned int insurance(struct game_context* bj) {
    unsigned int insurance, max_amount;

    insurance = blackjack_get_yes_no("Buy Insurance?");
    bj->asked_insurance = true;
    max_amount = bj->current_bet < (unsigned int)bj->player_money ?
                            bj->current_bet/2 : (unsigned int)bj->player_money;
    if (insurance != 0) return 0;

    insurance = blackjack_get_amount("How much?", 0, max_amount, 0);
    redraw_board(bj);
    return insurance;
}

/*****************************************************************************
* play_again() checks to see if the player wants to keep playing.
******************************************************************************/
static unsigned int play_again(void) {
    return blackjack_get_yes_no("Play Again?");
}

/*****************************************************************************
* blackjack_help() displays help text.
******************************************************************************/
static bool blackjack_help(void) {
    static char *help_text[] = {
        "Blackjack", "", "Aim", "",
        "Try", "to", "get", "as", "close", "to", "21", "without", "going",
        "over", "or", "simply", "beat", "out", "the", "dealer", "for", "the",
        "best", "hand.", "", "",
        "Controls", "",
        BJACK_SELECT_NAME,  ":", "hit", "/", "select", "",
        BJACK_STAY_NAME,    ":", "stay", "",
        BJACK_DOUBLE_NAME,  ":", "double", "down", "",
        BJACK_QUIT_NAME,    ":", "go", "to", "menu", "",
    };
    static struct style_text formation[]={
        { 0, TEXT_CENTER|TEXT_UNDERLINE },
        { 2, C_RED },
        { 26, C_RED },
        LAST_STYLE_ITEM
    };

    rb->lcd_setfont(FONT_UI);
#ifdef HAVE_LCD_COLOR
    rb->lcd_set_background(LCD_BLACK);
    rb->lcd_set_foreground(LCD_WHITE);
#endif
    if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
        return true;
    rb->lcd_setfont(FONT_SYSFIXED);

    return false;
}

static int blackjack_menu_cb(int action, const struct menu_item_ex *this_item)
{
    int i = ((intptr_t)this_item);
    if(action == ACTION_REQUEST_MENUITEM
       && !resume && (i==0 || i==5))
        return ACTION_EXIT_MENUITEM;
    return action;
}

/*****************************************************************************
* blackjack_menu() is the initial menu at the start of the game.
******************************************************************************/
static unsigned int blackjack_menu(void) {
    int selection = 0;
    bool breakout = false;

    MENUITEM_STRINGLIST(menu, "BlackJack Menu", blackjack_menu_cb,
                        "Resume Game", "Start New Game",
                        "High Scores", "Help",
                        "Playback Control",
                        "Quit without Saving", "Quit");

    while(!breakout) {
        switch(rb->do_menu(&menu, &selection, NULL, false)) {
            case 0:
                breakout = true;
                if(resume_file)
                    rb->remove(SAVE_FILE);
                resume_file = false;
                break;
            case 1:
                breakout = true;
                resume = false;
                break;
            case 2:
                highscore_show(-1, highscores, NUM_SCORES, false);
                break;
            case 3:
                if(blackjack_help())
                    return BJ_USB;
                break;
            case 4:
                if (playback_control(NULL))
                    return BJ_USB;
                break;
            case 5:
                return BJ_QUIT_WITHOUT_SAVING;
            case 6:
                if (resume)
                    return BJ_QUIT;
                else
                    return BJ_QUIT_WITHOUT_SAVING;

            case MENU_ATTACHED_USB:
                return BJ_USB;

            default:
                break;
        }
    }

    return 0;
}

/*****************************************************************************
* blackjack() is the main game subroutine, it returns the final game status.
******************************************************************************/
static int blackjack(struct game_context* bj) {
    int button;
    unsigned int w, h, temp_var, done = 0, todo = 1;
    signed int temp;
    bool breakout = false;
    bool dbl_down = false;

    /********************
    *       menu        *
    ********************/
    temp_var = blackjack_menu();
    if (temp_var != 0)
        return temp_var;

#if LCD_DEPTH > 1
    rb->lcd_set_background(BG_COLOR);
    rb->lcd_set_foreground(FG_COLOR);
#endif

    /********************
    *       init        *
    ********************/
    blackjack_init(bj);

    /********************
    *       play        *
    ********************/

    resume_file = false;
    /* check for resumed game */
    if(resume) {
        resume = false;
        redraw_board(bj);
        if (bj->split_status == 2) {
            todo=2;
            player_x = bj->num_player_cards[0] * 10 + 4;
        }
        else if (bj->split_status == 3) {
            player_x = bj->num_player_cards[1] * 10 + LCD_WIDTH/2 + 4;
            todo=2;
            done=1;
        }
    }
    else {
        bj->player_money = 1000;
        bj->current_bet = 10;
        blackjack_get_bet(bj);
        if (bj->current_bet == 0)
            return -1;
        rb->lcd_clear_display();
        deal_init_cards(bj);
        blackjack_drawtable(bj);
    }

    rb->lcd_update();

    breakout = false;

    while(true){
        if(bj->player_total == 21 && bj->num_player_cards[0] == 2) {
            bj->is_blackjack = true;
            bj->end_hand = true;
            finish_game(bj);
        }
        else if(bj->dealer_cards[1].is_soft_ace && !breakout &&
                !bj->asked_insurance) {
            temp_var = insurance(bj);
            if (bj->dealer_total == 21) {
                rb->splash(HZ, "Dealer has blackjack");
                bj->player_money += temp_var;
                bj->end_hand = true;
                breakout = true;
                redraw_board(bj);
                finish_game(bj);
            }
            else {
                rb->splash(HZ, "Dealer does not have blackjack");
                bj->player_money -= temp_var;
                breakout = true;
                redraw_board(bj);
                rb->lcd_update();
            }
        }
        if(!bj->end_hand && bj->split_status == 0 &&
           bj->player_cards[0][0].num == bj->player_cards[0][1].num) {
            split(bj);
            redraw_board(bj);
            rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
            if (bj->split_status == 2) {
                todo++;
                player_x = bj->num_player_cards[0] * 10 + 4;
            }
        }

        while(!bj->end_hand && done < todo) {
            button = rb->button_get(true);

            switch(button) {
                case BJACK_SELECT:
                    NEXT_CARD = new_card();
                    bj->player_total += NEXT_CARD.value;
                    draw_card(NEXT_CARD, true, player_x, player_y);
                    bj->num_player_cards[done]++;
                    if (bj->num_player_cards[done] == MAX_CARDS + 1) {
                        redraw_board(bj);
                        rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH,
                                            LCD_HEIGHT/2);
                    }
                    else if (bj->num_player_cards[done]>MAX_CARDS || todo > 1) {
                        rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
                                            CARD_HEIGHT+2);
                        player_x += 10;
                    }
                    else {
                        rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
                                            CARD_HEIGHT+2);
                        player_x += CARD_WIDTH + 4;
                    }
                    update_total(bj);

                    break;
                case BJACK_STAY:
                    bj->end_hand = true;
                    break;
                case BJACK_DOUBLEDOWN:
                    if ((signed int)bj->current_bet * 2 <
                            bj->player_money + 1 &&
                         bj->num_player_cards[0]==2 && todo==1) {
                        double_down(bj);
                        dbl_down = true;
                        if (bj->player_total < 22) {
                            bj->end_hand = true;
                            finish_game(bj);
                        }
                    }
                    else if((signed int)bj->current_bet * 2 >
                            bj->player_money){
                        rb->splash(HZ, "Not enough money to double down.");
                        redraw_board(bj);
                        rb->lcd_update();
                    }
                    break;

                case BJACK_QUIT:
                    resume = true;
                    return BJ_END;
            }

            while (bj->player_total > 21 && !bj->end_hand) {
                temp = check_for_aces(bj->player_cards[done],
                                      bj->num_player_cards[done]);
                if(temp != -1) {
                    bj->player_cards[done][temp].is_soft_ace = false;
                    bj->player_total -= 10;
                    update_total(bj);
                    if (dbl_down) {
                        bj->end_hand = true;
                        finish_game(bj);
                    }
                }
                else
                    bj->end_hand = true;
            }

            if (bj->end_hand) {
                done++;
                if(todo > 1) {
                    if (done == 2) {
                        temp = bj->player_total;
                        bj->player_total = temp_var;
                        temp_var = temp;
                        finish_game(bj);
                        rb->lcd_getstringsize(" Split 1 ", &w, &h);
                        rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
                                       " Split 1 ");
                        rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
                                            w,h);
                        bj->current_bet /= 2;
                        rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
                                            w,h);
                        rb->sleep(HZ*2);
                        bj->player_total = temp_var;
                        finish_game(bj);
                        rb->lcd_getstringsize(" Split 2 ", &w, &h);
                        rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
                                       " Split 2 ");
                        rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
                                            w,h);
                        rb->sleep(HZ*2);
                    }
                    else {
                        bj->end_hand = false;
                        bj->split_status = 3;
                        temp_var = bj->player_total;
                        bj->player_total = bj->player_cards[1][0].value;
                        update_total(bj);
                        redraw_board(bj);
                        player_x += 10;
                        rb->lcd_update();
                    }
                }
                else
                    finish_game(bj);
            }
        }

        if (bj->player_money < 10) {
            rb->sleep(HZ);
            return BJ_LOSE;
        }

        if (bj->end_hand) {                     /* If hand is over */
            if (play_again() != 0)              /* User wants to quit */
                return BJ_END;
            else {                              /* User keeps playing */
                breakout = false;
                temp = bj->current_bet;
                bj->current_bet = 0;
                redraw_board(bj);
                rb->lcd_update();
                bj->current_bet = temp;
                if(dbl_down) {
                    bj->current_bet /= 2;
                    dbl_down = false;
                }
                done = 0;
                todo = 1;
                blackjack_init(bj);
                blackjack_get_bet(bj);
                if (bj->current_bet == 0)
                    return BJ_END;
                deal_init_cards(bj);
                blackjack_drawtable(bj);
                rb->lcd_update();
            }
        }
    }
    /* Never reached */
    return PLUGIN_OK;
}

/*****************************************************************************
* plugin entry point.
******************************************************************************/
enum plugin_status plugin_start(const void* parameter)
{
    struct game_context bj;
    bool exit = false;

    (void)parameter;

#if LCD_DEPTH > 1
    rb->lcd_set_backdrop(NULL);
#endif

    /* load high scores */
    highscore_load(SCORE_FILE, highscores, NUM_SCORES);
    resume = blackjack_loadgame(&bj);
    resume_file = resume;

    rb->lcd_setfont(FONT_SYSFIXED);

    while(!exit) {
        switch(blackjack(&bj)){
            case BJ_LOSE:
                rb->splash(HZ, "Not enough money to continue");
                /* fall through to BJ_END */

            case BJ_END:
                if(!resume && bj.player_money > 10) {
                    /* There is no level, so store -1 to blank column */
                    int position = highscore_update(bj.player_money, -1, "",
                                                    highscores, NUM_SCORES);
                    if (position != -1)
                    {
                        if (position==0)
                            rb->splash(HZ*2, "New High Score");
                        highscore_show(position, highscores, NUM_SCORES, false);
                    }
                }
                break;

            case BJ_USB:
                highscore_save(SCORE_FILE, highscores, NUM_SCORES);
                return PLUGIN_USB_CONNECTED;

            case BJ_QUIT:
                rb->splash(HZ*1, "Saving game...");
                blackjack_savegame(&bj);
                /* fall through */

            case BJ_QUIT_WITHOUT_SAVING:
                exit = true;
                break;

            default:
                break;
        }
    }
    highscore_save(SCORE_FILE, highscores, NUM_SCORES);
    return PLUGIN_OK;
}
