/* AddressView.m:
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 *
 * Written by Mai Nguyen, NeXT Developer Support
 *
 */

#import <appkit/appkit.h>
#import <objc/List.h>
#import <dbkit/DBRecordList.h>
#import <dbkit/DBValue.h>
#import <libc.h>

#import "AddressView.h"
#import "Controller.h"

#define EMPTY_STRING NXLocalizedString("Cannot accept empty string", NULL, "Notify user that empty string input is not valid.")

static char cellName[100];

@implementation AddressView

- initFrame:(const NXRect *)frameRect
{
  	NXSize interCellSpacing = {0.0, 0.0}, docSize;

  [	super initFrame:frameRect];
  	cellMatrix = [[Matrix alloc] initFrame:frameRect
		mode:NX_LISTMODE
		cellClass:[TextFieldCell class]
		numRows:0
		numCols:1];

  	/*
  	 * In the following lines,
  	 * remember that "cellMatrix" is the matrix that will be installed
  	 * in the scrollview, "self" is the scrollview.
  	 */

  		/* we don't want any space between the matrix's cells  */
  	[cellMatrix setIntercell:&interCellSpacing];
  		/* resize the matrix to contain the cells */
 	[cellMatrix sizeToCells];
  	[cellMatrix setAutosizeCells:YES];
	
  	/*
  	 * when the user clicks in the matrix and then drags the mouse out of
  	 * scrollView's contentView, we want the matrix to scroll
  	 */
  	[cellMatrix setAutoscroll:YES];
  		/* let the matrix stretch horizontally */
  	[cellMatrix setAutosizing:NX_WIDTHSIZABLE];  
  		/* Install the matrix as the docview of the scrollview */
  	[[self setDocView:cellMatrix] free];
 	 	/* set up the visible attributes of the scrollview */
  	[self setVertScrollerRequired:YES];
  	[self setBorderType:NX_BEZEL];
  		/* tell the subviews to resize along with the scrollview */
  	[self setAutoresizeSubviews:YES];
  		/* This is the only way to get the clipview to resize too */
  	[[cellMatrix superview] setAutoresizeSubviews:YES];
  		/* Allow the scrollview to stretch both horizontally and vertically */
  	[self setAutosizing:NX_WIDTHSIZABLE|NX_HEIGHTSIZABLE];
  		/* Resize the matrix to fill the inside of the scrollview */
 	[self getContentSize:&docSize];
   	[cellMatrix sizeTo:docSize.width :docSize.height];

	/* Set the matrix single click action */
	[cellMatrix setTarget:self];
  	[cellMatrix setAction:@selector(showInfo:)];
 	 
	/* Allocate DBValue instances for accessing record contents */
	aValue = [[DBValue alloc] init];
	aValue2 = [[DBValue alloc] init];
	return self;
}

- free
{
 	 [cellMatrix free];
	 if (aValue)
	 	[aValue free];
	if (aValue2)
		[aValue2 free];
 	 return [super free];
}

- cellMatrix
{
 	 return cellMatrix;
}

/*
 * Since we recycle the cells (via renewRows:cols:), we also set the state
 * of each cell to 0 and unhighlight it.  If we don't do that, the recycled
 * cells will display their previous state.
 */
- loadCellsFrom:sender

{
    int i, cellCount;
 	const char *firstName, *lastName;
	char buf[100];
	DBRecordList *	recordList;
	List *	propertyList;
  		
	recordList = (DBRecordList *)[controller getRecordList];
	propertyList = (List *)[controller getPropertyList];
	cellCount = (int) [controller getRecordCount];
	
 	if (cellCount == 0 )
  		return self;
  
  	 
   	 /* tell the matrix there are 0 cells in it (but don't deallocate them) */
  	[cellMatrix renewRows:0 cols:1];
  	[cellMatrix lockFocus];		/* for highlightCellAt::lit: */
 	 for (i=0;i<cellCount;i++) {
    	TextFieldCell *cell;
   		 /*
    	 * add a row to the matrix.  (This doesn't necessarily allocate a new
    	 * cell, thanks to renewRows:cols:).
    	 */
    	[cellMatrix addRow];
    	cell = [cellMatrix cellAt:i:0];
   		 /* make sure the cell is neither selected nor highlighted */
   		 [cellMatrix highlightCellAt:i:0 lit:NO];
    	[cell setState:0];
		
    	/* install the string value in that cell */
		[recordList getValue:aValue 
					forProperty:[propertyList objectAt:1]at:i];											 
		lastName= (const char *)[aValue stringValue];

		[recordList getValue:aValue2 
					forProperty:[propertyList objectAt:2]at:i];														 
		firstName = (const char *)[aValue2 stringValue];
		sprintf(buf, "%s %s", lastName, firstName);
    	[cell setStringValue:buf];
		
  	}
  	[cellMatrix unlockFocus];
  	[cellMatrix sizeToCells];
  	[cellMatrix display];
  	
 	 return self;
}

/* Get the newly added row */
- (int) getNewRow
{
	int i;
	int count, row;
	const char* name;
	
	count = [cellMatrix cellCount];
	row = count; 	/* Initialize row to a meaningful number */
	for (i = 0; i < count; i++) {
		name = [[cellMatrix cellAt:i:0] stringValue];
		if (!strcmp(cellName, name) ) {
			row = i;
			break;
		}
	}
	return row;
}


/* Show the information contained in each selected record
 */
 
- showInfo:sender
{
	int index;
	DBRecordList * recordList;
	List *	propertyList;

	recordList = [controller getRecordList];
	propertyList = [controller getPropertyList];

	index = [cellMatrix selectedRow];

		/* Find the person's id	*/
	
	[recordList getValue:aValue forProperty:[propertyList objectAt:0]at:index];															 																		 
	[ssnField setStringValue:(const char *)[aValue stringValue]];


		/* Find the person's last name and first name */
	[recordList getValue:aValue forProperty:[propertyList objectAt:1]at:index];															 																		 
	[lnameField setStringValue: (const char *)[aValue stringValue]];

	[recordList getValue:aValue forProperty:[propertyList objectAt:2]at:index];																	 
    [fnameField setStringValue:(const char *)[aValue stringValue]];

				/* Find the person's phone number */
	[recordList getValue:aValue forProperty:[propertyList objectAt:3]at:index];														 																		 
	[phoneField setStringValue:(const char *)[aValue stringValue]];
	
		/* Find the person's address */
	[recordList getValue:aValue forProperty:[propertyList objectAt:4]at:index];														 																		 
	[addressField setStringValue:(const char *)[aValue stringValue]];

		/* Find the person's city of residence */
	[recordList getValue:aValue forProperty:[propertyList objectAt:5]at:index];
	[cityField setStringValue:(const char *)[aValue stringValue]];


		/* Find the person's state of residence */
	[recordList getValue:aValue forProperty:[propertyList objectAt:6]at:index];														 																		 
	[stateField setStringValue:(const char *)[aValue stringValue]];

		/* Find the person's zip code */
	[recordList getValue:aValue forProperty:[propertyList objectAt:7]at:index];														 																		 
	[zipField setStringValue:(const char *)[aValue stringValue]];
	return self;
}

/* Add a new record at the specified index. 
 */
- addRecordFrom:sender at:(unsigned)index
{		
	DBRecordList *	recordList;
	List *	propertyList;
	
	recordList = (DBRecordList *)[controller getRecordList];
	propertyList = (List *)[controller getPropertyList];
	
			/* In order to get records in and out, 
			 *	we must use a value object all the time
			 */
					
		/* add empty record */
	[recordList insertRecordAt:index];
	if ([self setNewRecordFrom:sender at:index] == nil) {
		return nil;
	}
								 			
	if ([recordList saveModifications] == 0)
		return self;
			
	return nil;	
}

/* Update the record specified at index.
 */
 
- updateRecordFrom:sender at:(unsigned) index
{
	DBRecordList *	recordList;
	List * propertyList;	
		
	recordList = (DBRecordList *)[controller getRecordList];
	propertyList = (List *) [controller getPropertyList];
		
	if ([self setNewRecordFrom:sender at:index] == nil) {
		return nil;
	}
	
	if ([recordList saveModifications] == 0)
		return self;
	return nil;	
}

/* Get the user input from the text fields. Note that if a field is empty,
 * the operation (either insert or update) would fail. Also, for simplicity,
 * an arbitrary contract number is assigned to new records.
 */
- setNewRecordFrom:sender at:(unsigned)index
{
	DBRecordList *aRecList;
	List *aPropList;
	const char *inputString;
	const char *lastName;

	aRecList = (DBRecordList *)[controller getRecordList];
	aPropList = (List *)[controller getPropertyList];
	
	/* get the ssn or author id */
 
 	inputString = (const char *)[ssnField stringValue];
	
		/* If the string is empty, abort the operation */
	if  ( !strcmp(inputString,"")){
		NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL);
		return nil;
 	}
	else {	
		[aValue setStringValue:inputString];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:0] at:index];
	}
		
		
		/* set last name */		
	lastName = (const char *)[lnameField stringValue];
	if  ( !strcmp(lastName,"")){
		NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL);
		return nil;
 	} 
	else {
		[aValue setStringValue:lastName];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:1] at:index];			 
	}
		
		
		/* set first name */
	inputString = (const char *)[fnameField stringValue];
	if  ( !strcmp(inputString,"")){
		NXRunAlertPanel (NULL, EMPTY_STRING, NULL, NULL, NULL);
		return nil;
 	}
	else {
		[aValue setStringValue:(const char *)inputString];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:2] at:index];
		sprintf( cellName, "%s %s", lastName, inputString);
	}
		
			/* set phone number */
	inputString = (const char *)[phoneField stringValue];
	if  ( !strcmp(inputString,"")){
		NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL);
		return nil;
 	}
	else { 	 
		[aValue setStringValue: inputString];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:3] at:index];
	}
	
			/* set address */
	inputString = (const char *)[addressField stringValue];
	if  ( !strcmp(inputString,"")){
		NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL);
		return nil;
 	}
	else {
		[aValue setStringValue: inputString];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:4] at:index];
	}

			/* set city name */
	inputString = (const char *)[cityField stringValue];
	if  ( !strcmp(inputString,"")){
		NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL);
		return nil;
 	} 
	else {
		[aValue setStringValue: inputString];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:5] at:index];
	}

			/* set state */
	inputString = (const char *)[stateField stringValue];
	if  ( !strcmp(inputString,"")){
		NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL);
		return nil;
 	}
	else { 	 
		[aValue setStringValue: inputString];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:6] at:index];
	}

			/* set zip code */
	inputString = (const char *)[zipField stringValue];
	if  ( !strcmp(inputString,"")){
		NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL);
		return nil;
 	} 
	else {
		[aValue setStringValue: inputString];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:7] at:index]; 
	}


		/* assign an arbitrary contract number 1 to the new record */
	[aRecList getValue:aValue forProperty:[aPropList objectAt:8] at:index];
	
	if ( ![aValue intValue]) {
		[aValue setIntValue:1];
		[aRecList setValue:aValue forProperty:[aPropList objectAt:8] at:index];
	}

	return self;
}



- deleteSelectedRecord:sender
{
	int row;
	DBRecordList * recordList;

	row = [cellMatrix selectedRow];
	recordList = (DBRecordList *)[controller getRecordList];
	[recordList deleteRecordAt:row];
	if ([recordList saveModifications] == 0)
		return self;
	else
		return nil;
}

@end
