#import "Resource.h"
#import "ResourceDocument.h"
#import "ResourceDataSource.h"

NSString *RKResourcePboardType = @"RKResourcePboardType";

@implementation Resource

- (id)init
{
	self = [super init];
	[self initWithType:@"NULL" andID:[NSNumber numberWithShort:128]];
	return self;
}

- (id)initWithType:(NSString *)typeValue andID:(NSNumber *)resIDValue
{
	[self initWithType:typeValue andID:resIDValue withName:@"" andAttributes:[NSNumber numberWithUnsignedShort:0]];
	return self;
}

- (id)initWithType:(NSString *)typeValue andID:(NSNumber *)resIDValue withName:(NSString *)nameValue andAttributes:(NSNumber *)attributesValue
{
	[self initWithType:typeValue andID:resIDValue withName:nameValue andAttributes:attributesValue data:[NSData data]];
	return self;
}

- (id)initWithType:(NSString *)typeValue andID:(NSNumber *)resIDValue withName:(NSString *)nameValue andAttributes:(NSNumber *)attributesValue data:(NSData *)dataValue
{
	// sets values directly for speed reasons (less messaging overhead)
	self = [super init];
	dirty = NO;
	representedFork = nil;
	name = [nameValue copy];
	type = [typeValue copy];
	resID = [resIDValue copy];
	attributes = [attributesValue copy];
	data = [dataValue retain];
	return self;
}


+ (id)resourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue
{
	Resource *resource = [[Resource allocWithZone:[self zone]] initWithType:typeValue andID:resIDValue];
	return [resource autorelease];
}

+ (id)resourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue withName:(NSString *)nameValue andAttributes:(NSNumber *)attributesValue
{
	Resource *resource = [[Resource allocWithZone:[self zone]] initWithType:typeValue andID:resIDValue withName:nameValue andAttributes:attributesValue];
	return [resource autorelease];
}

+ (id)resourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue withName:(NSString *)nameValue andAttributes:(NSNumber *)attributesValue data:(NSData *)dataValue
{
	Resource *resource = [[Resource allocWithZone:[self zone]] initWithType:typeValue andID:resIDValue withName:nameValue andAttributes:attributesValue data:dataValue];
	return [resource autorelease];
}

+ (Resource *)getResourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue inDocument:(NSDocument *)searchDoc
{
	NSDocument *doc;
	NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator];
	while(doc = [enumerator nextObject])
	{
		if(searchDoc == nil || searchDoc == doc)
		{
			// parse document for correct resource
			Resource *resource = [[(ResourceDocument *)doc dataSource] resourceOfType:typeValue andID:resIDValue];
			if(resource) return resource;
		}
	}
	return nil;
}

/* ResKnifeResourceProtocol implementation */

+ (NSArray *)allResourcesOfType:(NSString *)typeValue inDocument:(NSDocument *)searchDoc
{
	NSMutableArray *array = [NSMutableArray array];
	NSDocument *doc;
	NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator];
	while(doc = [enumerator nextObject])
	{
		// parse document for resources
		if(searchDoc == nil || searchDoc == doc)
			[array addObjectsFromArray:[[(ResourceDocument *)doc dataSource] allResourcesOfType:typeValue]];
	}
	return [NSArray arrayWithArray:array];
}

+ (id)resourceOfType:(NSString *)typeValue withName:(NSString *)nameValue inDocument:(NSDocument *)searchDoc
{
	NSDocument *doc;
	NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator];
	while(doc = [enumerator nextObject])
	{
		if(searchDoc == nil || searchDoc == doc)
		{
			// parse document for correct resource
			Resource *resource = [[(ResourceDocument *)doc dataSource] resourceOfType:typeValue withName:nameValue];
			if(resource) return resource;
		}
	}
	return nil;
}

+ (id)resourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue inDocument:(NSDocument *)searchDoc
{
	NSDocument *doc;
	NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator];
	while(doc = [enumerator nextObject])
	{
		if(searchDoc == nil || searchDoc == doc)
		{
			// parse document for correct resource
			Resource *resource = [[(ResourceDocument *)doc dataSource] resourceOfType:typeValue andID:resIDValue];
			if(resource) return resource;
		}
	}
	return nil;
}

// should probably be in resource document, not resource, but it fits in with the above methods quite well
+ (NSDocument *)documentForResource:(Resource *)resource
{
	NSDocument *doc;
	NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator];
	while(doc = [enumerator nextObject])
	{
		Resource *res;
		NSEnumerator *enumerator2 = [[(ResourceDocument *)doc resources] objectEnumerator];
		while(res = [enumerator2 nextObject])
		{
			if([res isEqual:resource])
				return doc;
		}
	}
	return nil;
}

- (void)dealloc
{
	[representedFork release];
	[name release];
	[type release];
	[resID release];
	[attributes release];
	[data release];
	[_docName release];
	[super dealloc];
}

- (id)copyWithZone:(NSZone *)zone
{
	Resource *copy = [[Resource alloc] initWithType:type andID:resID withName:name andAttributes:attributes data:[data copy]];
	[copy setDocumentName:_docName];
	return copy;
}

/* accessors */

- (void)touch
{
	[self setDirty:YES];
}

- (BOOL)isDirty
{
	return dirty;
}

- (void)setDirty:(BOOL)newValue
{
	dirty = newValue;
	[[NSNotificationCenter defaultCenter] postNotificationName:ResourceDidChangeNotification object:self];
}

- (NSDocument *)document
{
	return [Resource documentForResource:self];
}

- (void)setDocumentName:(NSString *)docName
{
	_docName = [docName copy];
}

- (NSString *)representedFork
{
	return representedFork;
}

- (void)setRepresentedFork:(NSString *)forkName
{
	representedFork = [forkName copy];
}

- (NSString *)name
{
	return name;
}

// shouldn't need this - it's used by forks to give them alternate names - should use name formatter replacement instead
- (void)_setName:(NSString *)newName
{
	id old = name;
	name = [newName copy];
	[old release];
}

- (void)setName:(NSString *)newName
{
	if(![name isEqualToString:newName])
	{
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self];
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceNameWillChangeNotification object:self];
		
		id old = name;
		name = [newName copy];
		[old release];
		
		// bug: this line is causing crashes!
//		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceNameDidChangeNotification object:self];
		[self setDirty:YES];
	}
}

- (NSString *)defaultWindowTitle
{
	if([name length] > 0)	return [NSString stringWithFormat: NSLocalizedString(@"%@: %@ %@ '%@'", @"default window title format with resource name"), _docName, type, resID, name];
	else					return [NSString stringWithFormat: NSLocalizedString(@"%@: %@ %@", @"default window title format without resource name"), _docName, type, resID];
}

- (NSString *)type
{
	return type;
}

- (void)setType:(NSString *)newType
{
	if(![type isEqualToString:newType])
	{
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self];
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceTypeWillChangeNotification object:self];
		
		id old = type;
		type = [newType copy];
		[old release];
		
		// bug: this line is causing crashes!
//		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceTypeDidChangeNotification object:self];
		[self setDirty:YES];
	}
}

- (NSNumber *)resID
{
	return resID;
}

- (void)setResID:(NSNumber *)newResID
{
	if(![resID isEqualToNumber:newResID])
	{
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self];
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceIDWillChangeNotification object:self];
		
		id old = resID;
		resID = [newResID copy];
		[old release];
		
		// bug: this line is causing crashes!
//		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceIDDidChangeNotification object:self];
		[self setDirty:YES];
	}
}

- (NSNumber *)attributes
{
	return attributes;
}

- (void)setAttributes:(NSNumber *)newAttributes
{
	if(![attributes isEqualToNumber:newAttributes])
	{
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self];
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceAttributesWillChangeNotification object:self];
		
		id old = attributes;
		attributes = [newAttributes copy];
		[old release];
		
		// bug: this line is causing crashes!
//		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceAttributesDidChangeNotification object:self];
		[self setDirty:YES];
	}
}

- (NSNumber *)size
{
	return [NSNumber numberWithUnsignedLong:[data length]];
}

- (NSData *)data
{
	return data;
}

- (void)setData:(NSData *)newData
{
	if(![data isEqualToData:newData])
	{
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self];
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceDataWillChangeNotification object:self];
		
		// note: this function retains, rather than copies, the supplied data
		id old = data;
		data = [newData retain];
		[old release];
		
		[[NSNotificationCenter defaultCenter] postNotificationName:ResourceDataDidChangeNotification object:self];
		[self setDirty:YES];
	}
}

/* encoding */

- (id)initWithCoder:(NSCoder *)decoder
{
	self = [super init];
	if(self)
	{
		dirty = YES;
		name = [[decoder decodeObject] retain];
		type = [[decoder decodeObject] retain];
		resID = [[decoder decodeObject] retain];
		attributes = [[decoder decodeObject] retain];
		data = [[decoder decodeDataObject] retain];
	}
	return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder
{
	[encoder encodeObject:name];
	[encoder encodeObject:type];
	[encoder encodeObject:resID];
	[encoder encodeObject:attributes];
	[encoder encodeDataObject:data];
}

/* description */

- (NSString *)description
{
	return [NSString stringWithFormat:@"\n%@\nName: %@\nType: %@  ID: %@\nSize: %d  Modified: %@", [super description], name, type, resID, [data length], dirty? @"YES":@"NO"];
}

@end
