
#import "Deck.h"
#import "Card.h"
#import "Random.h"
#import <appkit/nextstd.h>
#import <appkit/Panel.h>
#import <limits.h>

/* External Function Declarations */

static Random *ranGen;		/* returns a random number */


extern char *re_comp(char *s);
#define	Notify(title, msg)  NXRunAlertPanel(title, msg, "OK", NULL, NULL)
#define	NotifyArg(title, msg, arg)\
 NXRunAlertPanel(title, msg, "OK", NULL, NULL, arg)

#define MARKED (marker != DECK_UNMARKED)
#define MARK_OR_NE	(MARKED ? marker : numElements)

#define copyIds(From, To, N) bcopy(From, To, sizeof(id) * N)

static int  qcomp(a, b)
    id         *a, *b;
{
    return[*a compare:*b];
}

BOOL        regexString(char *expr)
{
    char       *result = re_comp(expr);
    if (result) {
	Notify("Find", result);
	return NO;
    }
    return YES;
}


@implementation Deck

/* Creating and freeing a Deck object */
+ new
{
    self = [self newCount:8];
    marker = DECK_UNMARKED;
    return self;
}

+ initialize
{
    [self setVersion:DECK_VERSION];
    ranGen = [Random new];
    return self;
}

- copy				/* Kludge since List is broken (reported) */
{
    id          newDeck = [[self class] newCount:numElements];
    copyIds(dataPtr, newDeck->dataPtr, numElements);
    newDeck->numElements = numElements;
    newDeck->marker = marker;
    return newDeck;
}

/* Marker Manipulation */

- setMarkerAtEnd
{
    return[self setMarkerAt:numElements];
}

- setMarkerAt:(unsigned)index
{
    marker = MIN(index, numElements);
    return self;
}

- (unsigned)marker
{
    return marker;
}

- clearMarker
{
    marker = DECK_UNMARKED;
    return self;
}


/* draw and discard */
- giveCard
{
    return[self giveCardAt:0];
}

- giveCardAt:(unsigned)index
{
    if (index >= MARK_OR_NE)
	return nil;
    if (MARKED)
	--marker;
    return[self removeObjectAt:index];
}

- takeCard:anObject
{
    return[self addObject:anObject];
}

/* Manipulate Deck */

- (unsigned)dealTo:(List *) handList
{
    return[self dealTo:handList each:numElements];
}

- (unsigned)dealTo:(List *) handList each:(int)numCards
{
    int         hands = [handList count];
    int         c, h;
    if (hands * numCards > numElements)
	numCards = numElements / hands;

    for (c = 0; c < numCards; c++)
	for (h = 0; h < hands; h++)
	    [NX_ADDRESS(handList)[h] takeCard:[self giveCard]];

    return (marker = numElements);
}

- emptyTo:(Deck *) hand
{
    id          card;
    while (card = [self giveCard])
	[hand takeCard:card];
    return self;
}

- cutAt:(unsigned)index
{
    id         *copyPtr, *copyIndex;
    id         *dataEnd, *dataIndex;
    if (index < 0 || index > MARK_OR_NE)
	return nil;
    NX_MALLOC(copyPtr, id, numElements);
    copyIds(dataPtr, copyPtr, numElements);

    copyIds(copyPtr + index, dataPtr, numElements - index);
    copyIds(copyPtr, dataPtr + (numElements - index), index);

    NX_FREE(copyPtr);
    [self setMarkerAtEnd];
    return self;
}

static void mixup(id * aPtr, id * bPtr, int n)
{
    id         *aIndex, *bIndex, *bEnd;
    int         i;
    bIndex = bPtr + n;		/* Fill in Backwards */
    bEnd = bPtr + (n / 5);	/* Mix 80% of indices */

    [ranGen scaleFrom:0 to:n];
    while (bIndex > bEnd) {	/* Pull in at Random */
	while (aPtr[i = [ranGen whole]] == nil);
	*(--bIndex) = aPtr[i];
	aPtr[i] = nil;
    }

    aIndex = aPtr - 1;
    while (bIndex > bPtr) {	/* Get rest */
	while (*(++aIndex) == nil);
	*(--bIndex) = *aIndex;
    }
}

- shuffle:sender
{
    id         *copyPtr, *copyEnd;
    [self setMarkerAtEnd];
    NX_MALLOC(copyPtr, id, numElements);

    mixup(dataPtr, copyPtr, numElements);
    mixup(copyPtr, dataPtr, numElements);

    NX_FREE(copyPtr);
    return self;
}

- sort:sender
{
    [self setMarkerAtEnd];
    qsort(dataPtr, numElements, sizeof(id), qcomp);
    return self;
}

- flipAll:sender
{
    id         *dataIndex, *dataEnd;
    dataEnd = dataPtr + numElements;
    for (dataIndex = dataPtr; dataIndex < dataEnd; ++dataIndex)
	[*dataIndex flip];
    return self;
}

- reverse:sender
{
    id         *copyPtr, *copyIndex;
    id         *dataIndex;
    [self setMarkerAtEnd];
    NX_MALLOC(copyPtr, id, numElements);
    copyIds(dataPtr, copyPtr, numElements);

    dataIndex = dataPtr + numElements;	/* Fill in Backwards */
    copyIndex = copyPtr;

    while (dataIndex > dataPtr)	/* Get rest */
	*(--dataIndex) = *(copyIndex++);

    NX_FREE(copyPtr);
    return self;
}

- findSTR:(const char *) aString
{
    id         *dataIndex, *dataEnd;
    dataEnd = dataPtr + numElements;
    if (regexString(aString))
	for (dataIndex = dataPtr; dataIndex < dataEnd; ++dataIndex)
	    if ([*dataIndex findSTR:aString])
		return *dataIndex;

    return nil;
}

- findAllSTR:(const char *) aString
{
    id         *dataIndex, *dataEnd;
    id          list;
    dataEnd = dataPtr + numElements;
    list = [List new];
    if (regexString(aString))
	for (dataIndex = dataPtr; dataIndex < dataEnd; ++dataIndex)
	    if ([*dataIndex findSTR:aString])
		[list addObject:*dataIndex];

    if ([list count])
	return list;
    else
	return nil;
}

@end
