/*
   Project: UL

   Copyright (C) 2005 Michael Johnston & Jordi Villa-Freixa

   Author: Michael Johnston

   Created: 2005-07-08 16:34:11 +0200 by michael johnston

   This application is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This application is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#include "AdunKernel/AdunModelObject.h"

@implementation AdModelObject

- (id) init
{
	id generalDataKeys, generalDataValues;

	if(self =  [super init])
	{
		date = [NSDate date];
		[date retain];
		dateFormatter = [[NSDateFormatter alloc] initWithDateFormat: @"%H:%M %d/%m"
				allowNaturalLanguage: NO];

		generaldata = [NSMutableDictionary dictionary];
		[generaldata setObject: [dateFormatter stringForObjectValue: date]
				forKey: @"Created"];
		[generaldata setObject: NSFullUserName() forKey: @"Creator"];
		[generaldata setObject: @"None" forKey: @"Database"];

		metadata = [NSMutableDictionary dictionary];
		[metadata setObject: @"None" forKey: @"Name"];
		[metadata setObject: @"None" forKey: @"Keywords"];

		dataDict = [NSMutableDictionary dictionary];
		[dataDict setObject: generaldata forKey: @"General Data"];
		[dataDict setObject: metadata forKey: @"Metadata"];
		[dataDict retain];

		volatileMetadata = [NSMutableDictionary new];
		//generate a unique id for this object

		identification = [NSString stringWithFormat: @"%@",
					[[NSProcessInfo processInfo] globallyUniqueString]];
		[identification retain];
		inputReferences = [NSMutableDictionary new];
		outputReferences = [NSMutableDictionary new];
	}	

	return self;
}

- (void) dealloc
{
	[volatileMetadata release];
	[identification release];
	[dataDict release];
	[date release];
	[dateFormatter release];
	[inputReferences release];
	[outputReferences release];

	[super dealloc];
}

- (void) updateMetadata: (NSDictionary*) values
{
	NSEnumerator* keyEnum;
	id key, value;

	keyEnum = [metadata keyEnumerator];
	while(key = [keyEnum nextObject])
		if((value = [values objectForKey: key]) != nil)
			[metadata setObject: value forKey: key];
}

- (NSMutableDictionary*) metadata
{
	return metadata;
}

- (NSMutableDictionary*) dataDictionary
{
	return dataDict;	
}

- (NSMutableDictionary*) allData
{
	id allData;

	allData = [NSMutableDictionary dictionary];
	[allData addEntriesFromDictionary: metadata];
	[allData addEntriesFromDictionary: generaldata];
	return allData;
}

- (id) identification
{
	return identification;
}

- (void) setIdentification: (NSString*) value
{
	[identification release];
	identification = value;
	[identification retain];
}

- (id) name
{
	return [metadata objectForKey: @"Name"];
}

- (id) creator
{
	return [generaldata objectForKey: @"Creator"];
}

- (id) created
{
	return [generaldata objectForKey: @"Created"];
}

- (NSDate*) creationDate
{
	return date;
}

- (id) keywords
{
	return [metadata objectForKey: @"Keywords"];
}

- (NSString*) database
{
	return [generaldata objectForKey: @"Database"];
}

- (NSString*) schema
{
	return [generaldata objectForKey: @"Database"];
}

- (NSString*) valueForMetadataKey: (NSString*) aString
{
	return [metadata objectForKey: aString];
}

- (void) setValue: (id) value forMetadataKey: (NSString*) aString
{
	return [metadata setObject: value forKey: aString];
}

- (void) removeMetadataKey: (NSString*) aString
{
	[metadata removeObjectForKey: aString];
}

- (NSString*) valueForVolatileMetadataKey: (NSString*) aString
{
	return [volatileMetadata objectForKey: aString];
}

- (void) setValue: (id) value forVolatileMetadataKey: (NSString*) aString;
{
	return [volatileMetadata setObject: value forKey: aString];
}

- (void) removeVolatileMetadataKey: (NSString*) aString;
{
	[volatileMetadata removeObjectForKey: aString];
}

- (NSDictionary*) volatileMetadata
{
	return [[volatileMetadata copy] autorelease];
}

- (NSArray*) inputReferencesToObjectsOfClass: (NSString*) className
{
	return [[inputReferences objectForKey: className] allObjects];
}

//FIXME: Trying to find the best way of returning the input 
//refs. Better as an array for use with an outline view, but
//we also need to store them by class etc.
- (NSArray*) inputReferences
{
	NSMutableArray* array = [NSMutableArray array];
	NSEnumerator* typeEnum = [inputReferences objectEnumerator];
	id types;

	while(types = [typeEnum nextObject])
		[array addObjectsFromArray: [types allValues]];

	return array;	
}

- (void) addInputReferenceToObject: (id) obj
{
	if([obj respondsToSelector: @selector(identification)])
	{
		[self addInputReferenceToObjectWithID: [obj identification]
			name: [obj name]
			ofType: NSStringFromClass([obj class])
			inSchema: [obj schema]
			ofDatabase: [obj database]];
	}
	else
		[NSException raise: NSInvalidArgumentException
			format: @"Object (%@) does not respond to identification.", 
			[obj description]];

}

- (void) addInputReferenceToObjectWithID: (NSString*) ident 
		name: (NSString*) objectName
		ofType: (NSString*) type 
		inSchema: (NSString*) schema
		ofDatabase: (NSString*) databaseName
{
	id refs;
	NSDictionary* dict;
	
	if(ident != nil && type != nil)
	{
		dict = [NSDictionary dictionaryWithObjectsAndKeys:
			ident, @"Identification",
			objectName, @"Name",
			type, @"Class",
			schema, @"Schema",
			databaseName, @"Database", nil];
		refs = [inputReferences objectForKey: type];
		if(refs == nil)
		{
			refs = [NSMutableDictionary dictionary];
			[inputReferences setObject: refs forKey: type];
		}
		[refs setObject: dict forKey: ident];
	}	
}

- (void) removeInputReferenceToObject: (id) obj
{
	if([obj respondsToSelector: @selector(identification)])
	{
		[self removeInputReferenceToObjectWithID: [obj identification]
			ofType: NSStringFromClass([obj class])];
	}
	else
		[NSException raise: NSInvalidArgumentException
			format: @"Object (%@) does not respond to identification.", 
			[obj description]];
}

- (void) removeInputReferenceToObjectWithID: (NSString*) ident 
		ofType: (NSString*) type 
{
	id refs;
	
	if(ident != nil && type != nil)
	{
		refs = [inputReferences objectForKey: type];
		if(refs != nil)
			[refs removeObjectForKey: ident];
	}	
}

- (NSArray*) outputReferencesToObjectsOfClass: (NSString*) className
{
	return [[outputReferences objectForKey: className] allObjects];
}

- (NSArray*) outputReferences
{
	NSMutableArray* array = [NSMutableArray array];
	NSEnumerator* typeEnum = [outputReferences objectEnumerator];
	id types;

	while(types = [typeEnum nextObject])
		[array addObjectsFromArray: [types allValues]];

	return array;	
}

- (void) addOutputReferenceToObject: (id) obj
{
	if([obj respondsToSelector: @selector(identification)])
	{
		[self addOutputReferenceToObjectWithID: [obj identification]
			name: [obj name]
			ofType: NSStringFromClass([obj class])
			inSchema: [obj schema]
			ofDatabase: [obj database]];
	}
	else
		[NSException raise: NSInvalidArgumentException
			format: @"Object (%@) does not respond to identification.", 
			[obj description]];

}

- (void) addOutputReferenceToObjectWithID: (NSString*) ident
		name: (NSString*) objectName
		ofType: (NSString*) type 
		inSchema: (NSString*) schema
		ofDatabase: (NSString*) databaseName
{
	id refs;
	NSDictionary* dict;
	
	if(ident != nil && type != nil)
	{
		dict = [NSDictionary dictionaryWithObjectsAndKeys:
			ident, @"Identification",
			objectName, @"Name",
			type, @"Class",
			schema, @"Schema",
			databaseName, @"Database", 
			nil];
		refs = [outputReferences objectForKey: type];
		if(refs == nil)
		{
			refs = [NSMutableDictionary dictionary];
			[outputReferences setObject: refs forKey: type];
		}
		[refs setObject: dict forKey: ident];
	}	

}

- (void) removeOutputReferenceToObject: (id) obj
{
	if([obj respondsToSelector: @selector(identification)])
	{
		[self removeOutputReferenceToObjectWithID: [obj identification]
			ofType: NSStringFromClass([obj class])];
	}
	else
		[NSException raise: NSInvalidArgumentException
			format: @"Object (%@) does not respond to identification.", 
			[obj description]];
}

- (void) removeOutputReferenceToObjectWithID: (NSString*) ident ofType: (NSString*) type 
{
	NSEnumerator* refsEnum;
	id refs, ref;
	
	if(ident != nil && type != nil)
	{
		refs = [outputReferences objectForKey: type];
		if(refs != nil)
			[refs removeObjectForKey: ident];
	}	
	else if(ident != nil)
	{
		//We have to find where the reference is stored
		//i.e. what type of object does ident refer to.
		refsEnum = [outputReferences objectEnumerator];
		while(refs = [refsEnum nextObject])
			if((ref = [refs objectForKey: ident]) != nil)
				type = [ref objectForKey: @"Class"];

		if(type != nil)
		{
			refs = [outputReferences objectForKey: type];
			[refs removeObjectForKey: ident];
		}	
	}
}

- (void) removeOutputReferenceToObjectWithID: (NSString*) ident
{
	if(ident != nil)
		[self removeOutputReferenceToObjectWithID: ident
			ofType: nil];
}

/*******
NSCoding Methods
********/

- (id) initWithCoder: (NSCoder*) decoder
{
	if([decoder allowsKeyedCoding])
	{
		dataDict = [[decoder decodeObjectForKey: @"DataDictionary"] retain];
		identification = [[decoder decodeObjectForKey: @"Identification"] retain];
		inputReferences = [[decoder decodeObjectForKey: @"inputReferences"] retain];
		outputReferences = [[decoder decodeObjectForKey: @"outputReferences"] retain];
		metadata = [dataDict objectForKey: @"Metadata"];
		generaldata = [dataDict objectForKey: @"General Data"];

	}
	else
	{
		dataDict = [[decoder decodeObject] retain];
		identification = [[decoder decodeObject] retain];
		inputReferences = [[decoder decodeObject] retain];
		outputReferences = [[decoder decodeObject] retain];
		metadata = [dataDict objectForKey: @"Metadata"];
		generaldata = [dataDict objectForKey: @"General Data"];
	}

	volatileMetadata = [NSMutableDictionary new];
	
	return self;
}

- (void) encodeWithCoder: (NSCoder*) encoder
{
	if([encoder allowsKeyedCoding])
	{
		[encoder encodeObject: dataDict forKey: @"DataDictionary"];
		[encoder encodeObject: identification forKey: @"Identification"];
		[encoder encodeObject: inputReferences forKey: @"inputReferences"];
		[encoder encodeObject: outputReferences forKey: @"outputReferences"];
	}
	else
	{
		[encoder encodeObject: dataDict];
		[encoder encodeObject: identification];
		[encoder encodeObject: inputReferences];
		[encoder encodeObject: outputReferences];
	}
}

@end
