#import "SVGImageElement.h"

#import "CALayerWithClipRender.h"
#import "SVGHelperUtilities.h"
#import "NSData+NSInputStream.h"

#import "SVGKImage.h"
#import "SVGKSourceURL.h"
#import "SVGKSourceNSData.h"
#import "SVGKInlineResource.h"

#import "SVGKDefine_Private.h"

CGImageRef SVGImageCGImage(UIImage *img)
{
#if SVGKIT_UIKIT
    return img.CGImage;
#else
    CGImageRef cgImage = [img CGImageForProposedRect:NULL context:nil hints:nil];
    return cgImage;
#endif
}

@interface SVGImageElement()
@property (nonatomic, strong, readwrite) NSString *href;
@end

@implementation SVGImageElement

@synthesize transform; // each SVGElement subclass that conforms to protocol "SVGTransformable" has to re-synthesize this to work around bugs in Apple's Objective-C 2.0 design that don't allow @properties to be extended by categories / protocols
@synthesize viewBox; // each SVGElement subclass that conforms to protocol "SVGFitToViewBox" has to re-synthesize this to work around bugs in Apple's Objective-C 2.0 design that don't allow @properties to be extended by categories / protocols
@synthesize preserveAspectRatio; // each SVGElement subclass that conforms to protocol "SVGFitToViewBox" has to re-synthesize this to work around bugs in Apple's Objective-C 2.0 design that don't allow @properties to be extended by categories / protocols

@synthesize x = _x;
@synthesize y = _y;
@synthesize width = _width;
@synthesize height = _height;

@synthesize href = _href;


- (void)postProcessAttributesAddingErrorsTo:(SVGKParseResult *)parseResult {
	[super postProcessAttributesAddingErrorsTo:parseResult];

	if( [[self getAttribute:@"x"] length] > 0 )
	_x = [[self getAttribute:@"x"] floatValue];

	if( [[self getAttribute:@"y"] length] > 0 )
	_y = [[self getAttribute:@"y"] floatValue];

	if( [[self getAttribute:@"width"] length] > 0 )
	_width = [[self getAttribute:@"width"] floatValue];

	if( [[self getAttribute:@"height"] length] > 0 )
	_height = [[self getAttribute:@"height"] floatValue];

	if( [[self getAttribute:@"href"] length] > 0 )
        self.href = [self getAttribute:@"href"];
    
    [SVGHelperUtilities parsePreserveAspectRatioFor:self];
}


- (CALayer *) newLayer
{
	CALayer* newLayer = [CALayerWithClipRender layer];
	
	[SVGHelperUtilities configureCALayer:newLayer usingElement:self];
	
	NSData *imageData;
	NSURL* imageURL = [NSURL URLWithString:_href];
	SVGKSource* effectiveSource = nil;
	if ([_href hasPrefix:@"http:"] || [_href hasPrefix:@"https:"] )
		imageData = [NSData dataWithContentsOfURL:imageURL];
	else
	if( [_href hasPrefix:@"data:"])
	{
		self.href = [_href stringByReplacingOccurrencesOfString:@"\\s+"
												 withString:@""
													options:NSRegularExpressionSearch
													  range:NSMakeRange(0, [_href length]) ];
		
		imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_href]];
	}
	else
	{
		effectiveSource = [self.rootOfCurrentDocumentFragment.source sourceFromRelativePath:_href];
		NSInputStream *stream = effectiveSource.stream;
        if (stream) {
            [stream open]; // if we do this, we CANNOT parse from this source again in future
            NSError *error = nil;
            imageData = [NSData dataWithContentsOfStream:stream initialCapacity:NSUIntegerMax error:&error];
            if( error )
                SVGKitLogError(@"[%@] ERROR: unable to read stream from %@ into NSData: %@", [self class], _href, error);
        } else {
            SVGKitLogError(@"[%@] ERROR: unable to load the source from URL: %@", [self class], _href);
        }
	}
	
	/** Now we have some raw bytes, try to load using Apple's image loaders
	 (will fail if the image is an SVG file)
	 */
	UIImage *image = [[UIImage alloc] initWithData:imageData];
	
    if( image == nil ) // NSData doesn't contain an imageformat Apple supports; might be an SVG instead
    {
        SVGKImage *svg = nil;
        
        if( effectiveSource == nil )
            effectiveSource = [SVGKSourceURL sourceFromURL:imageURL];
        
        if( effectiveSource != nil )
        {
            SVGKitLogInfo(@"Attempting to interpret the image at URL as an embedded SVG link (Apple failed to parse it): %@", _href );
            if( imageData != nil )
            {
                /** NB: sources can only be used once; we've already opened the stream for the source
                 earlier, so we MUST pass-in the already-downloaded NSData
                 
                 (if not, we'd be downloading it twice anyway, which can be lethal with large
                 SVG files!)
                 */
                svg = [SVGKImage imageWithSource: [SVGKSourceNSData sourceFromData:imageData URLForRelativeLinks:imageURL]];
            }
            else
            {
                svg = [SVGKImage imageWithSource: effectiveSource];
            }
            
            if( svg != nil )
            {
                image = svg.UIImage;
            }
        }
        
        // If still fail, use the broken image placeholder
        if (!image) {
            image = SVGKGetBrokenImageRepresentation();
        }
    }
    
	if( image != nil )
	{
        CGRect frame = CGRectMake(_x, _y, _width, _height);
        
        if( imageData )
            self.viewBox = SVGRectMake(0, 0, image.size.width, image.size.height);
        else
            self.viewBox = SVGRectMake(0, 0, _width, _height);
        
        CGImageRef imageRef = SVGImageCGImage(image);
        BOOL imageRefHasBeenRetained = false; // only one codepath CREATES a new image, because of Apple's API; the rest use an existing reference
        // apply preserveAspectRatio
        if( self.preserveAspectRatio.baseVal.align != SVG_PRESERVEASPECTRATIO_NONE
           && ABS( self.aspectRatioFromWidthPerHeight - self.aspectRatioFromViewBox) > 0.00001 )
        {
            double ratioOfRatios = self.aspectRatioFromWidthPerHeight / self.aspectRatioFromViewBox;
            if( self.preserveAspectRatio.baseVal.meetOrSlice == SVG_MEETORSLICE_MEET )
            {
                // shrink the image to fit in the frame, preserving the aspect ratio
                frame = [self clipFrame:frame fromRatio:ratioOfRatios];
            }
            else if( self.preserveAspectRatio.baseVal.meetOrSlice == SVG_MEETORSLICE_SLICE )
            {
                // crop the image
                CGRect cropRect = CGRectMake(0, 0, image.size.width, image.size.height);
                cropRect = [self clipFrame:cropRect fromRatio:1.0 / ratioOfRatios];
                imageRef = CGImageCreateWithImageInRect(imageRef, cropRect);
                imageRefHasBeenRetained = true;
            }
        }
        
        /** transform our LOCAL path into ABSOLUTE space */
        frame = CGRectApplyAffineTransform(frame, [SVGHelperUtilities transformAbsoluteIncludingViewportForTransformableOrViewportEstablishingElement:self]);
        newLayer.frame = frame;
        
        newLayer.contents = (__bridge id)imageRef;
        if( imageRefHasBeenRetained )
            CGImageRelease( imageRef );
	}
		
#if OLD_CODE
	__block CALayer *layer = [[CALayer layer] retain];

	layer.name = self.identifier;
	[layer setValue:self.identifier forKey:kSVGElementIdentifier];
	
	CGRect frame = CGRectMake(_x, _y, _width, _height);
	frame = CGRectApplyAffineTransform(frame, [SVGHelperUtilities transformAbsoluteIncludingViewportForTransformableOrViewportEstablishingElement:self]);
	layer.frame = frame;
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_href]];
        SVGImageRef image = [SVGImage imageWithData:imageData];
        
        //    _href = @"http://b.dryicons.com/images/icon_sets/coquette_part_4_icons_set/png/128x128/png_file.png";
        //    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_href]];
        //    UIImage *image = [UIImage imageWithData:imageData];

        dispatch_async(dispatch_get_main_queue(), ^{
            layer.contents = (id)SVGImageCGImage(image);
        });
    });

    return layer;
#endif
	
	return newLayer;
}

- (CGRect)clipFrame:(CGRect)frame fromRatio:(double)ratioOfRatios
{
    if( ratioOfRatios > 1 ) // if we're going to have space to either side
    {
        CGFloat width = frame.size.width;
        frame.size.width = frame.size.width / ratioOfRatios;
        switch( self.preserveAspectRatio.baseVal.align )
        {
            case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
            case SVG_PRESERVEASPECTRATIO_XMIDYMID:
            case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
            {
                frame.origin.x = frame.origin.x + ((width - frame.size.width) / 2);
            }break;
                
            case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
            case SVG_PRESERVEASPECTRATIO_XMAXYMID:
            case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
            {
                frame.origin.x = frame.origin.x + width - frame.size.width;
            }break;
                
            default:
                break;
        }
    }
    else // if we're going to have space above and below
    {
        CGFloat height = frame.size.height;
        frame.size.height = frame.size.height * ratioOfRatios;
        switch( self.preserveAspectRatio.baseVal.align )
        {
            case SVG_PRESERVEASPECTRATIO_XMINYMID:
            case SVG_PRESERVEASPECTRATIO_XMIDYMID:
            case SVG_PRESERVEASPECTRATIO_XMAXYMID:
            {
                frame.origin.y = frame.origin.y + ((height - frame.size.height) / 2);
            }break;
                
            case SVG_PRESERVEASPECTRATIO_XMINYMAX:
            case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
            case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
            {
                frame.origin.y = frame.origin.y + height - frame.size.height;
            }break;
                
            default:
                break;
        }
    }
    return frame;
}

- (void)layoutLayer:(CALayer *)layer {
    
}

-(double)aspectRatioFromWidthPerHeight
{
    return self.height == 0 ? 0 : self.width / self.height;
}

-(double)aspectRatioFromViewBox
{
    return self.viewBox.height == 0 ? 0 : self.viewBox.width / self.viewBox.height;
}

@end
