/***
 *
 * Implementation of cards-test.h.
 *
 * Author: Gene Fisher (gfisher@calpoly.edu)
 * Created: 23apr12
 * Modified: 263apr12
 *
 */

#include "cards_test.h"

/***
 *
 * Function main declares a couple testing card decks, and calls each of the
 * four top-level testing functions.
 *
 */
int main() {

    /*
     * Variable new_deck represents a deck of cards as typically arranged in a
     * new package when it's first opened.  Note the left padding of a blank in
     * all but the 10's cards.  This allows the suit to be accessed
     * consistently as deck[i][2], for any card i.
     */
    Deck new_deck = {
        "2C", "2D", "2H", "2S", "3C", "3D", "3H", "3S",
        "4C", "4D", "4H", "4S", "5C", "5D", "5H", "5S",
        "6C", "6D", "6H", "6S", "7C", "7D", "7H", "7S",
        "8C", "8D", "8H", "8S", "9C", "9D", "9H", "9S",
        "10C", "10D", "10H", "10S", "JC", "JD", "JH", "JS",
        "QC", "QD", "QH", "QS", "KC", "KD", "KH", "KS",
        "AC", "AD", "AH", "AS"
    }; 
    Deck shuffled_deck;         /* Shuffled deck returned from test_shuffle */

    /*
     * Test the deck shuffling function.
     */
    test_shuffle(new_deck, 8, shuffled_deck);

    /*
     * Test the hand dealing function with the new and shuffled decks.
     */
    test_deal_two_hands(new_deck, shuffled_deck);

    /*
     * Test the card comparison function.
     */
    test_compare_two_cards(new_deck, shuffled_deck);

    /*
     * Outta here.
     */
    return 0;
}

void test_shuffle(Deck new_deck, int num_shuffles, Deck shuffled_deck) {

    int i;                      /* Loop and array index */
    Deck decks[MAX_SHUFFLES];   /* Array of decks for side-by-side dumping */

    /*
     * Output a brief explanatory message.
     */
    printf("Results of %d test shuffles, with unshuffled deck shown in first output column: \n", num_shuffles);

    /*
     * Put the entering new deck in decks[0].
     */
    copy_deck(decks[0], new_deck);

    /*
     * Call shuffle num_shuffles times.
     */
    for (i = 0; i < num_shuffles; i++) {
        shuffle(decks[i], decks[i + 1]);
    }

    /*
     * Dump out the original deck and each of the shuffled decks, checking that
     * each deck has exactly one occurrence of each card.
     */
    dump_n_decks(decks, num_shuffles + 1);
    printf("\n\n");

    /*
     * Copy the 1st shuffled deck into the return parameter for use by a later
     * test.
     */
    copy_deck(shuffled_deck, decks[1]);
}

void test_deal_two_hands(Deck deck1, Deck deck2) {

    int top = 0;                /* Index of current top of deck */

    /*
     * Output a brief explanatory message.
     */
    printf("Using new deck, deal six hands of sizes 0, 1, 2, 3, 10, and 26:\n");

    /*
     * Using deck1, deal and dump hands of size 0, 1, 2, 3, 10, and 26.  The
     * last is a partial deal.
     */
    top += deal_and_dump(deck1, top, 0);
    top += deal_and_dump(deck1, top, 1);
    top += deal_and_dump(deck1, top, 2);
    top += deal_and_dump(deck1, top, 3);
    top += deal_and_dump(deck1, top, 10);
    top += deal_and_dump(deck1, top, 26);

    /*
     * Output the deck2 explanatory message.
     */
    printf("Using shuffled deck, deal six hands of sizes 0, 1, 2, 3, 5, 10, 26:\n");

    /*
     * Using deck2, deal and dump hands of size 0, 1, 2, 3, 10, and 26.  The
     * last is a partial deal.
     */
    top = 0;
    top += deal_and_dump(deck2, top, 0);
    top += deal_and_dump(deck2, top, 1);
    top += deal_and_dump(deck2, top, 2);
    top += deal_and_dump(deck2, top, 3);
    top += deal_and_dump(deck2, top, 10);
    top += deal_and_dump(deck2, top, 26);

    printf("\n");
}
    
int deal_and_dump(Deck deck, int top, int num_cards) {

    Hand hand1;                 /* 1st hand */
    Hand hand2;                 /* 2nd hand */
    int num_dealt;		/* Return value from deal_two_hands */

    /*
     * Do the deal.
     */
    num_dealt = deal_two_hands(deck, top, num_cards, hand1, hand2);

    /*
     * Dump each hand.
     */
    dump_hand(hand1, "Hand 1: ");
    dump_hand(hand2, "Hand 2: ");

    /*
     * Output a message if num_not_dealt is non-zero.
     */
    if (num_dealt < 2 * num_cards) {
        printf("Number of cards not dealt on last hand: %d\n", 
	    2 * num_cards - num_dealt);
    }

    /*
     * Finish off with a new line.
     */
    printf("\n");

    /*
     * Return the num_dealt for use by the caller
     */
    return num_dealt;
}

void dump_hand(Hand hand, char* message) {

    int i;                      /* Loop and array index */

    /*
     * Output the labeling message.
     */
    printf("%s", message);

    /*
     * Loop through the hand and dump each card.
     */
    for (i = 0; strcmp(hand[i], BLANK_CARD) != 0; i++) {
        printf("%s", hand[i]);

        /*
         * Output a comma separator for all but the last card in the hand.
         */
        if (strcmp(hand[i + 1], BLANK_CARD) != 0) {
            printf(", ");
        }
    }

    /*
     * Finish hand dump with new line.
     */
    printf("\n");
}

void test_compare_two_cards(Deck unshuffled_deck, Deck shuffled_deck) {

    int i;                      /* Loop and array index */

    /*
     * Output a brief explanatory message.
     */
    printf("Results of up to %d * 3 card comparisons are:\n", DECK_SIZE);

    /*
     * Call the compare function with unshuffled_deck[i] and
     * unshuffled_deck[i + 1 % DECK_SIZE], for 0 <= i < DECK_SIZE.
     */
    for (i = 0; i < DECK_SIZE; i++) {
        printf("Comparison of %s with %s yields: %d\n",
            unshuffled_deck[i],
            unshuffled_deck[(i + 1) % DECK_SIZE],
            compare_two_cards(unshuffled_deck[i],
                unshuffled_deck[(i + 1) % DECK_SIZE]));
    }
    printf("\n");

    /*
     * Call the compare function with unshuffled_deck[(i + 1) % DECK_SIZE] and
     * unshuffled_deck[i], for 0 <= i < DECK_SIZE.
     */
    for (i = 0; i < DECK_SIZE; i++) {
        printf("Comparison of %s with %s yields: %d\n",
            unshuffled_deck[(i + 1) % DECK_SIZE],
            unshuffled_deck[i],
            compare_two_cards(unshuffled_deck[(i + 1) % DECK_SIZE],
                unshuffled_deck[i]));
    }
    printf("\n");

    /*
     * Call the compare function with unshuffled_deck[i] and shuffled_deck[i],
     * for 0 <= i < DECK_SIZE, except where the cards are equal.
     */
    for (i = 0; i < DECK_SIZE; i++) {
        if (strcmp(unshuffled_deck[i], shuffled_deck[i]) != 0) {
            printf("Comparison of %s with %s yields: %d\n",
                unshuffled_deck[i],
                shuffled_deck[i],
                compare_two_cards(unshuffled_deck[i], shuffled_deck[i]));
        }
    }
    printf("\n");
}

void dump_n_decks(Deck* decks, int num_decks) {

    int i, j;                   /* Loop and array indices */
    int times_found;            /* Value returned from CardAppears... */

    /*
     * Loop through the array of decks, dumping the ith element of each on the
     * same line of output, separating each with a tab.  Also, check each deck
     * for exactly one occurrence of each card.
     */
    for (i = 0; i < DECK_SIZE; i++) {
        for (j = 0; (j < num_decks) && (j < MAX_SHUFFLES); j++) {
            printf("%s", decks[j][i]);

            /*
             * Output tab after all but last item.
             */
            if (j < num_decks - 1) {
                printf("%c", '\t');
            }

            /*
             * Check for exactly one occurrence.
             */
            if (! card_appears_in_deck_exactly_once(decks[j][i], decks[j],
                    &times_found )) {
                printf("SHUFFLE ERROR: Card %s  appears %d times in shuffled deck no. %d\n",
                    decks[j][i], times_found, j);
            }
        }
        printf("\n");
    }
}

void copy_deck(Deck dest_deck, Deck src_deck) {

    int i;                      /* Loop and array index */

    /*
     * Loop through the deck, copying each card from source to destination.
     */
    for (i = 0; i < DECK_SIZE; i++) {
        strcpy(dest_deck[i], src_deck[i]);
    }
}

int card_appears_in_deck_exactly_once(Card card, Deck deck, int* times_found) {

    int i;                      /* Loop and array index */

    /*
     * Start the times-found counter at 0.
     */
    *times_found = 0;

    /*
     * Search deck for the given card, incrementing times found for each
     * occurrence. 
     */
    for (i = 0; i < DECK_SIZE; i++) {
        if (strcmp(card, deck[i]) == 0) {
            (*times_found)++;
        }
    }

    /*
     * Return true if times_found = 1, false otherwise.
     */
    return *times_found == 1;
}