/* Copyright (c) 2011 Artjom Popov */

#import "UIImage+Additions.h"
#import "NSMutableArray+Additions.h"

#define EPImageCacheBucketSize 16

static NSString *EPImageCacheBucketDefaultIdentifier = @"EPImageCacheBucket";
static NSMutableDictionary *cacheBuckets = nil;

UIImage *EPImageGetCachedImageFromBucket(NSString *name, NSString *bucketIdentifier)
{
	if(cacheBuckets) {
		NSDictionary *bucket = nil;
		if ((bucket = [cacheBuckets objectForKey:bucketIdentifier])) {
			for (NSDictionary *item in bucket) {
				if ([item objectForKey:name]) {
					NSLog(@"BUCKET: %@", bucket);
					return (UIImage *)[item objectForKey:name];
				}
			}
		}
	}
	
	return nil;
}

void EPImagePutImageCacheBucket(UIImage *image, NSString *name, NSString *bucketIdentifier, NSUInteger bucketSize)
{
	NSMutableArray *bucket = nil;
	
	if(!cacheBuckets) {
		cacheBuckets = [[NSMutableDictionary alloc] init];
	}
	
	// Get bucket
	if(![cacheBuckets objectForKey:bucketIdentifier]) {
		bucket = [NSMutableArray arrayWithCapacity:bucketSize];
		[cacheBuckets setObject:bucket forKey:bucketIdentifier];
	} else {
		bucket = [cacheBuckets objectForKey:bucketIdentifier];
	}
	
	// Remove first item when the bucket is full
	if (bucket.count >= bucketSize) {
		[bucket removeFirstObject];
	}
	
	// Add image
	if (image && name) {
		NSLog(@"PUT: %@", name);
		[bucket addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:image, name, nil]];
	}
}

void EPImageAddRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight)
{
    if (ovalWidth == 0 || ovalHeight == 0) {
        CGContextAddRect(context, rect);
        return;
    }
	
    CGContextSaveGState(context);
    CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGContextScaleCTM (context, ovalWidth, ovalHeight);
    float fw = CGRectGetWidth (rect) / ovalWidth;
    float fh = CGRectGetHeight (rect) / ovalHeight;
    CGContextMoveToPoint(context, fw, fh/2);
    CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
    CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
    CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
    CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
    CGContextClosePath(context);
    CGContextRestoreGState(context);
}

void EPImageAddGlossEffect(CGContextRef context, CGRect rect, CGImageRef img, float ovalWidth, float ovalHeigh)
{	
	NSInteger height = rect.size.height;
	NSInteger width = rect.size.width;
	
	CGContextBeginPath(context);
	EPImageAddRoundedRectToPath(context, rect, ovalWidth, ovalHeigh);
	CGContextClosePath(context);
	CGContextClip(context);
	
	CGContextDrawImage(context, rect, img);
	
	// gloss	
	CGContextBeginPath(context);
	CGRect glossRect = CGRectMake(0, height - (height/2), width, height);		
	EPImageAddRoundedRectToPath(context, glossRect, 10.0F, 7.0F);
	CGContextClosePath(context);
	CGContextClip(context);
	
	CGContextSetRGBFillColor (context, 1, 1, 1, 0.15F);
	CGContextFillRect (context, CGRectMake (0, height - (height/2), width, height));
}

@implementation UIImage(Additions)

+ (UIImage *)imageNamed:(NSString *)name withTintColor:(UIColor *)color
{	
	NSString *bucketName = [NSString stringWithFormat:@"%@-%@", name, color];
	UIImage *image = nil;
	
	if ((image = EPImageGetCachedImageFromBucket(bucketName, EPImageCacheBucketDefaultIdentifier))) {
		return image;
	} else {
		image = [UIImage imageNamed:name];
		
		NSInteger height = image.size.height;
		NSInteger width = image.size.width;
		
		CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
		CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();	
		void *data = malloc(sizeof(UInt8) * width * height * 4);    
		
		CGContextRef context = CGBitmapContextCreate(data, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedFirst);
		
		CGContextClearRect(context, rect);
		
		CGContextSetFillColorWithColor(context, color.CGColor);
		CGContextTranslateCTM(context, 0, image.size.height);
		CGContextScaleCTM(context, 1.0, -1.0);
		CGContextSetBlendMode(context, kCGBlendModeColorBurn);
		CGContextDrawImage(context, rect, image.CGImage);
		CGContextClipToMask(context, rect, image.CGImage);
		CGContextAddRect(context, rect);
		CGContextDrawPath(context, kCGPathFill);
		
		CGImageRef imageRef = CGBitmapContextCreateImage(context);
		UIImage *newImage = [UIImage imageWithCGImage:imageRef];
		
		CGContextRelease(context);
		CGColorSpaceRelease(colorSpace);
		CGImageRelease(imageRef);
		free(data);
		
		EPImagePutImageCacheBucket(newImage, bucketName, EPImageCacheBucketDefaultIdentifier, EPImageCacheBucketSize);
		return newImage;
	}
	
	return nil;
}

+ (UIImage *)imageNamed:(NSString *)name withColor:(UIColor *)color
{
	NSString *bucketName = [NSString stringWithFormat:@"%@-%@", name, color];
	UIImage *image = nil;
	
	if ((image = EPImageGetCachedImageFromBucket(bucketName, EPImageCacheBucketDefaultIdentifier))) {
		return image;
	} else {
		image = [UIImage imageNamed:name];
		int height = image.size.height;
		int width = image.size.width;
		CGRect rect = CGRectMake(0.0F, 0.0F, width, height);
		CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
		
		NSInteger componentsNum = CGColorGetNumberOfComponents(color.CGColor);
		const CGFloat* components = CGColorGetComponents(color.CGColor);
		
		void *data = malloc(sizeof(UInt8) * width * height * 4);    
		
		CGContextRef context = CGBitmapContextCreate(data, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedFirst);
		
		CGContextClearRect(context, rect);
		CGContextDrawImage(context, rect, image.CGImage);	
		
		if(data != NULL) {
			
			for(int y = height - 1; y >= 0 ; y--) {
				
				for(int x = width - 1; x > 0; x--) {
					UInt8 *ptr = data + (y * width + x) * sizeof(UInt32);
					if(ptr[0] != 0.0F) {
						ptr[1] = (componentsNum > 2) ? (int)(components[0]*255) : (int)(components[0]*255); 
						ptr[2] = (componentsNum > 2) ? (int)(components[1]*255) : (int)(components[0]*255); 
						ptr[3] = (componentsNum > 2) ? (int)(components[2]*255) : (int)(components[0]*255);
					} 
				}
			}
		}
		
		CGImageRef imageRef = CGBitmapContextCreateImage(context);
		UIImage *newImage = [UIImage imageWithCGImage:imageRef];
		
		CGContextRelease(context);
		CGColorSpaceRelease(colorSpace);
		CGImageRelease(imageRef);
		free(data);
		
		EPImagePutImageCacheBucket(newImage, bucketName, EPImageCacheBucketDefaultIdentifier, EPImageCacheBucketSize);
		return newImage;
	}
	
	return nil;
}

+ (UIImage *)imageWithARGBData:(NSData *)data size:(CGSize)size
{
	int width = size.width;
	int height = size.height;
	
	if(data && sizeof(UInt8) * width * height * 4 == data.length) {
		CGImageRef imageRef;
		CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
		
		CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data);
		
		imageRef = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGImageAlphaPremultipliedFirst, provider, NULL, 1,kCGRenderingIntentDefault);
		
		UIImage *image = [UIImage imageWithCGImage:imageRef];
		CGImageRelease(imageRef);
		CGDataProviderRelease(provider);
		CGColorSpaceRelease(colorspace);
		
		return image;
	}
	
	//NSLog(@"ARGB data size is not valid");
	
	return nil;
}

+ (UIImage *)imageWithGlossEffect:(UIImage *)img cornerRadius:(int) cornerRadius 
{
	UIImage *newImage = nil;
	
	if(img)
	{
		int width = img.size.width;
		int height = img.size.height;
		
		CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
		void *data = malloc(sizeof(UInt8) * width * height * 4);
		
		CGContextRef context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedFirst);
		CGRect rect = CGRectMake(0, 0, width, height);
		CGContextClearRect(context, rect);
		
		EPImageAddGlossEffect(context, rect, img.CGImage, cornerRadius, cornerRadius);
		
		CGImageRef imageRef = CGBitmapContextCreateImage(context);
		newImage = [UIImage imageWithCGImage:imageRef];
		
		CGContextRelease(context);
		CGColorSpaceRelease(colorSpace);
		CGImageRelease(imageRef);
		free(data);
	}
	
    return newImage;
}

- (UIImage *)imageTransform:(CGAffineTransform)transform
{	
	int width = self.size.width;
	int height = self.size.height;
	
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();	
	void *data = malloc(sizeof(UInt8) * width * height * 4);    
	
	CGContextRef context = CGBitmapContextCreate(data, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedFirst);
	CGRect rect = CGRectMake(0, 0, width, height);
	CGContextClearRect(context, rect);
	
	CGContextConcatCTM(context, transform);
	CGContextDrawImage(context, rect, self.CGImage);
	
	CGImageRef imageRef = CGBitmapContextCreateImage(context);
	UIImage *newImage = [UIImage imageWithCGImage:imageRef];
	
	CGContextRelease(context);
	CGColorSpaceRelease(colorSpace);
	CGImageRelease(imageRef);
	free(data);
	
	return newImage;
	
}	

- (UIImage *)scaleToRect:(CGRect)rect
{
	int width = rect.size.width;
	int height = rect.size.height;
	
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();	
	void *data = malloc(sizeof(UInt8) * width * height * 4);    
	
	CGContextRef context = CGBitmapContextCreate(data, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedFirst);
	CGContextClearRect(context, rect);
	
	CGContextDrawImage(context, rect, self.CGImage);
	
	CGImageRef imageRef = CGBitmapContextCreateImage(context);
	UIImage *newImage = [UIImage imageWithCGImage:imageRef];
	
	CGContextRelease(context);
	CGColorSpaceRelease(colorSpace);
	CGImageRelease(imageRef);
	free(data);
	
	return newImage;
}

- (UIImage *)reflectedImage:(CGFloat)h
{
	CGSize size = self.size;
	NSInteger width = ceil(size.width);
	NSInteger height = ceil(h);
	CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
    CGContextRef gradientContext = CGBitmapContextCreate(nil, 1, height, 8, 0, colorspace, kCGImageAlphaNone);
	CGFloat colors[] = { 0.0F, 1.0F, 1.0F, 1.0F };
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorspace, colors, NULL, 2);
	void *data = malloc(sizeof(UInt8) * width * height * 4);    
	CGContextRef context;
	CGImageRef mask, image;
	UIImage *result;
	
	CGContextDrawLinearGradient(gradientContext, gradient, CGPointMake(0, height), CGPointZero, kCGGradientDrawsAfterEndLocation);
	CGColorSpaceRelease(colorspace);
    CGGradientRelease(gradient);
	
	CGContextSetGrayFillColor(gradientContext, 0.0F, 0.5F);
	CGContextFillRect(gradientContext, CGRectMake(0.0F, 0.0F, 1.0F, height));
	
	mask = CGBitmapContextCreateImage(gradientContext);
    CGContextRelease(gradientContext);
	
    colorspace = CGColorSpaceCreateDeviceRGB();
	context = CGBitmapContextCreate(data, width, height, 8, width * 4, colorspace, kCGImageAlphaPremultipliedFirst);
	CGColorSpaceRelease(colorspace);
	
	CGContextSetRGBFillColor(context, 1.0F, 1.0F, 1.0F, 1.0F);
	CGContextFillRect(context, CGRectMake(0.0F, 0.0F, width, height));
	CGContextConcatCTM(context, CGAffineTransformMake(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, height));
	CGContextClipToMask(context, CGRectMake(0.0F, 0.0F, width, height), mask);
	CGContextDrawImage(context, CGRectMake(0.0F, 0.0F, size.width, size.height), self.CGImage);
	CGImageRelease(mask);
	
	image = CGBitmapContextCreateImage(context);
	result = [UIImage imageWithCGImage:image];
	CGContextRelease(context);
	CGImageRelease(image);
	free(data);
	
	return result;
}

- (NSData *)ARGBData
{
    CGContextRef context;
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
	
    int width = self.size.width;
    int height = self.size.height;
	
	void *data = malloc(sizeof(UInt8) * width * height * 4);    
	
    context = CGBitmapContextCreate(data, width, height, 8, width * 4, colorspace, kCGImageAlphaPremultipliedFirst);
	
	CGContextClearRect(context, CGRectMake(0.0F, 0.0F, width, height));
	CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage);
	
    CGColorSpaceRelease(colorspace);
	CGContextRelease(context); 
	
    return [NSData dataWithBytesNoCopy:data length:sizeof(UInt8) * width * height * 4];
} 

@end
