/*--------------------------------------------------------------------------
 *
 * 	You may freely copy, distribute, and reuse the code in this example.
 * 	SHL Systemhouse disclaims any warranty of any kind, expressed or
 *	implied, as to its fitness for any particular use.
 *
 *
 *	EOModelInspector
 *
 *	Inherits From:		WMInspector
 *
 *	Conforms To:		none
 *
 *	Declared In:		EOModelInspector.h
 *
 *------------------------------------------------------------------------*/

#import "EOModelInspector.h"
#import "EOModelBrowserCell.h"
#import "SwapView.h"
#import "EntityPropertiesCategory.h"



/*--------------------------------------------------------------------------
 *	Some basic macros for the cells of our matrices
 *------------------------------------------------------------------------*/
#define ADAPTOR					[connectionDictionaryMatrix cellAt:0 :0]
#define DB_AND_HOST				[connectionDictionaryMatrix cellAt:1 :0]
#define USER_AND_PASSWORD		[connectionDictionaryMatrix cellAt:2 :0]

#define VALUE_CLASS_AND_TYPE	[attributeSettingsMatrix cellAt:0 :0]
#define EXTERN_TYPE				[attributeSettingsMatrix cellAt:1 :0]
#define ATTRIBUTE_EXTERN_NAME	[attributeSettingsMatrix cellAt:2 :0]

#define CLASS					[entitySettingsMatrix cellAt:0 :0]
#define ENTITY_EXTERN_NAME		[entitySettingsMatrix cellAt:1 :0]


static id
	_SELF = nil;


@implementation EOModelInspector

+ new
{
	char
		path[MAXPATHLEN+1];
	id
		bundle;

	if (_SELF)
		{
		return _SELF;
		}

	_SELF = self = [super new];

	if (!(bundle = [NXBundle bundleForClass:[self class]]))
		{
		return nil;
		}
	
	if (![bundle getPath:path forResource:[(Object *)[self class] name] ofType:"nib"]) 
		{
		return nil;
		}
	
	[NXApp loadNibFile:path owner:self withNames:NO fromZone:[(Object *)self zone]];

	/*
	 * Let's load in a bunch of images to be used in our inspector...
	 */
	
	if ([bundle getPath:path forResource:"SymbolKey" ofType:"tiff"])
		{
		keyImage = [[NXImage alloc] initFromFile:path];
		}
	else
		{
		NSLog (@"no key image: %s", path);
		}

	if ([bundle getPath:path forResource:"SymbolClass" ofType:"tiff"])
		{
		classImage = [[NXImage alloc] initFromFile:path];
		}
	else
		{
		NSLog (@"no class image: %s", path);
		}

	if ([bundle getPath:path forResource:"SymbolLock" ofType:"tiff"])
		{
		lockImage = [[NXImage alloc] initFromFile:path];
		}
	else
		{
		NSLog (@"no lock image: %s", path);
		}
	
	/*
	 * Let's setup the splitview
	 */
	[splitView addSubview:connectionDictionaryView];
	[splitView addSubview:browser];
	[splitView addSubview:propertySwapView];
	 
	/*
	 * Use our custom browser cell
	 */
	[browser setCellClass:[EOModelBrowserCell class]];
	
	/*
	 * We'll determine the column titles, thank you very much.
	 */
	[browser getTitleFromPreviousColumn:NO];
 	return _SELF;
}


- _entityForCurrentSelectionInColumn: (int) theColumn
/*--------------------------------------------------------------------------
 *	Walks the browser from theColumn and finds/returns the entity that
 *	corresponds to the current selection.  Let's say I have a relationship
 *	selected...I need to find out it's destination entity.
 *
 *	NOTE: The reason this seems to be necessary is that when we go to
 *	fill in a matrix column (after a relationship selection), all we
 *	know is that we've got a selected cell, it has a name and a tag.
 *
 *	More explanation of what we're doing here...
 *------------------------------------------------------------------------*/
{
	id
		/*
		 * First let's get the "root" entity...that is the entity selected
		 * the first column.
		 */
		myEntity = [[model entities] objectAtIndex:[[[browser matrixInColumn:0] selectedCell] tag]];
	int
		x;
	
	/*
	 * Now, let's walk from column 1, until we get to the
	 * column that's selected...
	 */
	for (x = 1; x < theColumn; x++)
		{
		id
			myProperty;

		/*
		 * Get the property
		 */
		myProperty = [[myEntity properties] objectAtIndex:[[[browser matrixInColumn:x] selectedCell] tag]];
		
		if ([myProperty isKindOfClass:[EOAttribute class]])
			{
			myEntity = [myProperty entity];
			}
		else
			{
			myEntity = [myProperty destinationEntity];
			}
		}
	
	return myEntity;
}


- (int) _fillInBrowserMatrix: theMatrix
	withEntities: (NSArray *) theEntities
/*--------------------------------------------------------------------------
 *	Called by our browser delegate method, to fill in the given matrix
 *	with "theEntities".  This is only called for the first column.
 *------------------------------------------------------------------------*/
{
	int
		x = 0;
	id
		myCell,
		myEntity,
		myString;
	
	while (x < [theEntities count]) 
		{
		myEntity = [theEntities objectAtIndex:x];
		[theMatrix addRow];
		myCell = [theMatrix cellAt:x :0];
		[myCell setTag:x];
		myString = [NSString stringWithFormat:@"%@", [(EOEntity *)myEntity name]];
		[myCell setStringValue:[myString cString]];
		
		if ([myEntity isReadOnly])
			{
			[myCell setTextColor:NX_COLORDKGRAY];
			}
		
		[myCell setLeaf:NO];
		[myCell setLoaded:YES];
		x++;
		}
	
	return x;
}


- (int) _fillInBrowserMatrix: theMatrix
	withPropertiesFromEntity: (EOEntity *) theEntity
/*--------------------------------------------------------------------------
 *	Called by our browser delegate method, to fill in the given matrix
 *	with the properties (attributes & relationships) for "theEntity".
 *	This is called many times, particularly for a selected relationship
 *	that must display the properties for its destination entity.
 *------------------------------------------------------------------------*/
{
	int
		x = 0;
	id
		myCell,
		myProperties = [theEntity properties],
		myProperty,
		myString;
	
	while (x < [myProperties count]) 
		{
		myProperty = [myProperties objectAtIndex:x];
		[theMatrix addRow];
		myCell = [theMatrix cellAt:x :0];
		[myCell setTag:x];
		
		if ([myProperty isKindOfClass:[EOAttribute class]])
			{
			myString = [NSString stringWithFormat:@"%@", [(EOAttribute *)myProperty name]];
			
			if ([myProperty isReadOnly])
				{
				[myCell setTextColor:NX_COLORDKGRAY];
				}
			
			[myCell setLeaf:YES];
			}
		else
			{
			myString = [NSString stringWithFormat:@"%@", [(EORelationship *)myProperty name]];
			[myCell setIsRelationship:YES];
			[myCell setIsToManyRelationship:[myProperty isToMany]];
			[myCell setLeaf:NO];
			}

		[myCell setStringValue:[myString cString]];
		[myCell setLoaded:YES];
		x++;
		}

	return x;
}


- revert: sender
{
	char
		myPath[MAXPATHLEN+1];
	
	/*
	 * Let's get the path of the selected item
	 */
	[self selectionPathsInto:myPath separator:'\0'];
	
	/*
	 * Let's get rid of the old model
	 */
	if (model)
		{
		[model release];
		model = nil;
		}
		
	/*
	 * Now create a model from our (.eomodel) file
	 */
	if (model = [[EOModel alloc] initWithContentsOfFile:[NSString stringWithCString:myPath]])
		{
		id
			myConnectionDictionary,
			myString,
			myStringA,
			myStringB,
			myDatabaseKey,
			myHostKey,
			myPasswordKey,
			myUserNameKey,
			myStringTable;
		char
			myPath[MAXPATHLEN+1];
		
		/*
		 * Let's display the viewer
		 */
		[swapView swapView:[viewerPanel contentView]];
		
		/*
		 * Let's set the strings for the main model attributes
		 *
		 * 		adaptor name, database/server name, host name/machine, user name & password
		 */
		
		/*
		 * First the adaptor name
		 */
		[ADAPTOR setStringValue:[[model adaptorName] cString]];
		
		/*
		 * Now we need the "connectionDictionary" to get the rest
		 */
		myConnectionDictionary = [model connectionDictionary];
	
		/*
		 * database/server name, host name/machine, get 'em then set 'em...
		 *
		 * Like: database/server_name (host_name/machine)
		 *
		 * NOTE:	This is tricky, since the Sybase and Oracle adaptors use
		 *			slightly different keys (ARRRGGGHHH!).
		 *
		 *			Sybase uses: databaseName & hostName
		 *			Oracle uses: serverId & hostMachine
		 *
		 * Okay...ready?  Here's what we're gonna do.  Assuming that these two
		 * keys could be different for any adaptor (since NeXT was too stupid
		 * to force 'em all to be the same), we'll simply gave a (string) table
		 * for each kind of adaptor.  This table will contain the keys:
		 * "database" and "host" which will map to that specific adaptor's key
		 * name.  Then we'll use that value as a key to our dictionary to get
		 * the real value that we want.  Whew...follow?
		 */
		
		/*
		 * Let's use Sybase's key names as a default...
		 */
		myDatabaseKey = [NSString stringWithCString:"databaseName"];
		myHostKey = [NSString stringWithCString:"hostName"];
		myPasswordKey = [NSString stringWithCString:"password"];
		myUserNameKey = [NSString stringWithCString:"userName"];
		
		/*
		 * Now, let's get a ".strings" file of the same name as the adaptor
		 * (according to the model)...
		 */
		[[NXBundle bundleForClass:[self class]] getPath:myPath
				forResource:[[model adaptorName] cString]
												ofType:"strings"];
		
		/*
		 * If there's such a file...
		 */
		if (*myPath)
			{
			/*
			 * make a string table
			 */
			myStringTable = [[NXStringTable alloc] init];
			
			/*
			 * read from the file
			 */
			if ([myStringTable readFromFile:myPath])
				{
				/*
				 * get the values for our standard keys: "database" and "host"
				 */
				myDatabaseKey = [NSString stringWithCString:[myStringTable valueForStringKey:"database"]];
				myHostKey = [NSString stringWithCString:[myStringTable valueForStringKey:"host"]];
				myPasswordKey = [NSString stringWithCString:[myStringTable valueForStringKey:"password"]];
				myUserNameKey = [NSString stringWithCString:[myStringTable valueForStringKey:"username"]];
				}
			
			[myStringTable free];
			}
		
		/*
		 * Now we get a couple of strings based on our keys.
		 * remember, is there was no file for this adaptor, we used defaults
		 * which are Sybase (for now).
		 */
		myStringA = [myConnectionDictionary objectForKey:myDatabaseKey];
		myStringB = [myConnectionDictionary objectForKey:myHostKey];
		myString = [NSString stringWithFormat:@"%@ (%@)", myStringA, myStringB];
		[DB_AND_HOST setStringValue:[myString cString]];
		
		/*
		 * user name & password, get 'em then set 'em...
		 *
		 * Like: user_name (password)
		 */
		myStringA = [myConnectionDictionary objectForKey:myUserNameKey];
		myStringB = [myConnectionDictionary objectForKey:myPasswordKey];
		myString = [NSString stringWithFormat:@"%@ (%@)", myStringA, myStringB];
		[USER_AND_PASSWORD setStringValue:[myString cString]];
		
		/*
		 * Let's tell the browser to fill up, and force a click...
		 */
		[browser loadColumnZero];
		[self itemSelect:nil];
		}
	else
		{
		/*
		 * If there was an error reading the file, let's display the error view
		 */
		[swapView swapView:[errorPanel contentView]];
		}
		
	[super revert:sender];
	return self;
}


- itemSelect: sender
{
	if ([sender selectedColumn] > 0)
		{
		id
			myEntity = [self _entityForCurrentSelectionInColumn:[sender selectedColumn]],
			myProperty = [[myEntity properties] objectAtIndex:[[sender selectedCell] tag]];
			
		if ([myProperty isKindOfClass:[EOAttribute class]])
			{
			/*
			 * Get the proper attribute (the for this column's selected cell).
			 */
			id
				myString;
			
			/*
			 * Swap in the attribute view
			 */
			[propertySwapView swapView:[attributePanel contentView]];
			[propertySwapView display];
			
			if ([[[myProperty entity] primaryKeyAttributes] containsObject:myProperty])
				{
				[attributeKeyButton setImage:[keyImage copy]];
				}
			else
				{
				[attributeKeyButton setImage:nil];
				}
			
			if ([[[myProperty entity] attributesUsedForLocking] containsObject:myProperty])
				{
				[attributeLockingButton setImage:[lockImage copy]];
				}
			else
				{
				[attributeLockingButton setImage:nil];
				}

			if ([[[myProperty entity] classProperties] containsObject:myProperty])
				{
				[attributeClassButton setImage:[classImage copy]];
				}
			else
				{
				[attributeClassButton setImage:nil];
				}
			
			/*
			 * Set the "value classname" & "value type" string
			 *
			 * We don't want anything in parenthesis is there is no "value
			 * type" (which is most cases).  But, if I don't do the "if" here,
			 * I get a string like:
			 *
			 *		NSString (nil)
			 *
			 * Which isn't what I want.
			 */
			myString = [NSString stringWithFormat:@"%s", [myProperty valueClassName]];
			
			if ([myProperty valueType])
				{
				myString = [myString stringByAppendingFormat:@" (%@)", [myProperty valueType]];
				}
			
			[VALUE_CLASS_AND_TYPE setStringValue:[myString cString]];
			
			/*
			 * Set the "external type" string
			 */
			[EXTERN_TYPE setStringValue:[[myProperty externalType] cString]];
			
			if ([myProperty isDerived] || [myProperty isFlattened])
				{
				[ATTRIBUTE_EXTERN_NAME setStringValue:[(NSString *)[myProperty definition] cString]];
				}
			else
				{
				[ATTRIBUTE_EXTERN_NAME setStringValue:[(NSString *)[myProperty columnName] cString]];
				}
			}
		else
			{
			/*
			 * Swap in the relationship view
			 */
			[propertySwapView swapView:[relationshipPanel contentView]];
			[propertySwapView display];
			
			/*
			 * Set our radio button to reflect the "manyness" of the
			 * relationship.
			 */
			[relationshipManynessMatrix selectCellAt:0 :([myProperty isToMany] ? 1 : 0)];
			}
		}
	else
		{
		/*
		 * If there is no selected column, or it is the entities column,
		 * there ARE NO attribute settings.
		 */
		id
			myEntity = [self _entityForCurrentSelectionInColumn:[sender selectedColumn]];
		
		[propertySwapView swapView:[entityPanel contentView]];
		[propertySwapView display];
		
		[CLASS setStringValue:[myEntity className]];
		[ENTITY_EXTERN_NAME setStringValue:[(NSString *)[myEntity externalName] cString]];
		}
	
	return self;
}



/*--------------------------------------------------------------------------
 *	NXBrowser delegate methods
 *------------------------------------------------------------------------*/

- (int) browser: sender
	fillMatrix: matrix
	inColumn: (int) column
{
	int
		x = 0;
	
	if (column == 0)
		{
		/*
		 * If we're the "root" column...
		 */
		x = [self _fillInBrowserMatrix:matrix
					withEntities:[model entities]];
		}
	else
		{
		/*
		 * Otherwise...
		 */
		x = [self _fillInBrowserMatrix:matrix
					withPropertiesFromEntity:[self _entityForCurrentSelectionInColumn:column]];
		}

	return x;
}


- (const char *) browser: sender
	titleOfColumn: (int) column
{
	NSString
		*columnTitle;
	
	if (column == 0)
		{
		columnTitle = [NSString stringWithCString:"Entities"];
		}
	else
		{
		columnTitle = [(EOEntity *)[self _entityForCurrentSelectionInColumn:column] name];
		}
	
	return [columnTitle cString];
}


/*--------------------------------------------------------------------------
 *	NXSplitView delegate methods
 *------------------------------------------------------------------------*/

- splitView:sender
	getMinY: (NXCoord *) minY
	maxY: (NXCoord *) maxY
	ofSubviewAt: (int) offset
{
	switch (offset)
		{
		case 0:
			*minY = 0.0;
			*maxY = 45.0;
			break;
		
		case 1:
			*minY = 155.0;
			*maxY = 225.0;
			break;
		}
	
	return self;
}


@end
