//
//  ECDTransitionSegment.m
//  Etokicho
//
//  Created by 二鏡 on 11/12/06.
//  Copyright (c) 2011年 二鏡庵. All rights reserved.
//

#import "ECDTransitionSegment.h"
#import "ServiceFunctions.h"

@interface ECDTransitionSegment (CoreDataGeneratedAccessor)
- (void)setPrimitiveItem:(ECQCCompositionItem*)aItem;
@end
static NSString *plistTemplateNameKey = @"templateName";
static NSString *plistCompositionKey = @"composition";
static NSString *plistFlipHorizontalKey = @"flipHorizontal";

@implementation ECDTransitionSegment

@dynamic templateName;
@dynamic flipHorizontal;
@dynamic item;
+ (NSString*)segmentType
{
    return @"TransitionSegment";
}

- (NSString*)type
{
    return NSLocalizedString(@"Transition", @"");
}

- (id)loadContentsOfURL:(NSURL*)URL
           propertyList:(NSDictionary*)plist
              inContext:(NSManagedObjectContext*)context
{    
    id item_ = [plist objectForKey: plistCompositionKey];
    
    self.msec = [[plist objectForKey: plistSegmentMsecKey] integerValue];
    self.flipHorizontal = [[plist objectForKey: plistFlipHorizontalKey] boolValue];
    self.templateName = [plist objectForKey: plistTemplateNameKey];
    self.item = [NSUnarchiver unarchiveObjectWithData: item_];
    
    return self;
}

- (BOOL)writeToURL:(NSURL*)URL
{
    id plist = [NSMutableDictionary dictionary];
    id item_ = [NSArchiver archivedDataWithRootObject: self.item];
    
    [plist setObject: [[self class] segmentType]
              forKey: plistSegmentTypeKey];
    [plist setObject: [NSNumber numberWithInteger: self.msec] 
              forKey: plistSegmentMsecKey];
    [plist setObject: self.templateName 
              forKey: plistTemplateNameKey];
    [plist setObject: item_ 
              forKey: plistCompositionKey];
    [plist setObject: [NSNumber numberWithBool: self.flipHorizontal] 
              forKey: plistFlipHorizontalKey];
    
    id plistURL = [ECDSegment propertyListForLocation: URL];
    return [plist writeToURL: plistURL atomically: YES];
}

- (void)dealloc
{
    // 念のため削除
    [srcImage release];
    [dstImage release];
    [super dealloc];
}

- (void)setItem:(ECQCCompositionItem*)aItem
{
    [self willChangeValueForKey: @"item"];
    id item = [aItem copy];
    [self setPrimitiveItem: item];
    [item release];
    [self didChangeValueForKey: @"item"];
}

- (void)_renderBlackScreen:(QTMovie*)aMovie
                      size:(NSSize)size
{
    CGImageRef blackImage = [[self class] allocBlackImage: size];
    id image = [[NSImage alloc] initWithCGImage: blackImage size: size];
    CGImageRelease(blackImage);
    
    [aMovie addImage: image
         forDuration: QTMakeTime(self.msec, 1000)
      withAttributes: [[self class] standardRenderAttributes]];
    
    [image release];
}

- (void)insertSegment:(QTMovie*)aMovie 
            frameRate:(NSUInteger)fps
                 size:(NSSize)size
{    
    if(self.lightPreview)
        return;
    ECQCCompositionItem *item = self.item;
    NSInteger msec = self.msec;
    
    // rendererを準備
    id comp = [item loadComposition];
    if(comp == nil)
    {
        // 失敗したら黒場面で埋める
        [self _renderBlackScreen: aMovie
                            size: size];
        return;
    }
    
    QCRenderer *renderer = [[QCRenderer alloc] initOffScreenWithSize: size
                                                          colorSpace: nil
                                                         composition: comp];
    [item apply: renderer];
    
    // imageの存在は外側で保証する
    [renderer setValue: (id)srcImage forInputKey: QCCompositionInputSourceImageKey];
    [renderer setValue: (id)dstImage forInputKey: QCCompositionInputDestinationImageKey];
    
    id attr = [[self class] standardRenderAttributes];
    NSUInteger f_sec, frames, adjust_cycle;
    _calculateFrameInfo(fps, msec, &f_sec, &frames, &adjust_cycle);
    
    NSUInteger is_adjust = 0;
    NSUInteger duration = 0;
    
    for(NSUInteger i=0;i<frames;i++)
    {
        id pool = [[NSAutoreleasePool alloc] init];
        
        // transition timeは0-1で正規化される
        CGFloat transition = (CGFloat)duration/(CGFloat)msec;
        id image;
        if([renderer renderAtTime: transition arguments: nil])
        {
            image = [renderer createSnapshotImageOfType: @"NSImage"];
        }
        else
        {
            // 一応黒場面で時間を維持。状況的にはクリティカルと思われる
            CGImageRef blackImage = [[self class] allocBlackImage: size];
            image = [[NSImage alloc] initWithCGImage: blackImage size: size];
            CGImageRelease(blackImage);
        }
        
        NSUInteger moment = f_sec;
        is_adjust = (is_adjust+1) % adjust_cycle;
        if(is_adjust == 0)
            moment += 1;
        [aMovie addImage: image
             forDuration: QTMakeTime(moment,1000)
          withAttributes: attr];
        duration += moment;
        
        [image release];
        [pool release];
    }
    [renderer release];
}

- (void)setSourceImage:(CIImage*)image
{
    if(srcImage == image)
        return;
    [srcImage release];
    srcImage = [image retain];
}

- (void)setDestinationImage:(CIImage*)image
{
    if(dstImage == image)
        return;
    [dstImage release];
    
    // ここでflipを自動で掛ける。
    if(self.flipHorizontal)
    {
        id filter = [CIFilter filterWithName: @"CIAffineTransform"];
        [filter setDefaults];
        
        id trans = [NSAffineTransform transform];
        [trans scaleXBy: -1.0 yBy: 1.0];
        [filter setValue: trans forKey: @"inputTransform"];
        [filter setValue: image forKey: @"inputImage"];
        dstImage = [[filter valueForKey: @"outputImage"] retain];
    }
    else
    {
        dstImage = [image retain];
    }
}

- (void)clearImage
{
    [srcImage release]; srcImage = nil;
    [dstImage release]; dstImage = nil;
}

- (id)cloneInContext:(NSManagedObjectContext*)context
{
    ECDTransitionSegment *ret = [super cloneInContext: context];
    ret.item = self.item;
    ret.templateName = self.templateName;
    ret.flipHorizontal = self.flipHorizontal;
    return ret;
}

@end
