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

#import "MainController.h"
#import "ECSceneView.h"
#import "UserDefaults.h"
#import "UTIs.h"
#import "NewProjectPanelController.h"

#import "ECMoviePreviewPanelController.h"
#import "ECSegmentGeneratorPanelController.h"
#import "ECTextImporterController.h"
#import "ECStoryboardPanelController.h"
#import "ECImageBrowserWindowController.h"
#import "ECScenePropertyPanelController.h"
#import "ECLayerPropertyPanelWindowController.h"
#import "ECCompositionWindowController.h"
#import "ECLongOperationPanelController.h"
#import "ECTextPanelWindowController.h"
#import "ECProjectInfoPanelController.h"
#import "NSWindowController+Interaction.h"

#import "ECDSceneSegment.h"
#import "ECDTransitionSegment.h"

static NSInteger 
ECRunProjectAlertPanel()
{
    return NSRunAlertPanel(NSLocalizedString(@"Project has changes...", @""), 
                           NSLocalizedString(@"Do you want to save before closing?", @""), 
                           NSLocalizedString(@"Save", @""),
                           NSLocalizedString(@"Don't Save",@""),
                           NSLocalizedString(@"Cancel", @""));
}


@interface MainController ()
@property (readwrite) BOOL renderingWillStop;
- (BOOL)canCloseProject;
- (BOOL)saveProjectAllowsOverride:(BOOL)shouldOverride;
- (void)orderFrontStoryboardIfNeed_;
@end

@implementation MainController
@synthesize timelineManager, configurationManager = configuration, renderingWillStop, project;
- (id)init
{
    self = [super init];
    configuration = [[ConfigurationManager alloc] init];
    if(configuration == nil)
        NSLog(@"initialize error");
    renderer = [[ECRenderer alloc] init];
    renderer.delegate = self;
    composition = [[ECSceneComposition alloc] init];
    timelineManager = [[ECTimelineManager alloc] init];
    
    return self;
}

#pragma mark Threaded Interaction
- (void)panelAbort_:(id)arg
{
    // abortをメインスレッド上で実行するためのコンテナメソッド
    // ドキュメントを読む限り、abortModalがスレッドセーフとの記載はないので正統的にはこうなる
    [NSApp abortModal];
}

- (void)_resetTimeline:(id)obj
{
    [timelineManager reset];
}

- (void)openProject_:(NSURL*)URL
{
    id pool = [[NSAutoreleasePool alloc] init];
    
    [self performSelectorOnMainThread: @selector(_resetTimeline:)
                           withObject: nil
                        waitUntilDone: YES];
    // ↓ここで時間がかかる
    id aProject = [[ECProject alloc] initWithContentsOfURL: URL
                                              intoTimeline: timelineManager];
    self.project = aProject;
    [aProject release];
    [timelineManager resetUndo];
    
    [[NSDocumentController sharedDocumentController]
     noteNewRecentDocumentURL: URL];
    id aScene = timelineManager.posterScene;
    if(aScene == nil)
        aScene = project.emptyScene;
    composition.scene = aScene;
    [self orderFrontStoryboardIfNeed_];
    
    // 一応メインスレッドで停止させる
    [self performSelectorOnMainThread: @selector(panelAbort_:) 
                           withObject: nil
                        waitUntilDone: NO];
    [pool release];
}

- (void)exportComplete_:(NSNumber*)completion
{
    [self panelAbort_: self]; // これはメインスレッドで来るからそのままでいい
    if([completion boolValue] == NO)
    {
        // 書き出しに失敗した
        NSRunCriticalAlertPanel(@"Failure exporting movie", @"Critical error was occured on exporting movie file.", @"OK", nil, nil);
        return;
    }
    [NSApp requestUserAttention: NSInformationalRequest];
}

- (void)openProject:(NSURL*)URL
{
    [[ECMoviePreviewPanelController sharedPanel] close];

    ECLongOperationPanelController *panel = [ECLongOperationPanelController sharedPanel];
    NSProgressIndicator *bar = panel.progress;
    [bar setIndeterminate: YES];
    panel.title = NSLocalizedString(@"Opening Project...", @"");
    panel.durationString = @"";
    panel.canStop = NO;
    [NSThread detachNewThreadSelector: @selector(openProject_:)
                             toTarget: self
                           withObject: URL];
    [panel beginWithCancelHandler: nil];    
}

- (void)exportMovieToURL:(NSURL*)URL
{
    // 既存ファイルが存在している場合は削除する
    id fm = [NSFileManager defaultManager];
    if([fm fileExistsAtPath: [URL path]])
        [fm removeItemAtURL: URL
                      error: nil];
    
    // 書きだしロックパネルを準備
    ECLongOperationPanelController *panel = [ECLongOperationPanelController sharedPanel];
    NSProgressIndicator *bar = panel.progress;
    [bar setIndeterminate: YES];
    panel.title = NSLocalizedString(@"Exporting...", @"");
    panel.durationString = @"";
    panel.canStop = NO;
    
    // 書き出すスレッドを分離
    [renderer detachExportThreadWithCompletionHandler: @selector(exportComplete_:)
                                               target: self
                                                  URL: URL];

    [panel beginWithCancelHandler: nil];
}

#pragma mark Project I/O

- (BOOL)saveProjectAllowsOverride:(BOOL)shouldOverride
{
    if(shouldOverride)
    {
        // 既存ファイルは上書き
        id url = [project loadURL];
        if(url != nil)
        {
            [project writeToURL: url
                   fromTimeline: timelineManager];
            [[NSDocumentController sharedDocumentController]
             noteNewRecentDocumentURL: url];
            [timelineManager updateChangeCount: NSChangeCleared];
            return YES;
        }
    }
    
    // 新規ファイルを保存
    id panel = [NSSavePanel savePanel];
    [panel setExtensionHidden: YES];
    [panel setAllowedFileTypes: [NSArray arrayWithObject: (id)utiProjectFile]];
    if([panel runModal] == NSFileHandlingPanelCancelButton)
        return NO;
    
    id url = [panel URL];
    
    // extension test
    id ext = [url pathExtension];
    if(![ext isEqualToString: gProjectFileExtension])
    {
        url = [url URLByAppendingPathExtension: gProjectFileExtension];
    }
    [project writeToURL: url
           fromTimeline: timelineManager];
    [[NSDocumentController sharedDocumentController]
     noteNewRecentDocumentURL: url];
    [timelineManager updateChangeCount: NSChangeCleared];
    
    return YES;
}

- (BOOL)canCloseProject
{
    [[ECMoviePreviewPanelController sharedPanel] close];
    if(timelineManager.hasChange)
    {
        NSInteger result = ECRunProjectAlertPanel();
        switch(result)
        {
            case NSAlertDefaultReturn: // ok
                if([self saveProjectAllowsOverride: YES] == NO)
                    return NO; // キャンセルされたら戻る
                break;
            case NSAlertAlternateReturn: // don't save
                break;
            case NSAlertOtherReturn: // cancel
                return NO;
        }
    }
    
    return YES;
}

#pragma mark Rendering
- (void)showPreviewPanel:(QTMovie*)aMovie
{
    id previewPanel = [ECMoviePreviewPanelController sharedPanel];
    [previewPanel setMovie: aMovie];
    [previewPanel showWindow: self];
}

- (void)renderCompleted:(QTMovie*)result
{
    [NSApp abortModal];
    [renderer clearRenderSource];
    
    if(result == nil)
    {
        preview.range = NSMakeRange(NSNotFound,NSNotFound); // 安全のためクリアしておく
        return;
    }
    
    if([[NSUserDefaults standardUserDefaults] boolForKey: udUseLightPreview] == NO)
        preview.isMovieCompleted = YES;
    [self showPreviewPanel: result];
    [NSApp requestUserAttention: NSInformationalRequest];
}

- (void)startRender:(NSValue*)val
{
    NSRange range = [val rangeValue];
    if([timelineManager isEmpty])
    {
        id title = NSLocalizedString(@"Project has no segments in timeline.",@"");
        id msg = NSLocalizedString(@"You cannot make empty movie.", @"");
        NSRunAlertPanel(title, msg, @"OK", nil, nil);
        return;
    }

    ECDTimeline *timeline = timelineManager.timeline;
    // renderer setup
    [renderer setRenderSource: [[timeline segments] array]
                        range: range];
    [renderer setLightPreview: [[NSUserDefaults standardUserDefaults] boolForKey: udUseLightPreview]];
    renderer.fps = timeline.fps;
    renderer.size = project.movieSize;
    preview.isMovieCompleted = NO;
    preview.range = range;
    renderingWillStop = NO;
    
    // panel setup
    ECLongOperationPanelController *panel = [ECLongOperationPanelController sharedPanel];
    NSProgressIndicator *bar = panel.progress;
    [bar setIndeterminate: NO];
    [bar setDoubleValue: 0.0];
    panel.title = NSLocalizedString(@"Rendering...", @"");
    panel.durationString = [NSString stringWithFormat: @"0/%lu", renderer.renderRange.length];
    panel.canStop = YES;
    
    if([renderer detachRenderThreadWithCompletionHandler: @selector(renderCompleted:)
                                                  target: self
                                                   error: nil] == NO)
    {
        // detach error
        // これがくるようなら致命的で直しようがない
        NSRunAlertPanel(@"Rendering Start Error", @"bug?", @"OK", nil, nil);
    }
    
    // cancel panel trap
    [panel beginWithCancelHandler: ^()
     {
         self.renderingWillStop = YES;
     }];
}

#pragma mark renderer delegate
- (void)rendererWillRenderSegment:(id)segment 
                          atIndex:(NSUInteger)i
                         inSource:(NSArray*)source
{
    // Transitionに前後のイメージを供給する
    // 前後がおかしい場合は常に黒イメージで補完する
    // Garbage in, Garbage out!
    Class Transition = [ECDTransitionSegment class];
    if([segment isKindOfClass: Transition])
    {
        NSUInteger limit = [source count];
        if(i == 0)
        {
            id color = [CIColor colorWithCGColor: CGColorGetConstantColor(kCGColorBlack)];
            id blackImage = [CIImage imageWithColor: color];
            [segment setSourceImage: blackImage];
        }
        else
        {
            id before = [source objectAtIndex: i-1];
            if([before isKindOfClass: Transition])
            {
                id color = [CIColor colorWithCGColor: CGColorGetConstantColor(kCGColorBlack)];
                id blackImage = [CIImage imageWithColor: color];
                [segment setSourceImage: blackImage];
            }
            else
            {
                CGImageRef cgimage = [before allocOutFrameImage];
                [segment setSourceImage: [CIImage imageWithCGImage: cgimage]];
                CGImageRelease(cgimage);
            }
        }
        
        if(i == limit-1)
        {
            id color = [CIColor colorWithCGColor: CGColorGetConstantColor(kCGColorBlack)];
            id blackImage = [CIImage imageWithColor: color];
            [segment setDestinationImage: blackImage];
        }
        else
        {
            id after = [source objectAtIndex: i+1];
            if([after isKindOfClass: Transition])
            {
                id color = [CIColor colorWithCGColor: CGColorGetConstantColor(kCGColorBlack)];
                id blackImage = [CIImage imageWithColor: color];
                [segment setDestinationImage: blackImage];
            }
            else
            {
                CGImageRef cgimage = [after allocInFrameImage];
                [segment setDestinationImage: [CIImage imageWithCGImage: cgimage]];
                CGImageRelease(cgimage);
            }
        }
    }
}

- (void)rendererDidRenderSegment:(id)segment
                         atIndex:(NSUInteger)i
                        inSource:(NSArray*)source
{
    if([segment isKindOfClass: [ECDTransitionSegment class]])
        [segment clearImage];
    
    NSUInteger all = [source count];
    ECLongOperationPanelController *panel = [ECLongOperationPanelController sharedPanel];
    panel.durationString = [NSString stringWithFormat: @"%lu/%lu", i+1, all];
    panel.progress.doubleValue = (CGFloat)(i+1)/(CGFloat)all;
}

- (BOOL)rendererShouldStopRendering
{
    return self.renderingWillStop;
}

#pragma mark Notification
- (void)applicationWillFinishLaunching:(id)notif
{
    registerUserDefaults();

    // 必須パネル群をロードしておく
    id compWc = [ECCompositionWindowController sharedCompositionWindow];
    [compWc setComposition: composition];
    
    [ECStoryboardPanelController sharedPanel];
    [ECScenePropertyPanelController sharedPanel];
    [[NSColorPanel sharedColorPanel] setShowsAlpha: YES];
    
    id center = [NSNotificationCenter defaultCenter];
    [center addObserver: self
               selector: @selector(segmentDidRequest:)
                   name: ECStoryBoardViewLayerSegmentRequestNotification
                 object: nil];
    [center addObserver: self 
               selector: @selector(timelineDidUpdate:)
                   name: NSManagedObjectContextObjectsDidChangeNotification
                 object: [timelineManager context]];
}

- (BOOL)application:(NSApplication *)theApplication
           openFile:(NSString *)filename
{
    id ws = [NSWorkspace sharedWorkspace];
    id UTI = [ws typeOfFile: filename 
                      error: nil];
    if(UTI == nil)
        return NO;
    
    id url = [NSURL fileURLWithPath: filename];

    if(UTTypeConformsTo((CFStringRef)UTI, utiProjectFile))
    {
        if([self canCloseProject] == NO)
            return NO;
        [self openProject: url];
        return YES;
    }
    
    if(UTTypeConformsTo((CFStringRef)UTI, utiSceneFile))
    {
        if(project == nil)
            return NO;
        
        id scene = [[ECScene alloc] initWithContentsOfURL: url];
        if(scene)
        {
            composition.scene = scene;
            [scene release];
            return YES;
        }
    }
    
    return NO;
}

- (void)applicationDidFinishLaunching:(id)notif
{
    [[NSFontManager sharedFontManager] setAction: @selector(changeLayerFont:)];
    [self toggleComposition: self];
}

- (void)applicationDidResignActive:(NSNotification *)aNotification
{
    ECCompositionWindowController *wc = [ECCompositionWindowController sharedCompositionWindow];
    [wc.sceneView setNeedsDisplay: YES];
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
    if([self canCloseProject])
        return NSTerminateNow;
    else
        return NSTerminateCancel;
}

- (void)applicationWillTerminate:(NSNotification *)aNotification
{
    // clear cache
    [renderer invalidate];
}

- (void)segmentDidRequest:(NSNotification*)aNotif
{
    id segment = [[aNotif userInfo] objectForKey: @"segment"];
    if([segment isKindOfClass: [ECDTransitionSegment class]])
        return;
    
    composition.scene = [[[segment scene] copy] autorelease];
    id wc = [ECCompositionWindowController sharedCompositionWindow];
    [[wc window] makeKeyAndOrderFront: self];
}

- (void)timelineDidUpdate:(NSNotification*)aNotif
{
    preview.isMovieCompleted = NO;
    preview.range = NSMakeRange(NSNotFound,NSNotFound);
}

#pragma mark -
#pragma mark Actions
#pragma mark Panels
- (IBAction)toggleComposition:(id)sender
{
    [[ECCompositionWindowController sharedCompositionWindow] orderToggle];
}

- (IBAction)toggleImageBrowser:(id)sender
{
    [[ECImageBrowserWindowController sharedBrowser] orderToggle];
}

- (IBAction)toggleLayerPropertyPanel:(id)sender
{
    [[ECLayerPropertyPanelWindowController sharedPanel] orderToggle];
}

- (IBAction)toggleScenePropertyPanel:(id)sender
{
    [[ECScenePropertyPanelController sharedPanel] orderToggle];
}

- (IBAction)toggleStoryboardPanel:(id)sender
{
    [[ECStoryboardPanelController sharedPanel] orderToggle];
}

- (IBAction)toggleGeneratorProperty:(id)sender
{
    [[ECSegmentGeneratorPanelController sharedPanel] orderToggle];
}

- (IBAction)toggleProjectStatics:(id)sender
{
    [[ECProjectInfoPanelController sharedPanel] orderToggle];
}

- (IBAction)toggleTextPanel:(id)sender
{
    [[ECTextPanelWindowController sharedPanel] orderToggle];
}

- (IBAction)toggleTextImporter:(id)sender
{
    [[ECTextImporterController sharedTextImporter] orderToggle];
}

#pragma mark Documents

- (IBAction)closeProject:(id)sender
{
    [[ECMoviePreviewPanelController sharedPanel] close];
    if([self canCloseProject])
    {
        [timelineManager reset];
        [timelineManager resetUndo];
        self.project = nil;
        composition.scene = nil;
    }
}

- (IBAction)openDocument:(id)sender
{
    if([self canCloseProject] == NO)
        return;
    
    NSOpenPanel *panel = [NSOpenPanel openPanel];
    id types = [NSArray arrayWithObjects: (id)utiProjectFile, nil];
    
    [panel setAllowedFileTypes: types];
    [panel setExtensionHidden: YES];
    
    [panel beginWithCompletionHandler: ^(NSInteger result)
     {
         if(result != NSFileHandlingPanelOKButton)
             return;
         
         NSURL *URL = [panel URL];
         [self application: NSApp openFile: [URL path]];
     }];
}

- (IBAction)openNewDocumentDialog:(id)sender
{    
    if([self canCloseProject] == NO)
        return ;
    
    NewProjectPanelController *panel = [NewProjectPanelController sharedPanel];
    [panel beginWithCompletionHandler: ^(NSInteger result) 
    {
        if(result == NSCancelButton)
            return;
        
        eResolutionMode mode = panel.mode;
        eFrameMode frameMode = panel.frameMode;
        // 一応エラーチェック
        if(mode >= eNicoNoMode || frameMode >= eNoneFPS)
            return;
        
        [[ECMoviePreviewPanelController sharedPanel] close];

        NSSize size = [panel sizeForMode: mode];
        NSInteger fps = [panel frameRateForMode: frameMode];

        // タイムラインを初期化
        [timelineManager resetWithFPS: fps];

        ECProject *aProject = [[[ECProject alloc] initWithMovieSize: size] autorelease];
        self.project = aProject;
        [timelineManager resetUndo];
        [composition setScene: [aProject emptyScene]];
    }];
}

- (IBAction)saveDocumentAs:(id)sender
{
    [self saveProjectAllowsOverride: NO];
}

- (IBAction)saveDocument:(id)sender
{
    [self saveProjectAllowsOverride: YES];
}

- (IBAction)exportMovie:(id)sender
{
    // ムービーが完成しているかをチェック
    if(preview.isMovieCompleted == NO ||
       NSEqualRanges(preview.range,NSMakeRange(0,0)) == NO)
    {
        id title = NSLocalizedString(@"Movie is not yet rendered.", @"");
        id msg = NSLocalizedString(@"Do you want to render a movie from now?", @"");
        id cancel = NSLocalizedString(@"Cancel", @"");
        if(NSRunInformationalAlertPanel(title, msg, @"OK", cancel, nil) != NSOKButton)
            return ;
        [self renderMovie: self];
        return;
    }
    
    // 保存先を決定
    id save = [NSSavePanel savePanel];
    [save setExtensionHidden: YES];
    [save setAllowedFileTypes: [NSArray arrayWithObject: (id)kUTTypeQuickTimeMovie]];
    [save setAllowsOtherFileTypes: NO];
    if([save runModal] == NSFileHandlingPanelCancelButton)
        return;
    
    id url = [save URL];
    
    // extension test
    id ext = [url pathExtension];
    if(![ext isEqualToString: @"mov"])
    {
        url = [url URLByAppendingPathExtension: @"mov"];
    }
    
    [self exportMovieToURL: url];
}

#pragma mark Render
- (IBAction)stopRender:(id)sender
{
    self.renderingWillStop = YES;
}

- (IBAction)renderMovie:(id)sender
{
    if(preview.isMovieCompleted && 
       NSEqualRanges(preview.range, NSMakeRange(0,0)))
    {
        [self showPreviewPanel: renderer.movie];
        return; 
    }
    
    [[NSUserDefaults standardUserDefaults] setBool: NO forKey: udUseLightPreview];
    [self performSelector: @selector(startRender:)
               withObject: [NSValue valueWithRange: NSMakeRange(0,0)]
               afterDelay: 0];
}

- (IBAction)renderMovieLight:(id)sender
{
    if(preview.isMovieCompleted && 
       NSEqualRanges(preview.range, NSMakeRange(0,0)))
    {
        [self showPreviewPanel: renderer.movie];
        return; 
    }
    [[NSUserDefaults standardUserDefaults] setBool: YES forKey: udUseLightPreview];
    [self performSelector: @selector(startRender:)
               withObject: [NSValue valueWithRange: NSMakeRange(0,0)]
               afterDelay: 0];    
}

- (IBAction)renderMovieInSelection:(id)sender
{
    NSIndexSet *indexes = [timelineManager selection];
    NSUInteger first = [indexes firstIndex];
    NSUInteger last = [indexes lastIndex];
    NSRange selection = NSMakeRange(first, last-first+1);
    
    // たまたま全体と一致する場合は{0,0}とみなす
    if(first == 0 && selection.length == [[timelineManager.timeline segments] count])
        selection = NSMakeRange(0,0);
    
    if(preview.isMovieCompleted)
    {
        if(NSEqualRanges(selection, preview.range))
        {
            [self showPreviewPanel: renderer.movie];
            return;
        }
    }
    [[NSUserDefaults standardUserDefaults] setBool: NO forKey: udUseLightPreview];
    [self performSelector: @selector(startRender:)
               withObject: [NSValue valueWithRange: selection]
               afterDelay: 0];    
}

#pragma mark Segments
- (IBAction)appendSegment:(id)sender
{
    ECScene *scene = composition.scene;
    id context = timelineManager.context;
    id timeline = timelineManager.timeline;
    id segment = [ECDSceneSegment attachNewSegmentWithScene: scene
                                                  inContext: context];
    
    // 自動transitionをつけるかどうかをみる
    id ud = [NSUserDefaults standardUserDefaults];
    if([ud boolForKey:udSegmentGeneratorInsertTransition])
    {
        id last = [[timeline segments] lastObject];
        if([last isKindOfClass: [ECDSceneSegment class]])
        {
            id transition = [ECDTransitionSegment attachNewTransitionInContext: context];
            id array = [NSArray arrayWithObjects: transition, segment, nil];
            [timeline addSegments: array];
        }
        else
        {
            [timeline addSegmentsObject: segment];
        }
    }
    else
    {
        [timeline addSegmentsObject: segment];
    }

    // intaraction
    [self orderFrontStoryboardIfNeed_];
}

- (IBAction)replaceSegment:(id)sender
{
    ECScene *scene = composition.scene;
    ECDSceneSegment *segment = [ECDSceneSegment attachNewSegmentWithScene: scene
                                                                inContext: timelineManager.context];
    [timelineManager.timeline replaceObjectInSegmentsAtIndex: [timelineManager.selection firstIndex]
                                                  withObject: segment];

    [self orderFrontStoryboardIfNeed_];
}

- (IBAction)removeSegment:(id)sender
{
    [timelineManager.timeline removeSegmentsAtIndexes: timelineManager.selection];
    [self orderFrontStoryboardIfNeed_];
}

- (IBAction)insertSegmentBefore:(id)sender
{
    /*** 必ずselectionが存在する文脈下で呼ばなければならない！ ***/
    id context = timelineManager.context;
    id timeline = timelineManager.timeline;
    NSUInteger idx = [timelineManager.selection firstIndex];
    
    // 挿入位置
    id insertion = [NSMutableIndexSet indexSetWithIndex: idx];
    
    // セグメントの準備
    ECScene *scene = composition.scene;
    id segment = [ECDSceneSegment attachNewSegmentWithScene: scene
                                                  inContext: context];
    id set = [NSMutableArray arrayWithObject: segment];
    
    // 現在のselectionがsceneであるならば
    // transitionを挟んでおく
    id ud = [NSUserDefaults standardUserDefaults];
    if([ud boolForKey:udSegmentGeneratorInsertTransition])
    {
        id current = [[timeline segments] objectAtIndex: idx];
        if([current isKindOfClass: [ECDSceneSegment class]])
        {
            id transition = [ECDTransitionSegment attachNewTransitionInContext: context];
            [set addObject: transition];
            [insertion addIndex: idx+1];
        }
    }
    
    // 挿入
    [timeline insertSegments: set
                   atIndexes: insertion];
    [self orderFrontStoryboardIfNeed_];
}

- (IBAction)insertSegmentAfter:(id)sender
{
    /*** 必ずselectionが存在する文脈下で呼ばなければならない！ ***/
    id context = timelineManager.context;
    id timeline = timelineManager.timeline;
    NSUInteger idx = [timelineManager.selection lastIndex];
    
    // 挿入位置
    id insertion = [NSMutableIndexSet indexSetWithIndex: idx+1];
    
    // セグメントの準備
    ECScene *scene = composition.scene;
    id segment = [ECDSceneSegment attachNewSegmentWithScene: scene
                                                  inContext: context];
    id set = [NSMutableArray arrayWithObject: segment];
    
    // 現在のselectionがsceneであるならば
    // transitionを挟んでおく
    id ud = [NSUserDefaults standardUserDefaults];
    if([ud boolForKey:udSegmentGeneratorInsertTransition])
    {
        id current = [[timeline segments] objectAtIndex: idx];
        if([current isKindOfClass: [ECDSceneSegment class]])
        {
            id transition = [ECDTransitionSegment attachNewTransitionInContext: context];
            [set insertObject: transition atIndex: 0];
            [insertion addIndex: idx+2];
        }
    }
    
    // 挿入
    [timeline insertSegments: set
                   atIndexes: insertion];
    [self orderFrontStoryboardIfNeed_];
}

- (IBAction)insertTransitionAfter:(id)sender
{
    /*** 必ずselectionが存在する文脈下で呼ばなければならない！ ***/
    id context = timelineManager.context;
    id timeline = timelineManager.timeline;
    NSUInteger idx = [timelineManager.selection lastIndex];

    id transition = [ECDTransitionSegment attachNewTransitionInContext: context];
    if(transition)
    {
        [timeline insertObject: transition
             inSegmentsAtIndex: idx+1];
        [self orderFrontStoryboardIfNeed_];
    }
}

- (IBAction)insertTransitionBefore:(id)sender
{
    /*** 必ずselectionが存在する文脈下で呼ばなければならない！ ***/
    id context = timelineManager.context;
    id timeline = timelineManager.timeline;
    NSUInteger idx = [timelineManager.selection firstIndex];
    
    id transition = [ECDTransitionSegment attachNewTransitionInContext: context];
    if(transition)
    {
        [timeline insertObject: transition
             inSegmentsAtIndex: idx];
        [self orderFrontStoryboardIfNeed_];
    }
}

#pragma mark Layer
- (IBAction)insertRegularLayer:(id)sender
{
    [composition insertRegular];
}

- (IBAction)insertMovieLayer:(id)sender
{
    // 未使用メソッド
//    [composition insertMovie];
}

- (IBAction)insertAnimationLayer:(id)sender
{
    [composition insertAnimation];
}

- (IBAction)removeLayer:(id)sender
{
    [composition remove];
}

#pragma mark -
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
    SEL action = [menuItem action];
    
    if(action == @selector(exportMovie:))
        return [timelineManager isEmpty] == NO;
    
    if(action == @selector(insertRegularLayer:) ||
       action == @selector(insertAnimationLayer:))
        return [composition scene] != nil;
    
    if(action == @selector(removeLayer:))
        return [composition canDelete];

    if(action == @selector(removeSegment:))
    {
        return [[timelineManager selection] count] != 0;
    }
    
    if(action == @selector(renderMovie:) ||
       action == @selector(renderMovieLight:))
    {
        return [timelineManager isEmpty] == NO;
    }
    
    if(action == @selector(renderMovieInSelection:))
    {
        NSIndexSet *selection = [timelineManager selection];
        NSUInteger count = [selection count];
        if(count == 0)
            return NO;
        NSUInteger length = [selection lastIndex] - [selection firstIndex]+1;
        return length == count; // 抜けがある場合はcountが少ないのでNOになる
    }
    
    if(action == @selector(appendSegment:))
        return project != nil;
    
    if(action == @selector(replaceSegment:))
    {
        return [[timelineManager selectedSegment] isKindOfClass: [ECDSceneSegment class]];
    }
    
    if(action == @selector(insertSegmentBefore:) ||
       action == @selector(insertSegmentAfter:))
    {
        return [[timelineManager selection] count] == 1;
    }
    
    if(action == @selector(insertTransitionAfter:) ||
       action == @selector(insertTransitionBefore:))
    {
        return [[timelineManager selection] count] == 1 &&
        [configuration currentTransition] != nil;
    }

    
    return YES;
}

#pragma mark Sub operation
- (void)orderFrontStoryboardIfNeed_
{
    id window = [[ECStoryboardPanelController sharedPanel] window];
    if([window isVisible] == NO)
        [window makeKeyAndOrderFront: self];
}

@end

