/******************************************************************************
 *
 * Copyright:
 *    (C) 2006 Embedded Artists AB
 *
 * File:
 *    cards.c
 *
 * Description:
 *    Implementation of cards game.
 *    Short game description:
 *    - 2 Players play against each other: player1 (P1, red color) and player2 (P2, blue color)
 *    - each player has 11 cards with values from 1 to 11
 *    - each player places one of his cards hiddenly
 *    - the placed cards are opened and the player with the higher card value gets a point
 *    - if the 2 cards have the same value, nobody gets a point
 *
 *    At the start of the game you can select between two modes:
 *    - one player mode: the role of player2 is taken by the ARM uC by placing a random card 
 *    - two players mode: two real players can play against each other
 *
 *****************************************************************************/

/******************************************************************************
 * Includes
 *****************************************************************************/
#include "../pre_emptive_os/api/osapi.h"
#include "../pre_emptive_os/api/general.h"
#include <printf_P.h>
#include <ea_init.h>
#include <stdlib.h>
#include "lcd.h"
#include "key.h"
#include "select.h"

#include "configAppl.h"

#include "cards.h"
#include "cards_bmp.h"   // bitmaps with cards


/******************************************************************************
 * Typedefs and defines
 *****************************************************************************/

#define GAME_NOT_STARTED 0
#define GAME_RUNNING     1
#define GAME_OVER        2
#define GAME_END         3

#define SCREEN_WIDTH  ((tU8)130)
#define SCREEN_HEIGHT ((tU8)130)


#define ROW_OFFSET 2
#define COL_OFFSET 5
#define ROW_SPACE  3
#define COL_SPACE  4
#define CARD_WIDTH  26
#define CARD_HEIGHT 35
#define MAX_COLUMN  3
#define MAX_ROW     2
#define NO_OF_CARDS 11
#define STATUSLINE_Y  115
#define COLOR_PLAYER1  0xE0
#define COLOR_PLAYER2  0x03
#define COLOR_SELECT   0xE3
#define COLOR_NEUTRAL  0x00
#define GAME_BKG_COLOR 0xD8

#define CARD_AVAILABLE 0x01
enum { MODE_FIRST_SCREEN, MODE_WAIT_PLAYER1, MODE_WAIT_PLAYER2, MODE_SELECT_CARDS, MODE_WAIT_SHOWCARDS, MODE_LAST_SCREEN };

tU8 cXpos = 0;
tU8 cYpos = 0;
tU8 arCardsPlayer1[NO_OF_CARDS];
tU8 arCardsPlayer2[NO_OF_CARDS];

tU8 cScorePlayer1 = 0;
tU8 cScorePlayer2 = 0;
tU8 cCardPlayer1;
tU8 cCardPlayer2;
tU8 cCurrentPlayer = 1;
tU8 arText[20];
tU8 cRemainingCards = NO_OF_CARDS;
tU8 gameMode;
tU8 gameStatus;
tU8 bOnePlayerMode = 0;

/*****************************************************************************
 * External variables
 ****************************************************************************/

extern volatile tU32 ms;


/*****************************************************************************
 *
 * Description:
 *    Draw a frame around card (col, row) to mark as selected
 *
 ****************************************************************************/
void selectCard(tU8 col, tU8 row)
{
   lcdRect(COL_OFFSET + col*(CARD_WIDTH+COL_SPACE)-2, ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE)-2, CARD_WIDTH+4, 2, COLOR_SELECT); 
   lcdRect(COL_OFFSET + col*(CARD_WIDTH+COL_SPACE)-2, ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE)+CARD_HEIGHT, CARD_WIDTH+4, 2, COLOR_SELECT); 
   lcdRect(COL_OFFSET + col*(CARD_WIDTH+COL_SPACE)-2, ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE)-2, 2, CARD_HEIGHT+4, COLOR_SELECT); 
   lcdRect(COL_OFFSET + col*(CARD_WIDTH+COL_SPACE)+CARD_WIDTH, ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE)-2, 2, CARD_HEIGHT+4, COLOR_SELECT); 
}


/*****************************************************************************
 *
 * Description:
 *    Clear the frame around card (col, row) to mark as unselected
 *
 ****************************************************************************/
void unselectCard(tU8 col, tU8 row)
{
   lcdRect(COL_OFFSET + col*(CARD_WIDTH+COL_SPACE)-2, ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE)-2, CARD_WIDTH+4, 2, GAME_BKG_COLOR); 
   lcdRect(COL_OFFSET + col*(CARD_WIDTH+COL_SPACE)-2, ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE)+CARD_HEIGHT, CARD_WIDTH+4, 2, GAME_BKG_COLOR); 
   lcdRect(COL_OFFSET + col*(CARD_WIDTH+COL_SPACE)-2, ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE)-2, 2, CARD_HEIGHT+4, GAME_BKG_COLOR); 
   lcdRect(COL_OFFSET + col*(CARD_WIDTH+COL_SPACE)+CARD_WIDTH, ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE)-2, 2, CARD_HEIGHT+4, GAME_BKG_COLOR); 
}



/*****************************************************************************
 *
 * Description:
 *    Shows card at (col, row) with (color)
 *
 ****************************************************************************/
void showCard(tU8 card, tU8 col, tU8 row, tU8 color)
{
   lcdColorizedIcon(col, row, CARD_WIDTH, CARD_HEIGHT, color, 0xFF, GAME_BKG_COLOR, &bmpCards[(tU32)card*CARD_WIDTH*CARD_HEIGHT]);
}



/*****************************************************************************
 *
 * Description:
 *    Show available cards of current player
 *
 ****************************************************************************/
void showAvailableCards(void)
{
  tU8 i;
  tU8 color;
  tU8 row, col;
  
  lcdClrscr();

  for(i = 0; i < NO_OF_CARDS; i++)
  {
     col = i % 4;
     row = i / 4;
     if(cCurrentPlayer == 1)
     {
        color = COLOR_PLAYER1;
        if(arCardsPlayer1[i] & CARD_AVAILABLE)
           showCard(i, COL_OFFSET + col*(CARD_WIDTH+COL_SPACE), ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE), color);
     }
     else
     {
        color = COLOR_PLAYER2;
        if(arCardsPlayer2[i] & CARD_AVAILABLE)
           showCard(i, COL_OFFSET + col*(CARD_WIDTH+COL_SPACE), ROW_OFFSET + row*(CARD_HEIGHT+ROW_SPACE), color);
     }
  }
  lcdGotoxy(1, STATUSLINE_Y);
  lcdColor(GAME_BKG_COLOR, color);
  if(cCurrentPlayer == 1)
     lcdPuts("P1:");
  else
     lcdPuts("P2:");
  lcdPuts(" select card");
  cXpos = 0;
  cYpos = 0;
  selectCard(cXpos, cYpos);
}


/*****************************************************************************
 *
 * Description:
 *    Shows hidden card at (col, row) with (color)
 *
 ****************************************************************************/
void showHiddenCard(tU8 col, tU8 row, tU8 color)
{
   lcdColorizedIcon(col, row, CARD_WIDTH, CARD_HEIGHT, color, color, GAME_BKG_COLOR, bmpCards);
}


/*****************************************************************************
 *
 * Description:
 *    Takes a random card out of the available cards
 *
 ****************************************************************************/
tU8 getRandomCard(void)
{
   tU8 value;
   tU8 card;
   tU8 i;

   value = rand() % 100;
   value++;
   card = 0;
   for(i = 0; i < value; i++)
   {
      do
      {
         card++;
         if(card >= NO_OF_CARDS)
            card = 0;
      }
      while( (arCardsPlayer2[card] & CARD_AVAILABLE) == 0 );
   }
   return(card);
}


/*****************************************************************************
 *
 * Description:
 *    Show placed cards and each players score
 *
 ****************************************************************************/
void showOverview(void)
{
   #define ROW_P1 10
   #define COL_P1 20
   #define ROW_P2 10
   #define COL_P2 84

   tU8 i;

   lcdColor(GAME_BKG_COLOR, COLOR_NEUTRAL);
   lcdClrscr();

   lcdRect(COL_P1 - 2, ROW_P1 - 2, CARD_WIDTH+4, 1, COLOR_NEUTRAL); 
   lcdRect(COL_P1 - 2, ROW_P1 + CARD_HEIGHT + 1, CARD_WIDTH+4, 1, COLOR_NEUTRAL); 
   lcdRect(COL_P1 - 2, ROW_P1 - 2, 1, CARD_HEIGHT+4, COLOR_NEUTRAL); 
   lcdRect(COL_P1 + CARD_WIDTH + 1, ROW_P1 - 2, 1, CARD_HEIGHT+4, COLOR_NEUTRAL); 

   lcdRect(COL_P2 - 2, ROW_P2 - 2, CARD_WIDTH+4, 1, COLOR_NEUTRAL); 
   lcdRect(COL_P2 - 2, ROW_P2 + CARD_HEIGHT + 1, CARD_WIDTH+4, 1, COLOR_NEUTRAL); 
   lcdRect(COL_P2 - 2, ROW_P2 - 2, 1, CARD_HEIGHT+4, COLOR_NEUTRAL); 
   lcdRect(COL_P2 + CARD_WIDTH + 1, ROW_P2 - 2, 1, CARD_HEIGHT+4, COLOR_NEUTRAL); 

   if(gameMode == MODE_WAIT_PLAYER1)
   {
      showCard(cCardPlayer1, COL_P1, ROW_P1, COLOR_PLAYER1);
      showCard(cCardPlayer2, COL_P2, ROW_P2, COLOR_PLAYER2);
      lcdColor(GAME_BKG_COLOR, COLOR_NEUTRAL);
      lcdGotoxy(1,50);
      if(cCardPlayer1 > cCardPlayer2)
         lcdPuts("P1 made a point.");
      else if(cCardPlayer2 > cCardPlayer1)
         lcdPuts("P2 made a point.");
      else
         lcdPuts("   No points.");

      lcdColor(GAME_BKG_COLOR, COLOR_PLAYER1);
      lcdGotoxy(1,65);
      lcdPuts("Player 1, press");
      lcdGotoxy(1,80);
      lcdPuts("key LEFT to");
      lcdGotoxy(1,95);
      lcdPuts("show your cards");
   }
   else if(gameMode == MODE_WAIT_PLAYER2)
   {
      showHiddenCard(COL_P1, ROW_P1, COLOR_PLAYER1);
      lcdColor(GAME_BKG_COLOR, COLOR_PLAYER2);
      lcdGotoxy(1,65);
      lcdPuts("Player 2, press");
      lcdGotoxy(1,80);
      lcdPuts("key RIGHT to");
      lcdGotoxy(1,95);
      lcdPuts("show your cards");
   }
   else if(gameMode == MODE_WAIT_SHOWCARDS)
   {
      showHiddenCard(COL_P1, ROW_P1, COLOR_PLAYER1);
      showHiddenCard(COL_P2, ROW_P2, COLOR_PLAYER2);
      lcdColor(GAME_BKG_COLOR, COLOR_NEUTRAL);
      lcdGotoxy(1,65);
      lcdPuts("Press key DOWN");
      lcdGotoxy(1,80);
      lcdPuts("to show and");
      lcdGotoxy(1,95);
      lcdPuts("compare cards...");
   }
   else if(gameMode == MODE_FIRST_SCREEN)
   {
      lcdColor(GAME_BKG_COLOR, COLOR_NEUTRAL);
      lcdGotoxy(5,50);
      lcdPuts("* Cards game *");
      lcdColor(GAME_BKG_COLOR, COLOR_PLAYER1);
      lcdGotoxy(1,65);
      lcdPuts("Player 1, press");
      lcdGotoxy(1,80);
      lcdPuts("key LEFT to");
      lcdGotoxy(1,95);
      lcdPuts("show your cards");
   }
   else if(gameMode == MODE_LAST_SCREEN)
   {
      showCard(cCardPlayer1, COL_P1, ROW_P1, COLOR_PLAYER1);
      showCard(cCardPlayer2, COL_P2, ROW_P2, COLOR_PLAYER2);
      lcdColor(GAME_BKG_COLOR, COLOR_NEUTRAL);
      lcdGotoxy(1,50);
      if(cCardPlayer1 > cCardPlayer2)
         lcdPuts("P1 made a point.");
      else if(cCardPlayer2 > cCardPlayer1)
         lcdPuts("P2 made a point.");
      else
         lcdPuts("   No points.");

      lcdGotoxy(1,80);
      lcdPuts("   Game over.");
      lcdGotoxy(1,95);
      lcdPuts("Winner:");
      if(cScorePlayer1 > cScorePlayer2)
      {
         lcdColor(GAME_BKG_COLOR, COLOR_PLAYER1);
         lcdPuts("Player 1");
      }
      else if(cScorePlayer2 > cScorePlayer1)
      {
         lcdColor(GAME_BKG_COLOR, COLOR_PLAYER2);
         lcdPuts("Player 2");
      }
      else
      {
         lcdPuts(" both...");
      }
   }

   lcdGotoxy(10, STATUSLINE_Y);
   lcdColor(GAME_BKG_COLOR, COLOR_PLAYER1);
   lcdPuts("P1:");
   i = 0;
   if(cScorePlayer1 > 9)
      arText[i++] = (cScorePlayer1 / 10) + '0';
   arText[i++] = (cScorePlayer1 % 10) + '0';
   arText[i++] = 0;
   lcdPuts(arText);

   lcdGotoxy(85, STATUSLINE_Y);
   lcdColor(GAME_BKG_COLOR, COLOR_PLAYER2);
   lcdPuts("P2:");
   i = 0;
   if(cScorePlayer2 > 9)
      arText[i++] = (cScorePlayer2 / 10) + '0';
   arText[i++] = (cScorePlayer2 % 10) + '0';
   arText[i++] = 0;
   lcdPuts(arText);
}




/*****************************************************************************
 *
 * Description:
 *    Handle keypress UP
 *    - MODE_SELECT_CARDS: unselect current field, select next field
 *
 ****************************************************************************/
void keyUp(void)
{
   if(gameMode == MODE_SELECT_CARDS)
   {
      unselectCard(cXpos, cYpos);
      if(cYpos > 0)
         cYpos--;
      else
         cYpos = MAX_ROW;
      selectCard(cXpos, cYpos);
   }
}


/*****************************************************************************
 *
 * Description:
 *    Handle keypress DOWN
 *    - MODE_SELECT_CARDS: unselect current field, select next field
 *    - MODE_WAIT_SHOWCARDS: show and compare placed cards
 *
 ****************************************************************************/
void keyDown(void)
{
   if(gameMode == MODE_SELECT_CARDS)
   {
      unselectCard(cXpos, cYpos);
      if(cYpos < MAX_ROW)
         cYpos++;
      else
         cYpos = 0;
      selectCard(cXpos, cYpos);
   }
   else if(gameMode == MODE_WAIT_SHOWCARDS)
   {
      if(cCardPlayer1 > cCardPlayer2)
         cScorePlayer1++;
      else if(cCardPlayer2 > cCardPlayer1)
         cScorePlayer2++;
      cRemainingCards--;
      if(cRemainingCards == 0)
      {
         gameMode = MODE_LAST_SCREEN;
      }
      else
      {
         gameMode = MODE_WAIT_PLAYER1;
      }
      showOverview();
   }

}


/*****************************************************************************
 *
 * Description:
 *    Handle keypress RIGHT
 *    - MODE_SELECT_CARDS: unselect current field, select next field
 *    - MODE_WAIT_PLAYER2: show available cards for player 2
 *
 ****************************************************************************/
void keyRight(void)
{
   if(gameMode == MODE_SELECT_CARDS)
   {
      unselectCard(cXpos, cYpos);
      if(cXpos < MAX_COLUMN)
         cXpos++;
      else
         cXpos = 0;
      selectCard(cXpos, cYpos);
   }
   else if(gameMode == MODE_WAIT_PLAYER2)
   {
      showAvailableCards();
      gameMode = MODE_SELECT_CARDS;
   }

}


/*****************************************************************************
 *
 * Description:
 *    Handle keypress LEFT
 *    - MODE_SELECT_CARDS: unselect current field, select next field
 *    - MODE_WAIT_PLAYER1: show available cards for player 1
 *
 ****************************************************************************/
void keyLeft(void)
{
   if(gameMode == MODE_SELECT_CARDS)
   {
      unselectCard(cXpos, cYpos);
      if(cXpos > 0)
         cXpos--;
      else
         cXpos = MAX_COLUMN;
      selectCard(cXpos, cYpos);
   }
   else if(gameMode == MODE_WAIT_PLAYER1)
   {
      showAvailableCards();
      gameMode = MODE_SELECT_CARDS;
   }

}


/*****************************************************************************
 *
 * Description:
 *    Handle keypress CENTER
 *    - MODE_SELECT_CARDS: place selected card
 *    - MODE_LAST_SCREEN: end game
 *
 ****************************************************************************/
void keyCenter(void)
{
   tU8 card;

   if(gameMode == MODE_SELECT_CARDS)
   {
      card = cXpos + cYpos*4;
      if(card >= NO_OF_CARDS)
         return;
      if(cCurrentPlayer == 1)
      {
         if( (arCardsPlayer1[card] & CARD_AVAILABLE) == 0 )
            return;
         else
         {
            arCardsPlayer1[card] &= (~CARD_AVAILABLE);
            cCardPlayer1 = card;
            if(bOnePlayerMode)
            {   // take a random card for player2
               cCardPlayer2 = getRandomCard();
               arCardsPlayer2[cCardPlayer2] &= (~CARD_AVAILABLE);
               gameMode = MODE_WAIT_SHOWCARDS;
            }
            else
            {
               gameMode = MODE_WAIT_PLAYER2;
            }
         }
      }
      else
      {
         if( (arCardsPlayer2[card] & CARD_AVAILABLE) == 0 )
            return;
         else
         {
            arCardsPlayer2[card] &= (~CARD_AVAILABLE);
            gameMode = MODE_WAIT_SHOWCARDS;
            cCardPlayer2 = card;
         }
      }
      showOverview();
      if(!bOnePlayerMode)
      {
         cCurrentPlayer++;
         if(cCurrentPlayer > 2)
            cCurrentPlayer = 1;
      }
   }
   else if(gameMode == MODE_LAST_SCREEN)
   {
      gameStatus = GAME_OVER;
   }

}




/*****************************************************************************
 *
 * Description:
 *    Draw game background and game board, initialize all variables
 *
 ****************************************************************************/
static void
setupGame (void)
{
  tU8 i;

  srand(ms);

  lcdColor(GAME_BKG_COLOR,COLOR_NEUTRAL);
  lcdClrscr();

  cScorePlayer1 = 0;
  cScorePlayer2 = 0;
  cCardPlayer1 = 0;
  cCardPlayer2 = 0;
  cCurrentPlayer = 1;
  cRemainingCards = NO_OF_CARDS;

  for(i = 0; i < NO_OF_CARDS; i++)
  {
     arCardsPlayer1[i] = CARD_AVAILABLE;
     arCardsPlayer2[i] = CARD_AVAILABLE;
  }
  gameMode = MODE_FIRST_SCREEN;
  showOverview();
  gameMode = MODE_WAIT_PLAYER1;
}


/*****************************************************************************
 *
 * Description:
 *    Implements menu to select one or two player mode
 *    
 ****************************************************************************/
tU8 menuCards (void)
{
   tU8 cChoice;
   tMenu menu;

   lcdColor(GAME_BKG_COLOR,COLOR_NEUTRAL);
   lcdClrscr();

   lcdGotoxy(23, 5);
   lcdPuts("Cards game");

   showCard(0, 20, 90, COLOR_PLAYER1);
   showCard(8, 84, 90, COLOR_PLAYER2);

   menu.xPos = 18;
   menu.yPos = 28;
   menu.xLen = 6+(11*8);
   menu.yLen = 4*14;
   menu.noOfChoices = 2;
   menu.initialChoice = 0;
   menu.pHeaderText = "Select mode";
   menu.headerColor = COLOR_NEUTRAL;
   menu.headerTextXpos = 20;
   menu.pChoice[0] = "One player ";
   menu.pChoice[1] = "Two players";
   menu.bgColor       = 0;
   menu.borderColor   = 0x6d;
   menu.choicesColor  = 0xfd;
   menu.selectedColor = 0xe0;
        
   cChoice = drawMenu(menu);

   return(cChoice);
}


/*****************************************************************************
 *
 * Description:
 *    Implements cards game
 *    
 ****************************************************************************/
void playCards(void)
{
  tU8 anyKey;
  tU8 cChoice;

  gameStatus = GAME_NOT_STARTED;

  while(gameStatus != GAME_END)
  {
    if(gameStatus == GAME_NOT_STARTED)
    {
        cChoice = menuCards();
        if(cChoice == 0)
           bOnePlayerMode = 1;
        else
           bOnePlayerMode = 0;

        setupGame();
        gameStatus = GAME_RUNNING;
    }
    
    osSleep(1);

    anyKey = checkKey();
    switch(gameStatus)
    {
      case GAME_RUNNING:
        if (anyKey != KEY_NOTHING)
        {
          if (anyKey == KEY_UP)
          {
            keyUp();
          }
          else if (anyKey == KEY_DOWN)
          {
            keyDown();
          }
          else if (anyKey == KEY_RIGHT)
          {
            keyRight();
          }
          else if (anyKey == KEY_LEFT)
          {
            keyLeft();
          }
          else if (anyKey == KEY_CENTER)
          {
            keyCenter();
          }

        }
        else
        {
          osSleep(1);
        }
        break;
      
      case GAME_OVER:
          osSleep(1);
          anyKey = checkKey();

          gameStatus = GAME_END;
             break;                       //End game
        
      default:
        gameStatus = GAME_END;
        break;
    }
  }
}

