///////////////////////////////////////////////////////////////////////////////
// $Id: Engine.cxx,v 1.1 1995/01/12 02:07:15 bmott Exp $
///////////////////////////////////////////////////////////////////////////////
//
// Engine.cxx - Oonsoo engine class
//
//
// Bradford W. Mott
// Copyright (C) 1994
// December 13,1994
//
///////////////////////////////////////////////////////////////////////////////
// $Log: Engine.cxx,v $
// Revision 1.1  1995/01/12  02:07:15  bmott
// Initial revision
//
///////////////////////////////////////////////////////////////////////////////

#include <stdio.h>

#include "UIApplication.hxx"
#include "TopLevel.hxx"
#include "PushButton.hxx"

#include "MainDeck.hxx"
#include "PlainDeck.hxx"
#include "PlayingDeck.hxx"

#include "NewGameCommand.hxx"
#include "AboutCommand.hxx"
#include "HelpCommand.hxx"
#include "QuitOrCloseCommand.hxx"
#include "DealCardsCommand.hxx"
#include "DeckMoveCommand.hxx"


#include "LinkedList.hxx"

#include "Engine.hxx"


///////////////////////////////////////////////////////////////////////////////
// Constructor 
///////////////////////////////////////////////////////////////////////////////
Engine::Engine(SpriteCollection* sprites)
    : mySpriteCollection(sprites)
{
  myTitle = NULL;
  myCongratulation = NULL;

  // NULL out deck references
  myMainDeck = NULL;
  for(int t=0; t<12; ++t)
    myPlayingDeck[t] = NULL;

  // Build the oonsoo main application window
  TopLevel* toplevel = new TopLevel("Oonsoo", 100, 100, 800, 600,
      new QuitOrCloseCommand());
  toplevel->background("Gray65");

    // Create a frame with a border
    Frame* cardTableFrame = new Frame(toplevel, "cardTableFrame", 10,10,
        780, 540, Sunken);
    cardTableFrame->background("Gray70");

    // Create a frame to act as the card table
    myCardTable = new Frame(cardTableFrame, "cardTable", 5, 5,
        770, 530, Flat);
    myCardTable->background("Gray70");

      // Create the oonsoo title 
      Sprite* title = mySpriteCollection->getByName("Title");
      myTitle = new DrawingArea(myCardTable, "title",
          (770 - title->width()) / 2, (530 - title->height()) / 3,
          title->width(), title->height());
      myTitle->background(title);
          
    // Create push buttons
    PushButton* pushButton = new PushButton(toplevel, "newGame",
         10, 600-40, 90, 30, "New Game", new NewGameCommand(this));
    pushButton->background("Gray70");

    pushButton = new PushButton(toplevel, "about",
        110, 600-40, 90, 30, "About...", new AboutCommand(mySpriteCollection));
    pushButton->background("Gray70");

    pushButton = new PushButton(toplevel, "rules",
        210, 600-40, 90, 30, "Help...", new HelpCommand(mySpriteCollection));
    pushButton->background("Gray70");

    pushButton = new PushButton(toplevel, "quit",
        720, 600-40, 70, 30, "Quit", new QuitOrCloseCommand());
    pushButton->background("Gray70");

  // Manage the main application window
  toplevel->manage();
}

///////////////////////////////////////////////////////////////////////////////
// Destructor 
///////////////////////////////////////////////////////////////////////////////
Engine::~Engine()
{
}

///////////////////////////////////////////////////////////////////////////////
// Setup a new game
///////////////////////////////////////////////////////////////////////////////
void Engine::newGame()
{
  int t;

  // Make sure myTitle has been removed
  delete myTitle;
  myTitle = NULL;

  // Make sure myCongratulation has been removed
  delete myCongratulation;
  myCongratulation = NULL;

  // Remove old decks
  delete myMainDeck;
  for(t=0; t<12; ++t)
    delete myPlayingDeck[t];

  // Main deck used to deal from
  myMainDeck = new MainDeck(myCardTable, "mainDeck", 20, 10, 64, 520,
      mySpriteCollection, new DealCardsCommand(this));
  myMainDeck->shuffle();
  myMainDeck->manage();
  
  // Create all twelve playing decks
  LinkedList<Card> cards;
  myPlayingDeck[0] = new PlayingDeck(myCardTable, "playingDeck",
      140    , 10, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[1] = new PlayingDeck(myCardTable, "playingDeck",
      140+110, 10, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[2] = new PlayingDeck(myCardTable, "playingDeck",
      140+220, 10, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[3] = new PlayingDeck(myCardTable, "playingDeck",
      140+330, 10, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[4] = new PlayingDeck(myCardTable, "playingDeck",
      140+440, 10, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[5] = new PlayingDeck(myCardTable, "playingDeck",
      140+550, 10, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[6] = new PlayingDeck(myCardTable, "playingDeck",
      140    , 275, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[7] = new PlayingDeck(myCardTable, "playingDeck",
      140+110, 275, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[8] = new PlayingDeck(myCardTable, "playingDeck",
      140+220, 275, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[9] = new PlayingDeck(myCardTable, "playingDeck",
      140+330, 275, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[10] = new PlayingDeck(myCardTable, "playingDeck",
      140+440, 275, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));
  myPlayingDeck[11] = new PlayingDeck(myCardTable, "playingDeck",
      140+550, 275, 64, 255, mySpriteCollection, &cards, 
      new DeckMoveCommand(this));

  // Manage each of the playing decks
  for(t=0; t<12; ++t)
    myPlayingDeck[t]->manage();

  // Deal the first twelve cards face down
  dealCards(FaceDown);
  
  // Deal the second twelve cards face up
  dealCards(FaceUp);
}


///////////////////////////////////////////////////////////////////////////////
// Deal twelve cards from the main deck to the playing deck
///////////////////////////////////////////////////////////////////////////////
void Engine::dealCards(CardOrientation orientation)
{
  // Add a card to each playing deck
  for(int t=0; t<12; ++t)
  {
    // Get the top card
    Card* card = myMainDeck->remove();
 
    // return if there are no more cards
    if(card == NULL)
      return;

    card->orientation(orientation);
    myPlayingDeck[t]->add(card);
  }
}

///////////////////////////////////////////////////////////////////////////////
// Handle moving cards from one deck to somewhere else
///////////////////////////////////////////////////////////////////////////////
void Engine::moveCards(Deck* from, Deck* moved)
{
  int t;
  Sound* soundSystem = application->soundSystem();

  // See if the moved deck intersects with any of the playing decks
  for(t = 0; t < 12; ++t)
  {
    int x1 = myPlayingDeck[t]->x();
    int y1 = myPlayingDeck[t]->y();
    int x2 = x1 + myPlayingDeck[t]->width();
    int y2 = y1 + myPlayingDeck[t]->height();

    int x3 = moved->x();
    int y3 = moved->y();
    int x4 = x3 + moved->width();
    int y4 = y3 + moved->height();

    // See if the moved deck intersects this playing deck
    if((x3 >= x1) && (x3 <= x2) && (y3 >= y1) && (y3 <= y2))
      break; 
    if((x4 >= x1) && (x4 <= x2) && (y4 >= y1) && (y4 <= y2))
      break; 
    if((x4 >= x1) && (x4 <= x2) && (y3 >= y1) && (y3 <= y2))
      break; 
    if((x3 >= x1) && (x3 <= x2) && (y4 >= y1) && (y4 <= y2))
      break; 
  }

  // Unmanage the moved deck
  moved->unmanage();

  // Take the cards out of the moved deck and place them in a linked list
  LinkedList<Card> movedCards;
  for(Card* card = moved->remove(); card != NULL; card = moved->remove())
    movedCards.prepend(card);
 
  // Delete the moved deck
  delete moved;
     
  if(t != 12)
  {
    // If it's the deck they moved from then go ahead and add it back
    if(myPlayingDeck[t] == from)
    {
      from->add(&movedCards);
    }
    else if(myPlayingDeck[t]->checkValidAddition(&movedCards))
    {
      myPlayingDeck[t]->add(&movedCards);

      // Tell the "from" deck to turn its top card over
      from->turnTopCardFaceUp();

      // Check to see if the game is finished
      CheckAndHandleFinished();
    }
    else
    {
      // Bad user! Move them back to where they came from
      from->add(&movedCards);
      if(soundSystem->state() == Enabled)
        soundSystem->playSample("BeepCasio");
      else
        XBell(application->display(), 50);
    }
  }
  else
  {
    // Bad user! Move them back to where they came from
    from->add(&movedCards);
    if(soundSystem->state() == Enabled)
      soundSystem->playSample("BeepCasio");
    else
      XBell(application->display(), 50);
  }
}


///////////////////////////////////////////////////////////////////////////////
// Check to see if the game has been finished
///////////////////////////////////////////////////////////////////////////////
void Engine::CheckAndHandleFinished()
{
  Sound* soundSystem = application->soundSystem();

  // Make sure each playing deck is correct  
  for(int t = 0; t < 12; ++t)
  {
    if(myPlayingDeck[t]->checkCompleteSuitInOrder() == 0)
      return;
  }

  // Ok, the game has been finished
  Sprite* congratulation = mySpriteCollection->getByName("Congratulation");
  myCongratulation = new DrawingArea(myCardTable, "congratulation",
      10, 10, congratulation->width(), congratulation->height());
  myCongratulation->background(congratulation);
  myCongratulation->manage();

  if(soundSystem->state() == Enabled)
    soundSystem->playSample("Chord");
  else
    XBell(application->display(), 50);
}


