//
//  ECRender.m
//  Etokicho
//
//  Created by 二鏡 on 11/11/19.
//  Copyright 2011年 二鏡庵. All rights reserved.
//

#import "ECRenderer.h"
extern NSString *udUseLightPreview;

@implementation ECRenderer
@synthesize movie,renderRange, shouldContinue,fps, size, delegate;

+ (void)initialize
{
    id array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    id cacheFolder = [array objectAtIndex: 0];
    id identifier = [[NSBundle mainBundle] bundleIdentifier];
    id myFolder = [cacheFolder stringByAppendingPathComponent: identifier];
    id fm = [NSFileManager defaultManager];
    [fm createDirectoryAtPath: myFolder 
  withIntermediateDirectories: YES
                   attributes: nil
                        error: nil];
}

+ (NSString*)cachePath
{
    id array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    id cacheFolder = [array objectAtIndex: 0];
    id identifier = [[NSBundle mainBundle] bundleIdentifier];
    id cacheFile = @"render.cache";
    id comps = [NSArray arrayWithObjects:cacheFolder, identifier, cacheFile, nil];
    return [NSString pathWithComponents: comps];
}

- (void)dealloc
{
    [movie release];
    [source release];
    [super dealloc];
}

#pragma mark Rendering
- (void)setRenderSource:(NSArray*)segments
                  range:(NSRange)range
{
    NSUInteger length = [segments count];
    NSRange fullRange = NSMakeRange(0,length);
    if(NSEqualRanges(range,NSMakeRange(0,0)))
    {
        range = fullRange;
    }
    [source release];
    source = [[segments subarrayWithRange: range] retain];
    renderRange = range;
}

- (void)setLightPreview:(BOOL)flag
{
    [source setValue: [NSNumber numberWithBool: flag]
              forKey: @"lightPreview"];    
}

- (void)clearRenderSource
{
    [source release];
    source = nil;
}

- (void)_endRender:(id)obj
{
    [movie attachToCurrentThread];
    [target performSelector: completionHandler
                 withObject: movie];
}

- (void)_abortRender:(id)obj
{
    [movie attachToCurrentThread];
    [self invalidate];
    [target performSelector: completionHandler
                 withObject: nil];    
}

- (void)_startRender:(id)obj
{
    id pool = [[NSAutoreleasePool alloc] init];
    [QTMovie enterQTKitOnThreadDisablingThreadSafetyProtection];
    [movie attachToCurrentThread];

    NSUInteger i, limit = [source count];
    id <ECRendererSegment> before = nil;
    for(i=0;i<limit;i++)
    {
        id <ECRendererSegment> segment = [source objectAtIndex: i];
        
        [segment cleanup]; 
        // 時刻0は常に同一として期待してもいいかな？
        // もしランダムに初期化するアニメーションレイヤーが入るとおかしいと思われるのだが、
        [delegate rendererWillRenderSegment: segment
                                    atIndex: i
                                   inSource: source];
        [segment insertSegment: movie
                     frameRate: fps
                          size: size];
        [delegate rendererDidRenderSegment: segment
                                   atIndex: i
                                  inSource: source];

        [before cleanup];
        before = segment;
        
        if([delegate rendererShouldStopRendering])
        {
            [before cleanup];
            // force break.
            [movie detachFromCurrentThread];
            [QTMovie exitQTKitOnThread];
            [self performSelectorOnMainThread: @selector(_abortRender:)
                                   withObject: nil
                                waitUntilDone: NO];
            [pool release];
            return;
        }
    }
    [before cleanup];

    [movie detachFromCurrentThread];
    [QTMovie exitQTKitOnThread];

    [self performSelectorOnMainThread: @selector(_endRender:)
                           withObject: nil
                        waitUntilDone: NO];
    [pool release];
}

- (BOOL)detachRenderThreadWithCompletionHandler:(SEL)aSel
                                         target:(id)obj
                                          error:(NSError**)error;
{
    if(source == nil)
    {
        // errorは面倒くさいので今書かない
        // no source
        return NO;
    }
    
    target = obj;
    completionHandler = aSel;
    shouldContinue = YES;
    
    // 無効化を保証
    [self invalidate];

    id tmpPath = [ECRenderer cachePath];
    movie = [[QTMovie alloc] initToWritableFile: tmpPath
                                          error: error];
    if(movie == nil)
        return NO;
    
    [movie detachFromCurrentThread];
    [NSThread detachNewThreadSelector: @selector(_startRender:)
                             toTarget: self
                           withObject: nil];
    
    return YES;
}

- (void)invalidate
{
    [movie invalidate];
    [movie release];
    movie = nil;
    
    id fm = [NSFileManager defaultManager];
    id path = [ECRenderer cachePath];
    [fm removeItemAtPath: path
                   error: nil];
}

#pragma mark Export
- (void)_endWrite:(id)obj
{
    [movie attachToCurrentThread];
    [target performSelector: completionHandler
                 withObject: obj];
}

- (void)_writeMovie:(NSURL*)aURL
{
    id pool = [[NSAutoreleasePool alloc] init];
    [QTMovie enterQTKitOnThreadDisablingThreadSafetyProtection];
    [movie attachToCurrentThread];

    id attr = [NSDictionary dictionaryWithObjectsAndKeys:
               [NSNumber numberWithBool: YES], QTMovieFlatten,
               [NSNumber numberWithInt: kQTFileTypeMovie], QTMovieExportType,
               nil];

    BOOL result = [movie writeToFile: [aURL path]
                      withAttributes: attr];
    id ret = [NSNumber numberWithBool: result];
    
    [movie detachFromCurrentThread];
    [QTMovie exitQTKitOnThread];

    [self performSelectorOnMainThread: @selector(_endWrite:)
                           withObject: ret
                        waitUntilDone: YES];

    [pool release];
}

- (BOOL)detachExportThreadWithCompletionHandler:(SEL)aSel
                                         target:(id)obj
                                            URL:(NSURL*)aURL
{
    if(movie == nil)
        return NO;
    
    target = obj;
    completionHandler = aSel;
    
    [movie detachFromCurrentThread];
    [NSThread detachNewThreadSelector: @selector(_writeMovie:)
                             toTarget: self
                           withObject: aURL];
    return YES;
}
@end
