/* indent:4  tabsize:8  font:fixed-width */

#import "CardPile.h"

@implementation CardPile


/*---------------------------------------------------------------------------
|
|    + initialize
|
|    returns: (id) [super initialize]
|
|----------------------------------------------------------------------------
|
|    Sets a random seed based on the time before initializing the class
|            
\----------------------------------------------------------------------------*/

+ initialize
{
    srandom(time(0));
    return [super initialize];
}


/*---------------------------------------------------------------------------
|
|    - init
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Creates an empty list to manage the cards in the pile after 
|    initializing the object
|            
\----------------------------------------------------------------------------*/

- init
{
    [self initForCardSize:CS_SMALL];
    return self;
}


/*---------------------------------------------------------------------------
|
|    - initForCardSize:(CardSize)aSize
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Designated initializer for CardPile class.
|
\----------------------------------------------------------------------------*/

- initForCardSize:(CardSize)aSize
{
    [super init];
    cardSize = aSize;
    cardList = [[List allocFromZone:[self zone]]init];
    return self;
}


/*---------------------------------------------------------------------------
|
|    - setCardSize:(CardSize)aSize
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Set the card size.
|
\----------------------------------------------------------------------------*/

- setCardSize:(CardSize)aSize
{
    if (aSize != cardSize)
    {
        // have to change all the cards to the right class
	
        id newCard;
	id oldCard;
	int i;
	int cardCount = [cardList count];
	
        for (i = 0; i < cardCount; i++)
	{
	    oldCard = [cardList objectAt:i];
	    if (aSize == CS_SMALL)
	    {
	        newCard = [[SmallCard allocFromZone:[self zone]]
		              initSuit:[oldCard suit]
		                 value:[oldCard value]
				 faceUp:[oldCard isFaceUp]];
            }
	    else
	    {
	        newCard = [[Card allocFromZone:[self zone]] 
			      initSuit:[oldCard suit]
		                 value:[oldCard value]
				 faceUp:[oldCard isFaceUp]];
	    }
	    [cardList removeObjectAt:i];
	    [oldCard free];
	    [cardList insertObject:newCard at:i];

	}
        cardSize = aSize;
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - (CardSize)returnSize
|
|    returns: (CardSize) current card size
|
|----------------------------------------------------------------------------
|
|    Return the current card size.
|
\----------------------------------------------------------------------------*/

- (CardSize)returnSize
{
    return cardSize;
}


/*---------------------------------------------------------------------------
|
|    - free
|
|    returns: (id) [super free]
|
|----------------------------------------------------------------------------
|
|    Discards all of the card objects, the list they are contained in, 
|    and finally the CardPile itself.
|            
\----------------------------------------------------------------------------*/

- free
{
    [self freeCards];
    [cardList free];
    return [super free];
}


/*---------------------------------------------------------------------------
|
|    - addCard:aCard
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Acts as a cover for insertCard::, but always adds cards to the top of 
|    a pile.
|
\----------------------------------------------------------------------------*/

- addCard:aCard
{
    return [self insertCard:aCard at:CS_TOP];
}


/*---------------------------------------------------------------------------
|
|    - insertCard:aCard at:(int) aPosition
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Adds a card to the pile at a specific position.  The constants CS_TOP 
|    and CS_BOTTOM can be used to specify the top and bottom of the pile, 
|    respectively.
|
\----------------------------------------------------------------------------*/

- insertCard:aCard at:(int)aPosition
{
    if (aPosition == CS_TOP)
    {
        [cardList addObject:aCard];
    }
    else
    {
        [cardList insertObject:aCard at:aPosition];
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - addPile:aCardPile
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Acts as a cover for insertCardPile::, but always adds cards to the top 
|    of a pile.
|
\----------------------------------------------------------------------------*/

- addPile:aCardPile
{
    return [self insertPile:aCardPile at:CS_TOP];
}


/*---------------------------------------------------------------------------
|
|    - addCopyOfPile:aCardPile
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Acts as a cover for insertCopyOfCardPile:at:, but always adds cards to 
|    the top of a pile.
|
\----------------------------------------------------------------------------*/

- addCopyOfPile:aCardPile
{
    return [self insertCopyOfPile:aCardPile at:CS_TOP];
}


/*---------------------------------------------------------------------------
|
|    - insertPile:aCardPile at:(int)aPosition
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Adds another pile of cards to this pile at a specific position.  The
|    constants CS_TOP and CS_BOTTOM can be used to specify the top and bottom
|    of the pile, respectively.
|
\----------------------------------------------------------------------------*/

- insertPile:aCardPile at:(int)aPosition
{
    int        cardIndex;

    if (aPosition == CS_TOP)
    {
        for (cardIndex = 0; cardIndex < [aCardPile cardCount]; cardIndex++)
        {
            [cardList addObject:[aCardPile cardAt:cardIndex]];
        }
    }
    else
    {
        for (cardIndex = 0; cardIndex < [aCardPile cardCount]; cardIndex++)
        {
            [cardList insertObject:[aCardPile cardAt:cardIndex]at:aPosition++];
        }
    }
    return self;
}

/*---------------------------------------------------------------------------
|
|    - insertCopyOfPile:aCardPile at:(int)aPosition
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Adds another pile of cards to this pile at a specific position.  The
|    constants CS_TOP and CS_BOTTOM can be used to specify the top and bottom
|    of the pile, respectively.  The cards added are copies of (and 
|    independent from) the source cards.
|
\----------------------------------------------------------------------------*/

- insertCopyOfPile:aCardPile at:(int)aPosition
{
    int        cardIndex;

    if (aPosition == CS_TOP)
    {
        for (cardIndex = 0; cardIndex < [aCardPile cardCount]; cardIndex++)
        {
            [cardList addObject:[[aCardPile cardAt:cardIndex]copy]];
        }
    }
    else
    {
        for (cardIndex = 0; cardIndex < [aCardPile cardCount]; cardIndex++)
        {
            [cardList insertObject:[[aCardPile cardAt:cardIndex]copy] 
	                        at:aPosition++];
        }
    }
    return self;
}

/*---------------------------------------------------------------------------
|
|    - removeCard:aCard
|
|    returns: (id) nil if the object wasn't in the CardPile
|
|             (id) aCard if the card was removed
|
|----------------------------------------------------------------------------
|
|    Remove a specific card from the pile.  The card is not deallocated, it 
|    is up to the client to reuse or free the object.  Note that this is the 
|    only way to remove a card from the pile.
|    
\----------------------------------------------------------------------------*/

- removeCard:aCard
{
    return [cardList removeObject:aCard];
}


/*---------------------------------------------------------------------------
|
|    - addDeck
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Add a full deck of cards to the top of the pile, neatly ordered.
|    
\----------------------------------------------------------------------------*/

- addDeck
{
    int suitCounter, valueCounter;

    for (valueCounter = CS_LOWVALUE; 
         valueCounter <= CS_HIGHVALUE;  
	 valueCounter++)
    {
        for (suitCounter = CS_LOWSUIT; 
	     suitCounter <= CS_HIGHSUIT;  
	     suitCounter++)
        {
            if (cardSize == CS_SMALL)
            {
                [self insertCard:[[SmallCard allocFromZone:[self zone]]
		                                  initSuit:suitCounter 
		                                     value:valueCounter]
                                                        at:CS_TOP];
            }
            else if (cardSize == CS_LARGE)
            {
                [self insertCard:[[Card allocFromZone:[self zone]]
		                             initSuit:suitCounter 
		                                value:valueCounter]
                                                   at:CS_TOP];
            }
        }
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - empty
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Empties the card list describing the pile.
|    
\----------------------------------------------------------------------------*/

- empty
{
    [cardList empty];
    return self;
}


/*---------------------------------------------------------------------------
|
|    - freeCards
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Discards all of the card objects contained in the pile.
|    
\----------------------------------------------------------------------------*/

- freeCards
{
    [cardList freeObjects];
    return self;
}


/*---------------------------------------------------------------------------
|
|    - flip
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Turns the pile over, reversing the order of the cards and the 
|    orientation of each card.
|
\----------------------------------------------------------------------------*/

- flip
{
    int cardIndex;

    for (cardIndex = [cardList count]- 1; cardIndex >= 0; cardIndex--)
    {
        [cardList addObject:[[cardList removeObjectAt:cardIndex]flip]];
    }
    return self;
}



/*---------------------------------------------------------------------------
|
|    - shuffle
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Randomly re-order all card objects contained in the pile.
|    
\----------------------------------------------------------------------------*/

- shuffle
{
    id         temp;
    int        counter;
    int        swapIndex;
    int        cardCount = [cardList count];

    for (counter = 0; counter < cardCount - 1; counter++)
    {
        swapIndex = ( random () % ( cardCount - counter)) + counter;
        temp = [cardList objectAt:counter];
        [cardList replaceObjectAt:counter with:[cardList objectAt:swapIndex]];
        [cardList replaceObjectAt:swapIndex with:temp];
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - cardAt:(int) aPosition
|
|    returns: (id)        A card object for the given index, if it exists
|
|             (id)        nil if there is no card at the specified index
|
|----------------------------------------------------------------------------
|
|    Allow a client to find out what card is at a given position in the pile.  
|    The constants CS_TOP and CS_BOTTOM can be used to specify the top and 
|    bottom of the pile, respectively.
|    
\----------------------------------------------------------------------------*/

- cardAt:(int)aPosition
{
    if (aPosition == CS_TOP)
    {
        aPosition = [cardList count]- 1;
        if (aPosition == -1)
        {
            return nil;
        }
    }
    return [cardList objectAt:aPosition];
}


/*---------------------------------------------------------------------------
|
|    - (int) cardIndex:aCard
|
|    returns: (int)        A index for the given card, if it exists in the pile
|
|             (int)        NX_NOT_IN_LIST if the card isn't in the pile
|
|----------------------------------------------------------------------------
|
|    Allow a client to find out what card is at a given position in the stack.
|    
\----------------------------------------------------------------------------*/

- (int)cardIndex:aCard
{
    return [cardList indexOf:aCard];
}


/*---------------------------------------------------------------------------
|
|    - (int) cardCount
|
|    returns: (int) The number of cards in the pile
|
|----------------------------------------------------------------------------
|
|    Allow a client to find out how many cards are in the pile.
|    
\----------------------------------------------------------------------------*/

- (int) cardCount
{
    return [cardList count];
}


/*---------------------------------------------------------------------------
|
|    - read:(NXTypedStream *) theStream
|
|    returns: (int) self
|
|----------------------------------------------------------------------------
|
|    Dearchive a CardPile.
|    
\----------------------------------------------------------------------------*/

- read:(NXTypedStream *) theStream
{
    [super read:theStream];
    cardList = NXReadObject(theStream);
    NXReadTypes(theStream, "i", &cardSize);
    return self;
}


/*---------------------------------------------------------------------------
|
|    - write:(NXTypedStream *) theStream
|
|    returns: (int) self
|
|----------------------------------------------------------------------------
|
|    Archive a CardPile.
|    
\----------------------------------------------------------------------------*/

- write:(NXTypedStream *)theStream
{
    [super write:theStream];
    NXWriteObject(theStream, cardList);
    NXWriteTypes(theStream, "i", &cardSize);
    return self;
}

@end
