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

#import "Card.h"

/*-----------------------------------------------------------------------------
|
|    Card Globals
|
|    The arrays _CardPipPositions and _CardCardPips provide information about
|    the positioning of pips on cards.  There are 17 possible positions,
|    described in pipPositions, which are selectively refrerred to for each of
|    13 possible card values by a character string.
|
|    The letters A-Q are used to represent pips present in the given card.
|
|    The id's _CardFront, _CardBack etc... are images shared by all instances
|    of Card
|
\----------------------------------------------------------------------------*/

static NXPoint _CardPipPositions[] = {
    {17, 121}, {40, 121}, {63, 121},
    {40, 104},
    {40, 95},
    {17, 86}, {63, 86},
    {17, 69}, {40, 69}, {63, 69},
    {17, 52}, {63, 52},
    {40, 43},
    {40, 34},
    {17, 17}, {40, 17}, {63, 17} 
};

char *_CardCardPips[] = {
    "I",
    "BP",
    "BIP",
    "ACOQ",
    "ACIOQ",
    "ACHJOQ",
    "ACEHJOQ",
    "ACEHJMOQ",
    "ACFGIKLOQ",
    "ACDFGKLNOQ",
    "CO",
    "CO",
    "AQ"
};

static id _CardFrontImage, _CardBackImage, _CardSymbolsImage,
    _CardKingClubsImage, _CardKingDiamondsImage, _CardKingHeartsImage,
    _CardKingSpadesImage, _CardQueenClubsImage, _CardQueenDiamondsImage,
    _CardQueenHeartsImage, _CardQueenSpadesImage, _CardJackClubsImage,
    _CardJackDiamondsImage, _CardJackHeartsImage, _CardJackSpadesImage,
    _CardAceOfSpadesImage;
    
static id _preDrawnImage[52];

static BOOL _preDrawnImagesAvailable;
static BOOL _setCustomImage;


@implementation Card

/*----------------------------------------------------------------------------
|
|    + initialize
|
|    returns:  (id) [super initialize]
|
|-----------------------------------------------------------------------------
|
|    Initialize the private pasteboard type used by the CardPileView and load
|    all images used by the CardPileView class.
|			
\----------------------------------------------------------------------------*/

+ initialize
{
    if (self == [Card class])
    {
	_CardFrontImage = [NXImage findImageNamed:"Card"];
	_CardBackImage = [NXImage findImageNamed:"Back"];
	_CardKingClubsImage = [NXImage findImageNamed:"KingClubs"];
	_CardKingDiamondsImage = [NXImage findImageNamed:"KingDiamonds"];
	_CardKingHeartsImage = [NXImage findImageNamed:"KingHearts"];
	_CardKingSpadesImage = [NXImage findImageNamed:"KingSpades"];
	_CardQueenClubsImage = [NXImage findImageNamed:"QueenClubs"];
	_CardQueenDiamondsImage = [NXImage findImageNamed:"QueenDiamonds"];
	_CardQueenHeartsImage = [NXImage findImageNamed:"QueenHearts"];
	_CardQueenSpadesImage = [NXImage findImageNamed:"QueenSpades"];
	_CardJackClubsImage = [NXImage findImageNamed:"JackClubs"];
	_CardJackDiamondsImage = [NXImage findImageNamed:"JackDiamonds"];
	_CardJackHeartsImage = [NXImage findImageNamed:"JackHearts"];
	_CardJackSpadesImage = [NXImage findImageNamed:"JackSpades"];
	_CardAceOfSpadesImage = [NXImage findImageNamed:"AceOfSpades"];
	_CardSymbolsImage = [NXImage findImageNamed:"Symbols"];
    }
    [super initialize];
    return self;
}


/*----------------------------------------------------------------------------
|
|    + drawCardImages
|
|    returns:  (id) self
|
|-----------------------------------------------------------------------------
|
|    Draws the pre-cached images for every card in the deck.
|			
\----------------------------------------------------------------------------*/

+ drawCardImages
{
    id tempCard;
    int i,j;
    NXSize aSize;

    if(_preDrawnImagesAvailable) return self;	// don't draw again
    [_CardFrontImage getSize:&aSize];
    for(i=0; i<4; i++){
	for(j=0; j<13; j++){
	    tempCard = [[Card alloc] initSuit:i value:j faceUp:YES];
	    _preDrawnImage[(i*13)+j] = [[NXImage alloc] initSize:&aSize];
	    [_preDrawnImage[(i*13)+j] useDrawMethod:@selector(preDrawCard:) inObject:tempCard];
	    if([_preDrawnImage[(i*13)+j] lockFocus])
		[_preDrawnImage[(i*13)+j] unlockFocus];
	    [tempCard free];
	}
    }
    _preDrawnImagesAvailable = YES;
    return self;
}


/*----------------------------------------------------------------------------
|
|    + freeCardImages
|
|    returns:  (id) self
|
|-----------------------------------------------------------------------------
|
|    Frees the pre-cached images for every card in the deck.
|			
\----------------------------------------------------------------------------*/

+ freeCardImages
{
    int i;
    
    if(!_preDrawnImagesAvailable) return self;	// don't free again
    for(i=0; i<52; i++){
    	[_preDrawnImage[i] free];
    }
    _preDrawnImagesAvailable = NO;
    return self;
}


/*----------------------------------------------------------------------------
|
|    + setCardBackImage:(id)theImage
|
|    returns:  (id) self
|
|-----------------------------------------------------------------------------
|
|    Sets the image used to draw the card backs.
|			
\----------------------------------------------------------------------------*/

+ setCardBackImage:theImage
{
    if(theImage){
        if(_setCustomImage) [_CardBackImage free];
    	_CardBackImage = [theImage copyFromZone:[self zone]];
	_setCustomImage = YES;
    }
    return self;
}


/*----------------------------------------------------------------------------
|
|    + setCardBack:(CardBack)aBack
|
|    returns:  (id) self
|
|-----------------------------------------------------------------------------
|
|    Sets the image used to draw the card backs.
|			
\----------------------------------------------------------------------------*/

+ setCardBack:(CardBack)aBack
{
    if(_setCustomImage) [_CardBackImage free];
    _setCustomImage = NO;
    
    switch (aBack)
    {
    case CS_DEFAULT:
	_CardBackImage = [NXImage findImageNamed:"Back"];
	break;
    case CS_TRAD:
	_CardBackImage = [NXImage findImageNamed:"TradBack"];
	break;
    case CS_SHIP:
	_CardBackImage = [NXImage findImageNamed:"Ship"];
	break;
    default:
	_CardBackImage = [NXImage findImageNamed:"Back"];
	break;
    }
    return self;
}


/*----------------------------------------------------------------------------
|
|    - init
|
|    returns: (id)  [self initSuit:value:upsideDown:]
|
|-----------------------------------------------------------------------------
|
|    Acts as a cover for initSuit:value:faceUp:
|			
\----------------------------------------------------------------------------*/

- init
{
    return [self initSuit:CS_ACE value:CS_CLUBS faceUp:NO];
}


/*---------------------------------------------------------------------------
|
|    - initSuit:(CardSuit) aSuit value:(CardValue) aValue
|
|    returns: (id)  [self initSuit:value:faceUp:]
|
|----------------------------------------------------------------------------
|
|    Acts as a cover for initSuit:value:faceUp:
|			
\----------------------------------------------------------------------------*/

- initSuit:(CardSuit)aSuit value:(CardValue)aValue
{
    return [self initSuit:aSuit value:aValue faceUp:NO];
}


/*---------------------------------------------------------------------------
|
|    - initSuit:(CardSuit)aSuit value:(CardValue)aValue 
|        faceUp:(BOOL)anOrientation
|
|    returns: (id)    [super init]
|
|----------------------------------------------------------------------------
|
|    Initializes a Card object to the given suit, value and orientation
|			
\----------------------------------------------------------------------------*/

- initSuit:(CardSuit)aSuit value:(CardValue)aValue faceUp:(BOOL)anOrientation
{
    suit = aSuit;
    value = aValue;
    faceUp = anOrientation;
    return [super init];
}

/*---------------------------------------------------------------------------
|
|    - copyFromZone:(NXZone *)zone
|
|    returns: (id)  an object which is a duplicate of the sender
|
|----------------------------------------------------------------------------
|
|    Create a copy of a card.
|			
\----------------------------------------------------------------------------*/

- copyFromZone:(NXZone *)zone
{
    return [[Card allocFromZone:zone]
                       initSuit:suit 
		          value:value 
			 faceUp:faceUp];
}

/*---------------------------------------------------------------------------
|
|    - setSuit:(CardSuit)aSuit
|
|    returns: (id)  self
|
|----------------------------------------------------------------------------
|
|    Changes the card suit
|			
\----------------------------------------------------------------------------*/

- setSuit:(CardSuit)aSuit
{
    suit = aSuit;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - setValue:(CardValue)aValue
|
|    returns: (id)  self
|
|----------------------------------------------------------------------------
|
|    Changes the card value
|			
\----------------------------------------------------------------------------*/

- setValue:(CardValue)aValue
{
    value = aValue;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - flip
|
|    returns: (id)  self
|
|----------------------------------------------------------------------------
|
|    Turn the card over
|			
\----------------------------------------------------------------------------*/

- flip
{
    faceUp = !faceUp;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - setFaceUp:(BOOL)anOrientation
|
|    returns: (id)  self
|
|----------------------------------------------------------------------------
|
|	Turn the card to a specific orientation
|			
\----------------------------------------------------------------------------*/

- setFaceUp:(BOOL)anOrientation
{
    faceUp = anOrientation;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - (CardSuit) suit
|
|    returns: (CardSuit)  The card's suit
|
|----------------------------------------------------------------------------
|
|    Allow a client to determine the suit of a card
|			
\----------------------------------------------------------------------------*/

- (CardSuit)suit
{
    return suit;
}


/*---------------------------------------------------------------------------
|
|    - (CardValue) value
|
|    returns:   (CardValue)  The card's value
|
|----------------------------------------------------------------------------
|
|	Allow a client to determine the value of a card
|			
\----------------------------------------------------------------------------*/

- (CardValue)value
{
    return value;
}


/*---------------------------------------------------------------------------
|
|    - (BOOL)isFaceUp
|
|    returns: (BOOL)  YES if the card is face up
|
|              (BOOL)  NO otherwise
|
|----------------------------------------------------------------------------
|
|    Allow a client to determine the orientation of a card
|			
\----------------------------------------------------------------------------*/

- (BOOL)isFaceUp
{
    return faceUp;
}


/*---------------------------------------------------------------------------
|
|    - (CardColor) cardColor
|
|    returns: (CardColor)  The card's color
|
|----------------------------------------------------------------------------
|
|    Allow a client to determine the color of a card, as derrived from the
|    suit of the card
|			
\----------------------------------------------------------------------------*/

- (CardColor)cardColor
{
    if ((suit == CS_SPADES) || (suit == CS_CLUBS))
    {
        return CS_BLACK;
    }
    return CS_RED;
}

/*---------------------------------------------------------------------------
|
|    - preDrawCard :(id) theImage
|
|    returns:  (id)  self
|
|----------------------------------------------------------------------------
|
|    Pre draw a visual representation of ourself.
|
\----------------------------------------------------------------------------*/

- preDrawCard:theImage
{
    NXPoint thePoint={0.,0.};
    
    [self drawOutlineAt:&thePoint];
    [self drawContentsAt:&thePoint];
    return self;
}

/*---------------------------------------------------------------------------
|
|    - drawCardAt :(NXPoint *) thePoint
|
|    returns:  (id)  self
|
|----------------------------------------------------------------------------
|
|    Draw a visual representation of ourself at a given location.
|
\----------------------------------------------------------------------------*/

- drawCardAt:(NXPoint *)thePoint
{
    if(_preDrawnImagesAvailable && faceUp){
    	[_preDrawnImage[(suit*13)+value] composite:NX_SOVER toPoint:thePoint];
	return self;
    }
    [self drawOutlineAt:thePoint];
    [self drawContentsAt:thePoint];
    return self;
}


/*---------------------------------------------------------------------------
|
|    - drawOutlineAt:(NXPoint *)thePoint
|
|    returns: (id)  self
|
|----------------------------------------------------------------------------
|
|    Draw the outline of a card at a given location.
|
\----------------------------------------------------------------------------*/

- drawOutlineAt:(NXPoint *)thePoint
{
    [_CardFrontImage composite:NX_SOVER toPoint:thePoint];
    return self;
}


/*---------------------------------------------------------------------------
|
|    - drawContentsAt:(NXPoint *)thePoint
|
|    returns: (id)  self
|
|----------------------------------------------------------------------------
|
|    Draw our contents at a given location
|
\----------------------------------------------------------------------------*/

- drawContentsAt:(NXPoint *)thePoint
{
    if (!faceUp)
    {
    
	/*------------------------------------------------------ 
	|
	|    Draw the back of the card
	|
	\------------------------------------------------------*/

        NXPoint destPoint = {thePoint->x + 8, thePoint->y + 7};

        [_CardBackImage composite:NX_SOVER toPoint:&destPoint];
	
    } else {
    
	/*---------------------------------------------------------
	|
	|    Draw the upper left / lower right value indicator
	|
	\---------------------------------------------------------*/

        {
            NXRect sourceRect = {value * 22, 80, 11, 20};
            NXPoint destPoint = {thePoint->x + 4, thePoint->y + 141};
			
            if (([self cardColor] == CS_RED))
	    {
                sourceRect.origin.x += 11;
            }
            [_CardSymbolsImage composite:NX_SOVER
                                fromRect:&sourceRect 
				 toPoint:&destPoint];
			
            sourceRect.origin.y -= 20;
            destPoint.x = thePoint->x + 92;
            destPoint.y = thePoint->y + 6;
			
            [_CardSymbolsImage composite:NX_SOVER
                                fromRect:&sourceRect
	 			 toPoint:&destPoint];
        }

	/*----------------------------------------------------------
	|
	|    Draw the upper left / lower right suit indicator
	|
	\----------------------------------------------------------*/

        {
            NXRect sourceRect = {suit * 13, 15, 13, 15};
            NXPoint destPoint = {thePoint->x + 3, thePoint->y + 126};
			
	    [_CardSymbolsImage composite:NX_SOVER
				fromRect:&sourceRect
				 toPoint:&destPoint];
	    sourceRect.origin.x += 52;
	    destPoint.x = thePoint->x + 91;
	    destPoint.y = thePoint->y + 26;
	    [_CardSymbolsImage composite:NX_SOVER
				fromRect:&sourceRect 
				 toPoint:&destPoint];
        }

	/*---------------------------------------------------------
	|
	|    Draw special card images for face cards and the
	|    ace of spades
	|
	\---------------------------------------------------------*/

        {
	    NXPoint destPoint = {thePoint->x + 16, thePoint->y + 16};
    
	    switch (value)
	    {
	    case CS_JACK:
	        switch (suit)
		{
		case CS_CLUBS:
                    [_CardJackClubsImage composite:NX_SOVER
		                           toPoint:&destPoint];
		    break;
		case CS_DIAMONDS:
                    [_CardJackDiamondsImage composite:NX_SOVER
		                              toPoint:&destPoint];
		    break;
		case CS_HEARTS:
                    [_CardJackHeartsImage composite:NX_SOVER
		                            toPoint:&destPoint];
                    break;
                case CS_SPADES:
                    [_CardJackSpadesImage composite:NX_SOVER 
		                            toPoint:&destPoint];
		    break;
		}
		return self;

            case CS_QUEEN:
                switch (suit)
		{
		case CS_CLUBS:
                    [_CardQueenClubsImage composite:NX_SOVER 
		                            toPoint:&destPoint];
		    break;
		case CS_DIAMONDS:
                    [_CardQueenDiamondsImage composite:NX_SOVER
		                               toPoint:&destPoint];
		    break;
		case CS_HEARTS:
                    [_CardQueenHeartsImage composite:NX_SOVER
		                             toPoint:&destPoint];
		    break;
		case CS_SPADES:
                    [_CardQueenSpadesImage composite:NX_SOVER
		                             toPoint:&destPoint];
		    break;
		}
		return self;
            
            case CS_KING:
	        switch (suit)
		{
		case CS_CLUBS:
		    [_CardKingClubsImage composite:NX_SOVER
		                           toPoint:&destPoint];
		    break;
		case CS_DIAMONDS:
		    [_CardKingDiamondsImage composite:NX_SOVER 
			                      toPoint:&destPoint];
		    break;
		case CS_HEARTS:
		    [_CardKingHeartsImage composite:NX_SOVER
		                            toPoint:&destPoint];
		    break;
		case CS_SPADES:
                    [_CardKingSpadesImage composite:NX_SOVER 
		                            toPoint:&destPoint];
		    break;
		}
		return self;
		
	    default:
	        break;
	    }
			
	    /*----------------------------------------------------
	    |
	    |    Not only draw the special image for the ace of
	    |    spades, but avoid the standard suit pip drawing
	    |
	    \----------------------------------------------------*/

	    if ((value == CS_ACE) && (suit == CS_SPADES))
	    {
                [_CardAceOfSpadesImage composite:NX_SOVER toPoint:&destPoint];
                return self;
	    }
        }
		
	/*------------------------------------------------------
	|
	|    Draw suit pips on the card based on patterns
	|    defined by the _CardCardSymbols and
	|    _CardCardPips arrays
	|
	\------------------------------------------------------*/

	{
	    NXRect suitRect = {suit * 26, 31, 26, 29};
	    NXRect upsideSuitRect = {104 + suit * 26, 31, 26, 29};
	    char *pipString = _CardCardPips[value];

	    while (*pipString)
	    {
		NXPoint tempPoint = _CardPipPositions[*pipString - 'A'];
		tempPoint.x += thePoint->x;
		tempPoint.y += thePoint->y;
		[_CardSymbolsImage composite:NX_SOVER
		    fromRect:(*pipString < 'K') ? &suitRect : &upsideSuitRect
		     toPoint:&tempPoint];
		pipString++;
	    }
        }
    }
    return self;
}


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

- read:(NXTypedStream *)theStream
{
    [super read:theStream];
    NXReadTypes(theStream, "iic", &suit, &value, &faceUp);
    return self;
}


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

- write:(NXTypedStream *)theStream
{
    [super write:theStream];
    NXWriteTypes(theStream, "iic", &suit, &value, &faceUp);
    return self;
}

@end
