/*
 * cocos2d for iPhone: http://www.cocos2d-iphone.org
 *
 * Copyright (c) 2009 Jason Booth
 *
 * Copyright (c) 2009 Robert J Payne
 *
 * Copyright (c) 2008-2010 Ricardo Quesada
 * Copyright (c) 2011 Zynga Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

/*
 * To create sprite frames and texture atlas, use this tool:
 * http://zwoptex.zwopple.com/
 */

#import "Platforms/CCNS.h"
#import "ccMacros.h"
#import "CCTextureCache.h"
#import "CCSpriteFrameCache.h"
#import "CCSpriteFrame.h"
#import "CCSprite.h"
#import "Support/CCFileUtils.h"


@implementation CCSpriteFrameCache

#pragma mark CCSpriteFrameCache - Alloc, Init & Dealloc

static CCSpriteFrameCache *sharedSpriteFrameCache_=nil;

+ (CCSpriteFrameCache *)sharedSpriteFrameCache
{
	if (!sharedSpriteFrameCache_)
		sharedSpriteFrameCache_ = [[CCSpriteFrameCache alloc] init];
		
	return sharedSpriteFrameCache_;
}

+(id)alloc
{
	NSAssert(sharedSpriteFrameCache_ == nil, @"Attempted to allocate a second instance of a singleton.");
	return [super alloc];
}

+(void)purgeSharedSpriteFrameCache
{
	[sharedSpriteFrameCache_ release];
	sharedSpriteFrameCache_ = nil;
}

-(id) init
{
	if( (self=[super init]) ) {
		spriteFrames_ = [[NSMutableDictionary alloc] initWithCapacity: 100];
		spriteFramesAliases_ = [[NSMutableDictionary alloc] initWithCapacity:10];
	}
	
	return self;
}

- (NSString*) description
{
	return [NSString stringWithFormat:@"<%@ = %08X | num of sprite frames =  %i>", [self class], self, [spriteFrames_ count]];
}

-(void) dealloc
{
	CCLOGINFO(@"cocos2d: deallocing %@", self);
	
	[spriteFrames_ release];
	[spriteFramesAliases_ release];
	[super dealloc];
}

#pragma mark CCSpriteFrameCache - loading sprite frames

-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture
{
	/*
	 Supported Zwoptex Formats:
	 ZWTCoordinatesFormatOptionXMLLegacy = 0, // Flash Version
	 ZWTCoordinatesFormatOptionXML1_0 = 1, // Desktop Version 0.0 - 0.4b
	 ZWTCoordinatesFormatOptionXML1_1 = 2, // Desktop Version 1.0.0 - 1.0.1
	 ZWTCoordinatesFormatOptionXML1_2 = 3, // Desktop Version 1.0.2+
	*/
	NSDictionary *metadataDict = [dictionary objectForKey:@"metadata"];
	NSDictionary *framesDict = [dictionary objectForKey:@"frames"];

	int format = 0;
	
	// get the format
	if(metadataDict != nil)
		format = [[metadataDict objectForKey:@"format"] intValue];
	
	// check the format
	NSAssert( format >= 0 && format <= 3, @"cocos2d: WARNING: format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:texture:");
	
	
	// add real frames
	for(NSString *frameDictKey in framesDict) {
		NSDictionary *frameDict = [framesDict objectForKey:frameDictKey];
		CCSpriteFrame *spriteFrame;
		if(format == 0) {
			float x = [[frameDict objectForKey:@"x"] floatValue];
			float y = [[frameDict objectForKey:@"y"] floatValue];
			float w = [[frameDict objectForKey:@"width"] floatValue];
			float h = [[frameDict objectForKey:@"height"] floatValue];
			float ox = [[frameDict objectForKey:@"offsetX"] floatValue];
			float oy = [[frameDict objectForKey:@"offsetY"] floatValue];
			int ow = [[frameDict objectForKey:@"originalWidth"] intValue];
			int oh = [[frameDict objectForKey:@"originalHeight"] intValue];
			// check ow/oh
			if(!ow || !oh)
				CCLOG(@"cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist");
			
			// abs ow/oh
			ow = abs(ow);
			oh = abs(oh);
			// create frame
			
			spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture
													rectInPixels:CGRectMake(x, y, w, h)
														 rotated:NO
														  offset:CGPointMake(ox, oy)
													originalSize:CGSizeMake(ow, oh)];
		} else if(format == 1 || format == 2) {
			CGRect frame = CCRectFromString([frameDict objectForKey:@"frame"]);
			BOOL rotated = NO;
			
			// rotation
			if(format == 2)
				rotated = [[frameDict objectForKey:@"rotated"] boolValue];
			
			CGPoint offset = CCPointFromString([frameDict objectForKey:@"offset"]);
			CGSize sourceSize = CCSizeFromString([frameDict objectForKey:@"sourceSize"]);
			
			// create frame
			spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture
													rectInPixels:frame
														 rotated:rotated
														  offset:offset
													originalSize:sourceSize];
		} else if(format == 3) {
			// get values
			CGSize spriteSize = CCSizeFromString([frameDict objectForKey:@"spriteSize"]);
			CGPoint spriteOffset = CCPointFromString([frameDict objectForKey:@"spriteOffset"]);
			CGSize spriteSourceSize = CCSizeFromString([frameDict objectForKey:@"spriteSourceSize"]);
			CGRect textureRect = CCRectFromString([frameDict objectForKey:@"textureRect"]);
			BOOL textureRotated = [[frameDict objectForKey:@"textureRotated"] boolValue];
			
			// get aliases
			NSArray *aliases = [frameDict objectForKey:@"aliases"];
			for(NSString *alias in aliases) {
				if( [spriteFramesAliases_ objectForKey:alias] )
					CCLOG(@"cocos2d: WARNING: an alias with name %@ already exists",alias);
				
				[spriteFramesAliases_ setObject:frameDictKey forKey:alias];
			}
			
			// create frame
			spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture 
													rectInPixels:CGRectMake(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height) 
														 rotated:textureRotated 
														  offset:spriteOffset 
													originalSize:spriteSourceSize];
		}

		// add sprite frame
		[spriteFrames_ setObject:spriteFrame forKey:frameDictKey];
		[spriteFrame release];
	}
}

-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture
{
	NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
	NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];

	[self addSpriteFramesWithDictionary:dict texture:texture];
}

-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName
{
	NSAssert( textureFileName, @"Invalid texture file name");
	CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:textureFileName];
	
	if( texture )
		[self addSpriteFramesWithFile:plist texture:texture];
	else
		CCLOG(@"cocos2d: CCSpriteFrameCache: couldn't load texture file. File not found: %@", textureFileName);
}

-(void) addSpriteFramesWithFile:(NSString*)plist
{
    NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
	
    NSString *texturePath = nil;
    NSDictionary *metadataDict = [dict objectForKey:@"metadata"];
    if( metadataDict )
        // try to read  texture file name from meta data
        texturePath = [metadataDict objectForKey:@"textureFileName"];
	
	
    if( texturePath )
    {
        // build texture path relative to plist file
        NSString *textureBase = [plist stringByDeletingLastPathComponent];
        texturePath = [textureBase stringByAppendingPathComponent:texturePath];
    } else {
        // build texture path by replacing file extension
        texturePath = [plist stringByDeletingPathExtension];
        texturePath = [texturePath stringByAppendingPathExtension:@"png"];
		
		CCLOG(@"cocos2d: CCSpriteFrameCache: Trying to use file '%@' as texture", texturePath); 
    }
	
    CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:texturePath];
	
	if( texture )
		[self addSpriteFramesWithDictionary:dict texture:texture];
	
	else
		CCLOG(@"cocos2d: CCSpriteFrameCache: Couldn't load texture");
}

-(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName
{
	[spriteFrames_ setObject:frame forKey:frameName];
}

#pragma mark CCSpriteFrameCache - removing

-(void) removeSpriteFrames
{
	[spriteFrames_ removeAllObjects];
	[spriteFramesAliases_ removeAllObjects];
}

-(void) removeUnusedSpriteFrames
{
	NSArray *keys = [spriteFrames_ allKeys];
	for( id key in keys ) {
		id value = [spriteFrames_ objectForKey:key];		
		if( [value retainCount] == 1 ) {
			CCLOG(@"cocos2d: CCSpriteFrameCache: removing unused frame: %@", key);
			[spriteFrames_ removeObjectForKey:key];
		}
	}	
}

-(void) removeSpriteFrameByName:(NSString*)name
{
	// explicit nil handling
	if( ! name )
		return;
	
	// Is this an alias ?
	NSString *key = [spriteFramesAliases_ objectForKey:name];
	
	if( key ) {
		[spriteFrames_ removeObjectForKey:key];
		[spriteFramesAliases_ removeObjectForKey:name];

	} else
		[spriteFrames_ removeObjectForKey:name];
}

- (void) removeSpriteFramesFromFile:(NSString*) plist
{
	NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
	NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
	
	[self removeSpriteFramesFromDictionary:dict];
}

- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary
{
	NSDictionary *framesDict = [dictionary objectForKey:@"frames"];
	NSMutableArray *keysToRemove=[NSMutableArray array];
	
	for(NSString *frameDictKey in framesDict)
	{
		if ([spriteFrames_ objectForKey:frameDictKey]!=nil)
			[keysToRemove addObject:frameDictKey];
	}
	[spriteFrames_ removeObjectsForKeys:keysToRemove];
}

- (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture
{
	NSMutableArray *keysToRemove=[NSMutableArray array];
	
	for (NSString *spriteFrameKey in spriteFrames_)
	{
		if ([[spriteFrames_ valueForKey:spriteFrameKey] texture] == texture) 
			[keysToRemove addObject:spriteFrameKey];
		
	}
	[spriteFrames_ removeObjectsForKeys:keysToRemove];
}

#pragma mark CCSpriteFrameCache - getting

-(CCSpriteFrame*) spriteFrameByName:(NSString*)name
{
	CCSpriteFrame *frame = [spriteFrames_ objectForKey:name];
	if( ! frame ) {
		// try alias dictionary
		NSString *key = [spriteFramesAliases_ objectForKey:name];
		frame = [spriteFrames_ objectForKey:key];
		
		if( ! frame )
			CCLOG(@"cocos2d: CCSpriteFrameCache: Frame '%@' not found", name);
	}
	
	return frame;
}

#pragma mark CCSpriteFrameCache - sprite creation

-(CCSprite*) createSpriteWithFrameName:(NSString*)name
{
	CCSpriteFrame *frame = [spriteFrames_ objectForKey:name];
	return [CCSprite spriteWithSpriteFrame:frame];
}
@end