/*
    UltraSet (C) 2003 Jason Hunt
    huntjas2@msu.edu
*/

#include <vcl.h>
#include "set_unit.h"
#include "set_code.h"

/* display a debug message */
void debug(String label, String data)
{
    set_game->Memo1->Lines->Add(label + " " + data);
}



/* constructor for known card data */
card_class::card_class(byte nNumber, byte nFill, byte nColor, byte nShape)
{
    number = nNumber;
    fill = nFill;
    color = nColor;
    shape = nShape;

    selected = false;
    hint = false;

    base_image = set_game->board.images[9*(nFill-1) + 3*(nColor-1) + (nShape-1)];
}
/* end of card constructor */


/* draw a the card at pos grid position */
void card_class::draw( byte pos )
{
    // calculate the pixel position based on the grid position
    unsigned int base_x = (pos / 3) * (set_game->board.card_x_size + x_pad) + 3;
    unsigned int base_y = (pos % 3) * (set_game->board.card_y_size + y_pad) + 3;

    image *back;

    if (selected)
        back = set_game->board.card_base_sel;
    else
        if (hint && set_game->board.cheat)
            back = set_game->board.card_base_che;
        else
            back = set_game->board.card_base;

    set_game->Canvas->Draw(base_x, base_y, back);

    // adjust the starting base posiong to draw the card primatives
    base_x += 12; base_y += 10;

    unsigned char count = number;

    if (number == 1) base_y += 36;   // move one down farther
    if (number == 2) base_y += 18;

    // draw count number of cards
    while (count--)
    {
        set_game->Canvas->Draw(base_x, base_y, base_image);
        base_y += 36;
    }
}
/* end of draw card */



/* the board class constructor */
board_class::board_class( void )
{
    randomize(); // ensure a random game on each program startup

    // all of the image filenames
    AnsiString res_names[27] =  {"LGD","LGO","LGS","LPD","LPO","LPS","LRD",
                                 "LRO","LRS","OGD","OGO","OGS","OPD","OPO",
                                 "OPS","ORD","ORO","ORS","SGD","SGO","SGS",
                                 "SPD","SPO","SPS","SRD","SRO","SRS"};

    // load the unselected card background from the resources
    card_base = new Graphics::TBitmap();
    card_base->LoadFromResourceName((int)HInstance, "IM_BASE");

    // load the selected card background from the resources
    card_base_sel = new Graphics::TBitmap();
    card_base_sel->LoadFromResourceName((int)HInstance, "IM_BASE_SEL");

    // load the cheat base
    card_base_che = new Graphics::TBitmap();
    card_base_che->LoadFromResourceName((int)HInstance, "IM_BASE_CHE");

    /* load all of the images */
    for (unsigned int index = 0; index < 27; index++)
    {
        // create the image
        images[index] = new Graphics::TBitmap();

        // load the base image from the resource
        images[index]->LoadFromResourceName((int)HInstance, "IM_" + res_names[index]);
    }

    // set the card size
    card_x_size = card_base->Width;
    card_y_size = card_base->Height;

    // clear cheat mode
    cheat = 0;

    // reset the board variables
    reset();
}
/* end of the board constructor */



/* the board destructor */
board_class::~board_class( void )
{
    // free the card base image
    if (card_base != NULL) delete card_base;

    // free the selected card base image
    if (card_base_sel != NULL) delete card_base_sel;

    // free the cheat card base image
    if (card_base_che != NULL) delete card_base_che;

    // free all of the card primitaves
    for (unsigned int index = 0; index < 27; index++)
        if (images[index] != NULL) delete images[index];
}
/* end of the board destructor */


/* reset the board variables */
void board_class::reset( void )
{
    // reaset game variables
    num_selected = 0;

    // clear the number of cards
    num_cards = 0;

    // setup the inital board dimentions
    x_size = 4;

    // size the form properly
    set_game->Width  = x_size * (card_x_size + x_pad) + 2;
    set_game->Memo1->Width  = set_game->Width - 10;
    set_game->Edit1->Width  = set_game->Width - 10;


    // create the draw deck
    make_deck();

    // make sure there are no cards on the board
    for (int count = 0; count < max_cards; count++)
        if (positions[count] != NULL)
        {
            delete positions[count];    // delete the card
            positions[count] = NULL;    // make the position as empty
        }

    // add 12 random cards
    for (unsigned int index = 0; index < 12; index++)
        add_card();

    set_on_board();

    // draw the board
    draw();
}
/* end of board_class::reset */


/* create the draw deck */
void board_class::make_deck( void )
{
    // clear the deck
    deck.clear();

    // create the deck
    for (int number = 1; number <= 3; number++)
        for (int fill = 1; fill <= 3; fill++)
            for (int color = 1; color <= 3; color++)
                for (int shape = 1; shape <= 3; shape++)
                    deck.push_back(card_class(number, fill, color, shape));
}
/* end of create deck */


/* draw the entire board */
void board_class::draw( void )
{
    for (int i = 0; i < max_cards; i++)
        if (positions[i] != NULL)
            positions[i]->draw(i);
}
/* end of draw board */


/* erase a board position */
void board_class::erase_position( unsigned int pos_index )
{
    // calculate the pixel position based on the grid position
    unsigned int base_x = (pos_index / 3) * (card_x_size + x_pad) + 3;
    unsigned int base_y = (pos_index % 3) * (card_y_size + y_pad) + 3;

    set_game->Canvas->Pen->Color = clWhite;
    set_game->Canvas->Rectangle(base_x, base_y, base_x + card_x_size, base_y + card_y_size);
}
/* end of erase board position */


/* adds a card to the board */
byte board_class::add_card( void )
{
    if (deck.size() > 0)
    {
        byte pos_index;

        for (pos_index = 0; pos_index < max_cards; pos_index++)
            if (positions[pos_index] == NULL) break;

        if (pos_index == max_cards)
        {
            debug("Addcard", "the board is fill");
            return(0);
        }

        if (pos_index / 3 >= x_size)
        {
            // make some room for it
            x_size++;

            // resize the form
            set_game->Width += card_x_size + x_pad + 2;
            set_game->Memo1->Width  = set_game->Width - 10;
            set_game->Edit1->Width  = set_game->Width - 10;
        }

        // choose a card from the deck
        byte deck_index = random(deck.size());

        // make room for it and copy it from the deck
        positions[pos_index] = new card_class(deck[deck_index]);

        // remove card from deck
        deck.erase(deck.begin() + deck_index);

        // increase the number of cards
        num_cards++;

        return(pos_index);
    }
    return(0); // unable to add card
}
/* end of board_class::add_card */


/* adds a card to the board */
void board_class::remove_card( byte pos )
{
   num_cards--;

   erase_position(pos);
   delete positions[pos];
   positions[pos] = NULL;
}
/* end of board_class::add_card */

/***************************************
** End of the class utility functions **
***************************************/



/************************************
** All of the class game functions **
************************************/

/* compares three card attributes */
inline int check(byte A, byte B, byte C)
{
    return (((A == B) && (B == C) && (C == A)) ||
            ((A != B) && (B != C) && (C != A)));
}
/* end of same or different */

/* returns false if the passed cards form a set */
inline byte board_class::cards_are_set(card_class *cards[3])
{
    if (!check(cards[0]->number, cards[1]->number, cards[2]->number)) return(1); else // number
    if (!check(cards[0]->fill,   cards[1]->fill,   cards[2]->fill))   return(2); else // fill
    if (!check(cards[0]->color,  cards[1]->color,  cards[2]->color))  return(3); else // color
    if (!check(cards[0]->shape,  cards[1]->shape,  cards[2]->shape))  return(4); else // shape
        return(0); // founf a set
}
/* end of cards_are_set */


/* add three cards to the board */
void board_class::add_three_cards( void )
{
    for (int i = 0; i < 3; i++)
    {
        byte new_index =  add_card();

        if (positions[new_index] != NULL)
            positions[new_index]->draw(new_index);
    }
}
/* end of add_three_cards */



/* the user clicked on a card */
void board_class::card_click(int x, int y)
{
    // convert from screen pos in board index
    byte pos =  x / (card_x_size + x_pad) * 3 + y / (card_y_size + y_pad);

    // make sure we are in range
    if (pos >= max_cards) return;

    // make sure there is cards there
    if (positions[pos] == NULL) return;

    if (positions[pos]->selected)
    {
        // unselect the card
        num_selected--;
        positions[pos]->selected = false;
        positions[pos]->draw(pos);
    }
    else
        if (num_selected < 3)
        {
            // select the card
            num_selected++;
            positions[pos]->selected = true;
            positions[pos]->draw(pos);

            // the user selected three cards, see if its a set
            if (num_selected == 3)
             check_selected_cards();
       }

}
/* end of select_check */


/* check the selected cards for a set */
void board_class::check_selected_cards( void )
{
    card_class *check[3];
    byte index_list[3];
    unsigned int found_count = 0;

    for (int index = 0; index < max_cards; index++)
    {
        if ((positions[index] != NULL))
        {
            if (positions[index]->selected && (found_count < 3))
            {
                index_list[found_count] = index;
                check[found_count++] = positions[index];
            }
        }
    }

    if (found_count != 3) return; // we did not find three cards

    byte result = cards_are_set(check);

    if (result == 0)
    {
        set_game->Status->SimpleText = "Found a set";

        num_selected = 0;

        // remove the three selected cards
        for (int count = 0; count < 3; count++)
            remove_card(index_list[count]);

        // ensure a set one board
        set_on_board();

        // ensure a full board, if possiable
        if (num_cards < 12)
            add_three_cards();

        // we have more rows then needed
        if ((x_size > num_cards / 3) && (num_cards >= 12))
        {
            rearrange_board();          // compress the board
            x_size--;                   // decrese the grid width
            set_game->Memo1->Width  -=  card_x_size + x_pad + 2; // resize the form
            set_game->Edit1->Width  -=  card_x_size + x_pad + 2; // resize the form
            set_game->Width -= card_x_size + x_pad + 2; // resize the form
        }
    }
    else
    {
        // tell the use why the cards they selected were not a set
        if (result == 1) set_game->Status->SimpleText = "Check the number"; else
        if (result == 2) set_game->Status->SimpleText = "Check the fill"; else
        if (result == 3) set_game->Status->SimpleText = "Check the color"; else
        if (result == 4) set_game->Status->SimpleText = "Check the shape";
    }
}
/* end of check_selected_cards */


/* return true if there is a set on the board, also mark the hint cards */
byte board_class::locate_set( void )
{
    byte set_found = 0;

    card_class *check[3];

    // check every combination for a set
    for (unsigned int card1 = 0; (card1 < max_cards) && !set_found; card1++)
       for (unsigned int card2 = 0; (card2 < max_cards) && !set_found; card2++)
           for (unsigned int card3 = 0; (card3 < max_cards) && !set_found; card3++)
               if (((card1 != card2) &&
                    (card2 != card3) &&
                    (card3 != card1)) &&
                  ((positions[card1] != NULL) &&
                   (positions[card2] != NULL) &&
                   (positions[card3] != NULL)))
                   {
                        check[0] = positions[card1];
                        check[1] = positions[card2];
                        check[2] = positions[card3];

                        if (cards_are_set(check) == 0)
                        {
                            check[0]->hint = true;
                            check[1]->hint = true;
                            check[2]->hint = true;

                            set_found = 1;

                            positions[card1]->draw(card1);
                            positions[card2]->draw(card2);
                            positions[card3]->draw(card3);
                        }
                   }

    return(set_found);
}
/* end of locate set */


/* check the currently selected cards for a set */
void board_class::set_on_board( void )
{
    if (!locate_set())
    {
        if (deck.size() > 0)
        {
            add_three_cards();            // add three more cards
            set_on_board(); // keep recursing until there is set on board
        }
        else
        {
            ShowMessage("No More moves.");
            reset();
        }
    }
}
/* end of check_for_set */


/* clear up the board layout */
void  board_class::rearrange_board( void )
{
    card_class *out_list[3];
    byte out_count = 0;

    // make a list of the cards on the right hand side
    for (int i = max_cards - 1; (i >= 0) && (out_count < 3); i--)
    {
        if (positions[i] != NULL)
        {
            out_list[out_count++] = positions[i];
            positions[i] = NULL;
            erase_position(i);
        }
    }

    // find a more compact place to put them
    while (out_count--)
    {
        int found_spot = 0;
        for (int i = 0; (i < max_cards) && (!found_spot); i++)
            if (positions[i] == NULL)
            {
                positions[i] = out_list[out_count];
                positions[i]->draw(i);
                found_spot = 1;
            }
    }
}

/************************************
** End of the class game functions **
************************************/

