#import "Card.h"
/* -------------------------------------------------------------------
#import <objc/Storage.h>

typedef struct _CardData {
	char *stringValue;
	int stringLength;
	int intValue;	
	float floatValue;
	id object; } CardData;
#define cardDescription "{*iif@}"
#define CardFieldAt(C,F) (((NXStorageID *)C)->dataPtr+F)
#define CardStringField(F) CardFieldAt(F)->stringValue
enum {SortStrings, SortInts, SortFloats, SortObjects};

	RDR, Inc. 

	Objective-C source file for the class Card


	COPYRIGHT (C), 1991, RDR, Inc.
	ALL RIGHTS RESERVED.

	Responsible:			Approved:
	RDR:Ernest Prabhakar		

	Date:				Rev:
	1991-Jul-01			0.8

                                 Card                                 

	1. Introduction

  * A Card is any object to be collected in a Deck. 
  * Card is expected to receive the following messages
  *	- findSTR:(const char *)aString (returns self or nil) 
  *		This is to support searching.  Can also use the compiled regex.
  *	- (int)compare:aCard (-,0,+ if self <,=,> aCard)
  *		This is to support finding.
  * This particular implementation is designed for use with
  * flashcards, index cards, or playing cards.  It implements the
  * data as strings which can be read from/to streams.
  * Data streams are usually RTF or PostScript, but it doesn't matter.

  * The main ideas is that Card is a Storage of strings, numbers, and objects.


	2. Revision History

	___	The starting point.	Ernest Prabhakar/1990-Jun-29
	___	Add objects.		Ernest Prabhakar/1990-Aug-17
	___ 	Make fully general.	ENP/1991-Jul-01


	3. Source Code

------------------------------------------------------------------- */

/* ------------------------- Import files ------------------------- */
#import <appkit/nextstd.h>
#import <objc/hashtable.h>
#import <sys/param.h>
#import <regex.h>

/* ------------------------  Classes used  ------------------------ */
#import <appkit/Panel.h>
#import "List-Ordered.h"

/* --------------------------  Defines  --------------------------- */

#define FieldAt(F) ((CardData *)dataPtr+F)
#define StringField(F) FieldAt(F)->stringValue
#define ValidIndex(f) (f >= 0 && f < numElements)

#define IsEnd(CH) ((CH == '\n') || (CH == EOF))
#define NOUGHT '\0'

/* -------------------------  Constants  -------------------------- */

/* ----------------------  Class variables  ----------------------- */

static int  activeField;	/* which field to Find / Search on */
static int  sortFlag;		/* flag for sorting */

/* --------------------  Auxiliary Functions  --------------------- */

static CardData *getString(NXStream *stream)
{
	static char temp[MAXPATHLEN];
	int i=0;
	char c;
	static CardData field;
	
	while ((c=NXGetc(stream)) != '"' && c != EOF)
		temp[i++] = c;	/* NX_MALLOC(field,CardData,1);*/
	NX_MALLOC(field.stringValue,char,i+1);
	strncpy(field.stringValue,temp,i);
	(field.stringValue)[i]=NOUGHT;
	field.stringLength = i;
	if (IsEnd(c)) NXUngetc(stream);
	return &field;
}

static CardData *getWord(NXStream *stream)
{
	static char temp[MAXPATHLEN];
	int i=0;
	char c;
	static CardData field;
	
	while (((c=NXGetc(stream)) != '\t') && (c != ' ') && !IsEnd(c))
		temp[i++] = c;
	NX_MALLOC(field.stringValue,char,i+1);
	strncpy(field.stringValue,temp,i);
	(field.stringValue)[i]=NOUGHT;
	field.stringLength = i;
	if (IsEnd(c)) NXUngetc(stream);
	return &field;
}

/*====================================================================
                     Implementation of class Card                     
====================================================================*/

@implementation Card : Storage
{
}

/*====================================================================
                           Factory Methods                            
====================================================================*/
/* Set and Return Parameters */

/*--------------------------------------------------------------------
|+setActiveField:(unsigned)field| Sets the field used for Sorts/searches.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
+setActiveField:(unsigned)field
{
    activeField = field;
    return self;
}

/*--------------------------------------------------------------------
|+(unsigned)activeField|  Returns the field use for sorting/searching.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
+(unsigned)activeField
{
    return activeField;
}

/*--------------------------------------------------------------------
|+setSortFlag:(int)flag| 	Sets the sort flag;
  * There are no restrictions on 'flag' value, so to change sorting 
  * all you would need is redefine the compare method.
  * Existing values use String, InvertedStrings, and the Tag to sort;
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
+setSortFlag:(int)flag
{
    sortFlag = flag;
    return self;
}

/*--------------------------------------------------------------------
|+(int)sortFlag| 				Returns the sort flag.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
+(int)sortFlag
{
    return sortFlag;
}

/*====================================================================
                         Initialize and Free                          
====================================================================*/
/*--------------------------------------------------------------------
|-init|			 	  * Creates a Card with two (2) fields.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-init
{
    return [self initCount:2];
}

/*--------------------------------------------------------------------
|-initCount:(unsigned)count| 	       Creates a Card with count fields
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-initCount:(unsigned)count
{
    return [self initCount:count elementSize:sizeof(CardData)
	   description:cardDescription];
}

/*--------------------------------------------------------------------
|-free| 			     Free all the strings and objects.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-free
{
    int i;
    for (i = 0; i < numElements; i++) {
	if (StringField(i)) NX_FREE( StringField(i) );
	if (FieldAt(i)->object) [FieldAt(i)->object free];
    }
	
    return [super free];
}

/*====================================================================
                     Importing and Exporting Data                     
====================================================================*/

/*--------------------------------------------------------------------
|-importFrom:(NXStream *)stream| 
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-importFrom:(NXStream *)stream
{
    char c;

    while ((c=NXGetc(stream)) != EOF)
	    switch (c) {
		case '\n':
		    return self;
		case '"': 
		    [self addElement:getString(stream)];
		    break;
		case ',':
		case '\t':	/* Do nothing */
		case ' ':
		    break;
	      default: 
		    NXUngetc(stream);
		    [self addElement:getWord(stream)];
		    break;
	} 	

    return nil;			/* End of File */

}

/*--------------------------------------------------------------------
|-exportTo:(NXStream *)stream| 
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-exportTo:(NXStream *)stream
{
    int         i;

    for (i = 0; i < numElements; i++)
	NXPrintf(stream, "\"%s\"%s", StringField(i),
		 (i < (numElements-1)) ? ",\t" : "\n");

    return self;
}

/*====================================================================
                          String Fields I/O                           
====================================================================*/
/*--------------------------------------------------------------------
|-setStringValue:(const char *)aString|        Defaults to activeField
Creates a copy of the string, and frees when storage is freed.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setStringValue:(const char *)aString
{
    return [self setStringValue:aString at:activeField];
}

/*--------------------------------------------------------------------
|-setStringValue:(const char *)aString at:(int)field|
Creates a copy of the string, and frees when storage is freed.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setStringValue:(const char *)aString at:(int)field
{
    return [self setStringValueNoCopy:NXCopyStringBuffer(aString) at:field];
}

/*--------------------------------------------------------------------
|-setStringValueNoCopy:(char *)aString|  Defaults to activeField
Frees when storage is freed.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setStringValueNoCopy:(char *)aString
{
    return [self setStringValueNoCopy:aString at:activeField];
}

/*--------------------------------------------------------------------
|-setStringValueNoCopy:(char *)aString at:(int)field|
Frees when storage is freed.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setStringValueNoCopy:(char *)aString at:(int)field
{
    if (ValidIndex(field)) {
	if (StringField(field)) NX_FREE(StringField(field));
	StringField(field) = aString;
	FieldAt(field)->stringLength = strlen(aString);
	return self;
    }
    return nil;
}

/*--------------------------------------------------------------------
|-setStringValueFrom:(NXStream *)aStream|       Defaults to activeField
Reads in string from the stream.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setStringValueFrom:(NXStream *)aStream
{
    return [self setStringValueFrom:aStream at:activeField];
}

/*--------------------------------------------------------------------
|-setStringValueFrom:(NXStream *)aStream at:(unsigned)field|
Reads in string from the stream.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setStringValueFrom:(NXStream *)aStream at:(unsigned)field
{
    char *buf;
    int len, maxlen;

    if (ValidIndex(field)) {
	if (StringField(field)) NX_FREE(StringField(field));
	NXGetMemoryBuffer(aStream, &buf, &len, &maxlen);
	NX_MALLOC(StringField(field), char, len + 1);
	strncpy(StringField(field), buf, len);
	StringField(field)[len] = NOUGHT;
	FieldAt(field)->stringLength = len;
	return self;
    }
    return nil;
}

/*--------------------------------------------------------------------
|-(const char *)stringValue| 		      Defaults to active field.
Returns the string.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(const char *)stringValue
{
    return [self stringValueAt:activeField];
}

/*--------------------------------------------------------------------
|-(const char *)stringValueAt:(unsigned)field| 
Returns the string at that position, or NULL if invalid.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(const char *)stringValueAt:(unsigned)field
{
    if (ValidIndex(field)) {
	return StringField(field);
    } else {
	return NULL;
    }
} 

/*--------------------------------------------------------------------
|-(NXStream *)stringStream| 		      Defaults to active field.
Returns a stream pointing to the string.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(NXStream *)stringStream
{
    return [self stringStreamAt:activeField];
}

/*--------------------------------------------------------------------
|-(NXStream *)stringStreamAt:(unsigned)field| 
Returns a stream pointing to the string, if valid, else NULL.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(NXStream *)stringStreamAt:(unsigned)field
{
    if (ValidIndex(field))
	return NXOpenMemory(StringField(field), FieldAt(field)->stringLength-1,
			    NX_READONLY);
    return NULL;
}

/*====================================================================
                             Integer I/O
====================================================================*/

/*--------------------------------------------------------------------
|-setIntValue:(int)anInt| 		     Defaults to active field.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setIntValue:(int)anInt
{
    return [self setIntValue:anInt at:activeField];
}

/*--------------------------------------------------------------------
|-setIntValue:(int)anInt at:(unsigned)field| Defaults to active field.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setIntValue:(int)anInt at:(unsigned)field
{
    FieldAt(field)->intValue = anInt;
    return self;
}

/*--------------------------------------------------------------------
|-(int)intValue| 			     Defaults to active field.
Returns the integer.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(int)intValue
{
    return [self intValueAt:activeField];
}

/*--------------------------------------------------------------------
|-(int)intValueAt:(unsigned)field| 			     Defaults to active field.
Returns the integer.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(int)intValueAt:(unsigned)field
{
    return FieldAt(field)->intValue;
}

/*====================================================================
                              Float I/O                               
====================================================================*/

/*--------------------------------------------------------------------
|-setFloatValue:(float)aFloat| 	     Defaults to active field.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setFloatValue:(float)aFloat
{
    return [self setFloatValue:aFloat at:activeField];
}

/*--------------------------------------------------------------------
|-setFloatValue:(float)aFloat at:(unsigned)field|Defaults to active field.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setFloatValue:(float)aFloat at:(unsigned)field
{
    FieldAt(field)->floatValue = aFloat;
    return self;
}

/*--------------------------------------------------------------------
|-(float)floatValue| 			     Defaults to active field.
Returns the float.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(float)floatValue
{
    return [self floatValueAt:activeField];
}

/*--------------------------------------------------------------------
|-(float)floatValueAt:(unsigned)field| 	     Defaults to active field.
Returns the float.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(float)floatValueAt:(unsigned)field
{
    return FieldAt(field)->floatValue;
}

/*====================================================================
                             Object I/O
====================================================================*/

/*--------------------------------------------------------------------
|-setObject:anObject|	 		     Defaults to active field.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setObject:anObject
{
    return [self setObject:anObject at:activeField];
}

/*--------------------------------------------------------------------
|-setObject:anObject at:(unsigned)field|     Defaults to active field.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-setObject:anObject at:(unsigned)field
{
    FieldAt(field)->object = anObject;
    return self;
}

/*--------------------------------------------------------------------
|-object| 			  	     Defaults to active field.
Returns the object.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-object
{
    return [self objectAt:activeField];
}

/*--------------------------------------------------------------------
|-objectAt:(unsigned)field| 	     	     Defaults to active field.
Returns the object.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-objectAt:(unsigned)field
{
    return FieldAt(field)->object;
}

/*====================================================================
                         Finding and Sorting                          
====================================================================*/

/*--------------------------------------------------------------------
|-findSTR:(const char *) aString| 
 * If aString matches fields[sortField], return that data string.
  * Otherwise return null.  Can also use regular expersion (re_cmp).
 
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-findSTR:(const char *) aString
{
    if (re_exec(StringField(activeField)))
	return self;
    return nil;
}

/*--------------------------------------------------------------------
|-(int)compare:aCard|		  	    self(value) - aCard(value)
(-, 0, +) if self <, =, > aCard
Sorts according to activeField and sortType.

  * Pulls out the equivalent field and compares it.
  * You can overrule this for specialized Card types.

enum {SortStrings, SortInts, SortFloats, SortObjects}
 
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-(int)compare:aCard
{
    /* If not enough fields, sort by size */

    if ((numElements <= activeField) || ([aCard count] <= activeField))
	return numElements - [aCard count];

    switch (sortFlag) {
	case SortStrings:
	    return strcmp(StringField(activeField), [aCard stringValue]);
	case SortInts:
	    return [self intValue] - [aCard intValue];
	case SortFloats:
	    return [self floatValue] - [aCard floatValue];
        case SortObjects:
	    if ([[self object] respondsTo:@selector(compare:)])
		return [[self object] compare:[aCard object]];
	default:
 	    return numElements - [aCard count];
    }
}

/*--------------------------------------------------------------------
|-rotate| 					    Rotate the fields.
The first shall be last, and the last move up one.
Returns self.
                                          Ernest Prabhakar/1991-Jul-01
--------------------------------------------------------------------*/
-rotate
{
    CardData    field;
    field = *(CardData *)[self elementAt:0];
    [[self removeAt:0] addElement:(void *)&field];
    return self;
}

@end
