//
//  DTTextAttachment.m
//  DTCoreText
//
//  Created by Oliver on 14.01.11.
//  Copyright 2011 Drobnik.com. All rights reserved.
//

#import "DTTextAttachment.h"
#import "DTCoreText.h"
#import "DTCoreGraphicsUtils.h"

#import "DTBase64Coding.h"
#import "DTDictationPlaceholderTextAttachment.h"
#import "DTIframeTextAttachment.h"
#import "DTImageTextAttachment.h"
#import "DTObjectTextAttachment.h"
#import "DTVideoTextAttachment.h"
#import "DTLog.h"
#import "NSCoder+DTCompatibility.h"

static NSMutableDictionary *_classForTagNameLookup = nil;

@interface DTTextAttachment ()

@end

@implementation DTTextAttachment
{
	NSURL *_hyperLinkURL;
	NSString *_hyperLinkGUID;
	
	CGFloat _fontLeading;
	CGFloat _fontAscent;
	CGFloat _fontDescent;
}

+ (void)initialize
{
	// this gets called from each subclass
	// prevent calling from children
	if (self != [DTTextAttachment class])
	{
		return;
	}
	
	_classForTagNameLookup = [[NSMutableDictionary alloc] init];
	
	// register standard tags
	[DTTextAttachment registerClass:[DTImageTextAttachment class] forTagName:@"img"];
	[DTTextAttachment registerClass:[DTVideoTextAttachment class] forTagName:@"video"];
	[DTTextAttachment registerClass:[DTIframeTextAttachment class] forTagName:@"iframe"];
	[DTTextAttachment registerClass:[DTObjectTextAttachment class] forTagName:@"object"];
}

+ (DTTextAttachment *)textAttachmentWithElement:(DTHTMLElement *)element options:(NSDictionary *)options
{
	Class class = [DTTextAttachment registeredClassForTagName:element.name];
	
	if (!class)
	{
		return nil;
	}

	DTTextAttachment *attachment = [class alloc];
	
	return [attachment initWithElement:element options:options];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
	self = [super init];
	if (self) {
		_displaySize = [aDecoder decodeCGSizeForKey:@"displaySize"];
		_originalSize = [aDecoder decodeCGSizeForKey:@"originalSize"];
		_maxImageSize = [aDecoder decodeCGSizeForKey:@"maxImageSize"];
		_contentURL = [aDecoder decodeObjectForKey:@"contentURL"];
		_attributes = [aDecoder decodeObjectForKey:@"attributes"];
		_verticalAlignment = [aDecoder decodeIntegerForKey:@"verticalAlignment"];
	}
	return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
	[aCoder encodeCGSize:_displaySize forKey:@"displaySize"];
	[aCoder encodeCGSize:_originalSize forKey:@"originalSize"];
	[aCoder encodeCGSize:_maxImageSize forKey:@"maxImageSize"];
	[aCoder encodeObject:_contentURL forKey:@"contentURL"];
	[aCoder encodeObject:_attributes forKey:@"attributes"];
	[aCoder encodeInteger:_verticalAlignment forKey:@"verticalAlignment"];
}

- (id)initWithElement:(DTHTMLElement *)element options:(NSDictionary *)options
{
	self = [super init];
	
	if (self)
	{
		// width, height from tag
		_originalSize = element.size; // initially not known
		
		// determine if there is a display size restriction
		_maxImageSize = CGSizeZero;
		
		NSValue *maxImageSizeValue =[options objectForKey:DTMaxImageSize];
		if (maxImageSizeValue)
		{
#if TARGET_OS_IPHONE
			_maxImageSize = [maxImageSizeValue CGSizeValue];
#else
			_maxImageSize = [maxImageSizeValue sizeValue];
#endif
		}
		
		// set the display size from the original size, restricted to the max size
		[self setDisplaySize:_originalSize withMaxDisplaySize:_maxImageSize];

		_attributes = element.attributes;
	}
	
	return self;
}

- (void)adjustVerticalAlignmentForFont:(CTFontRef)font
{
	_fontLeading = CTFontGetLeading(font);
	_fontAscent = CTFontGetAscent(font);
	_fontDescent = CTFontGetDescent(font);
}

- (CGFloat)ascentForLayout
{
	switch (_verticalAlignment) 
	{
		case DTTextAttachmentVerticalAlignmentBaseline:
		{
			return _displaySize.height;
		}
		case DTTextAttachmentVerticalAlignmentTop:
		{
			return _fontAscent;
		}	
		case DTTextAttachmentVerticalAlignmentCenter:
		{
			CGFloat halfHeight = (_fontAscent + _fontDescent) / 2.0f;
			
			return halfHeight - _fontDescent + _displaySize.height/2.0f;
		}
		case DTTextAttachmentVerticalAlignmentBottom:
		{
			return _displaySize.height - _fontDescent;
		}
	}
}

- (CGFloat)descentForLayout
{
	switch (_verticalAlignment) 
	{
		case DTTextAttachmentVerticalAlignmentBaseline:
		{
			return 0;
		}	
		case DTTextAttachmentVerticalAlignmentTop:
		{
			return _displaySize.height - _fontAscent;
		}	
		case DTTextAttachmentVerticalAlignmentCenter:
		{
			CGFloat halfHeight = (_fontAscent + _fontDescent) / 2.0f;
			
			return halfHeight - _fontAscent + _displaySize.height/2.0f;
		}	
		case DTTextAttachmentVerticalAlignmentBottom:
		{
			return _fontDescent;
		}
	}
}

#pragma mark - Subclass Customization

+ (void)registerClass:(Class)class forTagName:(NSString *)tagName
{
	Class previousClass = [DTTextAttachment registeredClassForTagName:tagName];

	if (previousClass)
	{
		DTLogWarning(@"Replacing previously registered class '%@' for tag name '%@' with '%@'", NSStringFromClass(previousClass), tagName, NSStringFromClass(class));
	}
	
	[_classForTagNameLookup setObject:class forKey:tagName];
}

+ (Class)registeredClassForTagName:(NSString *)tagName
{
	return [_classForTagNameLookup objectForKey:tagName];
}

#pragma mark Properties
/** Mutator for originalSize. Sets displaySize to the same value as originalSize. 
 @param originalSize The CGSize to store in originalSize. */
- (void)setOriginalSize:(CGSize)originalSize
{
	if (!CGSizeEqualToSize(originalSize, _originalSize))
	{
		_originalSize = originalSize;
		
		if (!_displaySize.width || !_displaySize.height)
		{
			[self setDisplaySize:_originalSize withMaxDisplaySize:_maxImageSize];
		}
	}
}

- (void)setDisplaySize:(CGSize)displaySize withMaxDisplaySize:(CGSize)maxDisplaySize
{
	if (_originalSize.width && _originalSize.height)
	{
		// width and/or height missing
		if (displaySize.width==0 && displaySize.height==0)
		{
			displaySize = _originalSize;
		}
		else if (!displaySize.width && displaySize.height)
		{
			// width missing, calculate it
			CGFloat factor = _originalSize.height / displaySize.height;
			displaySize.width = round(_originalSize.width / factor);
		}
		else if (displaySize.width>0 && displaySize.height==0)
		{
			// height missing, calculate it
			CGFloat factor = _originalSize.width / displaySize.width;
			displaySize.height = round(_originalSize.height / factor);
		}
	}

	if (maxDisplaySize.width>0 && maxDisplaySize.height>0)
	{
		if (maxDisplaySize.width < displaySize.width || maxDisplaySize.height < displaySize.height)
		{
			displaySize = DTCGSizeThatFitsKeepingAspectRatio(displaySize, maxDisplaySize);
		}
	}
	
	_displaySize = displaySize;
}

- (void)setDisplaySize:(CGSize)displaySize
{
	_displaySize = displaySize;
}

@synthesize originalSize = _originalSize;
@synthesize displaySize = _displaySize;
@synthesize contentURL = _contentURL;
@synthesize hyperLinkURL = _hyperLinkURL;
@synthesize attributes = _attributes;
@synthesize verticalAlignment = _verticalAlignment;
@synthesize hyperLinkGUID = hyperLinkGUID;

@end
