// MorphDocument.m 
//
// created by Martin Wennerberg on Sun 12-Nov-1995 
//
// when		who	modification

#import "MorphDocument.h"
#import "AppController.h"
#import "MorphLine.h"
#import "Morpher.h"
#import "Editor.h"
#import "NSBitmapImageRep_editing.h"
#import "algebra.h"
#import "PreviewCell.h"
#import "InspectorController.h"
#import "MorphLineInspector.h"

#define NIB_NAME @"MorphDocument.nib"

@implementation MorphDocument
+ (NSSet *) readableFileTypes;
{
    return [NSSet setWithObject:MORPH_DOC_EXTENTION];
}

+ (void) load
{
    [[NSApp delegate] registerDocumentClass:[self class]];
}

- (void) dealloc
{
    [firstImage release];
    [lastImage release];
    [morphLines release];
    [super dealloc];
}

- initWithContentsOfFile:(NSString *)path
{
    NSString		*filename;
    NSString		*fullPath;
    NSImage		*image;
    NSArray		*lineDefs;
    int			 i;
    MorphLine		*morphLine;
    NSDictionary        *infoDict;
    NSDictionary        *morphLineDict;
    NSSize				interCellSpacing = {2, 2};
    
    self = [super initWithContentsOfFile:path];
    morphLines = [[NSMutableArray allocWithZone:[self zone]] initWithCapacity:20];
    selectedLines = [[NSMutableArray allocWithZone:[self zone]] initWithCapacity:20];

    if (![NSBundle loadNibNamed:NIB_NAME owner: self])
    {
            [self autorelease];
            return nil;
    }

    previewMatrix = [[NSMatrix allocWithZone:[self zone]] initWithFrame:[previewBox bounds]
                                                                   mode:0 
                                                              cellClass:[PreviewCell class] 
                                                           numberOfRows:1 
                                                        numberOfColumns:5];
    [previewMatrix setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    [previewMatrix setAutosizesCells:YES];
    [previewMatrix setDelegate:self];
    [previewMatrix setIntercellSpacing:interCellSpacing];
    [(NSBox *)previewBox setContentView:previewMatrix];
    
    [firstEditor setDelta:0.0];
    [lastEditor setDelta:1.0];

    [[InspectorController sharedInspectorController] registerInspector:[MorphLineInspector sharedInspector]];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selectionChanged:) name:NOTIFICATION_SELECTION_CHANGED object:nil];

   if (!filePath)
        return self;

    [[self window] setTitle:[NSLocalizedString (@"Opening file ", @"Opening window title") stringByAppendingString:filePath]];

    infoDict = [[[NSDictionary allocWithZone:[self zone]] initWithContentsOfFile:[filePath stringByAppendingPathComponent:DICT_PATHCOMPONENT]] autorelease];
    if (!infoDict)
    {
        [self autorelease];
        return nil;
    }
    
    filename = [infoDict objectForKey:DICT_IMAGE0_PATH_KEY];
    if ([filename characterAtIndex:0] == '/')
        fullPath = filename;
    else
        fullPath = [filePath stringByAppendingPathComponent:filename];

    image = [[[NSImage allocWithZone:[self zone]] initWithContentsOfFile:fullPath] autorelease];
    [self setImage:image atDelta:0.0];

    filename = [infoDict objectForKey:DICT_IMAGE1_PATH_KEY];
    if ([filename characterAtIndex:0] == '/')
        fullPath = filename;
    else
        fullPath = [filePath stringByAppendingPathComponent:filename];

    image = [[[NSImage allocWithZone:[self zone]] initWithContentsOfFile:fullPath] autorelease];
    [self setImage:image atDelta:1.0];

    if ([infoDict objectForKey:@"frameWidth"])
        [frameWidthField setStringValue:[infoDict objectForKey:@"frameWidth"]];
    if ([infoDict objectForKey:@"frameHeight"])
        [frameHeightField setStringValue:[infoDict objectForKey:@"frameHeight"]];

    lineDefs = [infoDict objectForKey:DICT_MORPHLINES_KEY];

    for (i = 0; i < [lineDefs count]; ++i)
    {
        morphLineDict = [lineDefs objectAtIndex:i];
        morphLine = [[MorphLine allocWithZone:[self zone]] initWithValuesInDict:morphLineDict];
        if (morphLine != nil)
            [self addMorphLine:morphLine];
    }
    [[self window] setRepresentedFilename:path];
    [[self window] setDocumentEdited:NO];
    [self calcPreview:nil];

    if (filePath)
        [[self window] setTitleWithRepresentedFilename:filePath];

    return self;
}

- (void) selectionChanged:(NSNotification *)sender
{
    [firstEditor setNeedsDisplay:YES];
    [lastEditor setNeedsDisplay:YES];
    [previewMatrix setNeedsDisplay:YES];
}

- (NSArray *) morphLines
{
    return morphLines;
}

- (void) addMorphLine:(MorphLine *)morphLine
{
    [morphLines addObject:morphLine];
    [firstEditor setNeedsDisplay:YES];
    [lastEditor setNeedsDisplay:YES];
    [[self window] setDocumentEdited:YES];
}

- (void) deleteMorphLine:(MorphLine *)morphLine
{
    [morphLines removeObject:morphLine];
    [firstEditor setNeedsDisplay:YES];
    [lastEditor setNeedsDisplay:YES];
    [selectedLines removeObject:morphLine];
    [[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:NOTIFICATION_SELECTION_CHANGED object:self]
                                postingStyle:NSPostASAP coalesceMask:NSNotificationCoalescingOnName forModes:nil];

    [[self window] setDocumentEdited:YES];
}

// Access methods
- (void) setImage:(NSImage *)image atDelta:(float) delta
{
    NSSize	size = [image size];
    
    if (delta < 0.5)
    {
        [firstImage release];
        firstImage = [image retain];
    }
    else
    {
        [lastImage release];
        lastImage = [image retain];
    }
    [image setScalesWhenResized:YES];
    [frameWidthField setIntValue:size.width];
    [frameHeightField setIntValue:size.height];
    [[self window] setDocumentEdited:YES];
}

- (NSImage *) imageAtDelta:(float)delta
{
    if (delta < 0.5)
        return firstImage;
    else
        return lastImage;
}

- (NSWindow *)window
{
    return [firstEditor window];
}

// Actions
- (void) save:sender
{
    NSFileManager	*fileManager;
    NSString            *dirPath;
    NSString		*fullPath;
    NSMutableDictionary *dict;
    NSImageRep          *rep;
    NSEnumerator        *repEnum;
    NSImage             *im0;
    NSImage             *im1;
    NSData              *im0Data = nil;
    NSData              *im1Data = nil;

    if (filePath == nil)
        return [self saveAs:sender];

    // Create the info dict
    dict = [NSMutableDictionary dictionaryWithCapacity:10];
    
    // Add the morph lines to the dict
    [dict setObject:[self morphLines] forKey: DICT_MORPHLINES_KEY];

    // Get the NSData for the images
    im0 = [self imageAtDelta:0.0];
    im1 = [self imageAtDelta:1.0];

    repEnum = [[im0 representations] objectEnumerator];
    while ((rep = [repEnum nextObject]))
    {
        if ([rep respondsToSelector:@selector(EPSRepresentation)])
        {
            im0Data = [rep performSelector:@selector(EPSRepresentation)];
            [dict setObject:@"Image_0.eps"  forKey: DICT_IMAGE0_PATH_KEY];
            break;
        }
    }
    repEnum = [[im1 representations] objectEnumerator];
    while ((rep = [repEnum nextObject]))
    {
        if ([rep respondsToSelector:@selector(EPSRepresentation)])
        {
            im1Data = [rep performSelector:@selector(EPSRepresentation)];
            [dict setObject:@"Image_0.eps"  forKey: DICT_IMAGE1_PATH_KEY];
            break;
        }
    }
    if (im0Data == nil)
    {
        [dict setObject:@"Image_0.tiff"  forKey: DICT_IMAGE0_PATH_KEY];
        im0Data = [im0 TIFFRepresentation];
    }
    if (im1Data == nil)
    {
        [dict setObject:@"Image_1.tiff"  forKey: DICT_IMAGE1_PATH_KEY];
        im1Data = [im1 TIFFRepresentation];
    }

    [dict setObject:[NSString stringWithFormat:@"%i", [frameHeightField intValue]] forKey:@"frameHeight"];
    [dict setObject:[NSString stringWithFormat:@"%i", [frameWidthField intValue]] forKey:@"frameWidth"];
    
    // (Re)create the file package
    dirPath = [self filePath];
    fileManager = [NSFileManager defaultManager];
    [fileManager removeFileAtPath:filePath handler:nil];
    [fileManager createDirectoryAtPath:filePath attributes:nil];

    // Save the info plist
    fullPath = [filePath stringByAppendingPathComponent:DICT_PATHCOMPONENT];
    NSAssert1 ( [dict writeToFile:fullPath atomically:NO], @"Failed to write info dict %@", fullPath);

    // Save the image data
    fullPath = [dirPath stringByAppendingPathComponent:[dict objectForKey: DICT_IMAGE0_PATH_KEY]];
    NSAssert1 ( [im0Data writeToFile:fullPath atomically:NO], @"Failed to write image %@", fullPath);
    fullPath = [dirPath stringByAppendingPathComponent:[dict objectForKey: DICT_IMAGE1_PATH_KEY]];
    NSAssert1 ( [im1Data writeToFile:fullPath atomically:NO], @"Failed to write image %@", fullPath);

    [[self window] setDocumentEdited:NO];
}

- (void) saveAs:sender
{
    NSSavePanel *sp;
    NSString    *path;
    NSString    *fname;
    NSString    *dname;
    int          result;

    [[self window] setDocumentEdited:NO];
    path = [self filePath];
    if (path)
    {
        dname = [path stringByDeletingLastPathComponent];
        fname = [path lastPathComponent];
    }
    else
    {
        dname = NSHomeDirectory();
        fname = nil;
    }
    sp = [NSSavePanel savePanel];
    [sp setRequiredFileType: MORPH_DOC_EXTENTION];
    result = [sp runModalForDirectory:dname file:fname];
    if (result == NSOKButton)
    {
        path = [sp filename];
        [self setFilePath:[sp filename]];
        [self save:self];
    }
}

- (void) saveTo:sender
{
    [[self window] setDocumentEdited:NO];
    return;
}

- (void) saveFrames:sender
{
    NSString    *path;
    NSString    *fullPath;
    NSSavePanel *sp;
    NSString    *dname;
    NSString    *fname;
    NSMutableDictionary *dict;
    int			 result;
    NSFileManager	*fileManager;
    int          frameCount;
    NSSize		 frameSize;
    
    path = [self filePath];
    if (path)
    {
        dname = [path stringByDeletingLastPathComponent];
        fname = [[path lastPathComponent] stringByDeletingPathExtension];
    }
    else
    {
        dname = NSHomeDirectory();
        fname = nil;
    }

    sp = [NSSavePanel savePanel];
    [sp setRequiredFileType: XMOVIE_EXTENTION];
    [sp setAccessoryView:frameSettingsView];
    result = [sp runModalForDirectory:dname file:fname];
    if (result != NSOKButton)
        return;
    
    path = [sp filename];
    frameCount = [frameCountField intValue];
    frameSize.width = [frameWidthField floatValue];
    frameSize.height = [frameHeightField floatValue];
    
    // (Re)create the file package
    fileManager = [NSFileManager defaultManager];
    [fileManager removeFileAtPath:path handler:nil];
    [fileManager createDirectoryAtPath:path attributes:nil];

    // Save the info plist
    fullPath = [path stringByAppendingPathComponent:DICT_PATHCOMPONENT];
    dict = [NSMutableDictionary dictionaryWithCapacity:10];
    // Insert code to fill the dictionary with usefull information
    NSAssert1 ( [dict writeToFile:fullPath atomically:NO], @"Failed to write info dict %@", fullPath);

    [[self window] display];
    
    [self calcFramesAndSaveToPath:path count:frameCount size:frameSize];
    [[[NSApplication sharedApplication] delegate] application:[NSApplication sharedApplication] openFile:path];
}

- (void) selectAll:sender
{
    [selectedLines removeAllObjects];
    [selectedLines addObjectsFromArray:[self morphLines]];
    [[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:NOTIFICATION_SELECTION_CHANGED object:self]
                                postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
}

- (void) selectNone:sender
{
    [selectedLines removeAllObjects];
    [[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:NOTIFICATION_SELECTION_CHANGED object:self]
                                postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
}

- (void) addToCurrentSelection:(id)obj
{
    if (obj != nil && ![selectedLines containsObject:obj])
        [selectedLines addObject:obj];
    [[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:NOTIFICATION_SELECTION_CHANGED object:self]
                                postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
}

- (void) removeFromCurrentSelection:(id)obj
{
    if (obj != nil && [selectedLines containsObject:obj])
    {
        [selectedLines removeObject:obj];
        [[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:NOTIFICATION_SELECTION_CHANGED object:self]
                                    postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil];
    }
}

- (NSArray *) currentSelection
{
    return selectedLines;
}

- (void) calcPreview:sender
{
    NSBitmapImageRep	*firstBitmap;
    NSBitmapImageRep	*lastBitmap;
    NSBitmapImageRep	*bitmap;
    Morpher				*morpher;
    float		 		 delta;
    NSArray				*cells;
    PreviewCell			*cell;
    int					 i;
    NSSize				size;
    NSSize				winSize;

    firstBitmap = [[[self imageAtDelta:0] representations] lastObject];
    lastBitmap = [[[self imageAtDelta:1] representations] lastObject];
    cells = [previewMatrix cells];
    size = [previewMatrix cellSize];

    [progressIndicator setDoubleValue:0];
    winSize = size;
    winSize.height += 20;
    [progressIndicator setDoubleValue:0];
    [[progressIndicator window] setContentSize:winSize];
    [[progressIndicator window] makeKeyAndOrderFront:nil];
    [[progressIndicator window] display];

    morpher = [[Morpher allocWithZone:[self zone]] initWithFirstBitmap:firstBitmap lastBitmap:lastBitmap morphLines:morphLines];
    for (i = 0; i < [cells count]; ++i)
    {
        cell = [cells objectAtIndex:i];
        delta = i / (float) ([cells count] - 1.0);

        [cell setShouldDrawLines:YES];
        
        [progressIndicator setDoubleValue:delta];
        [progressIndicator display];
        
        bitmap = [[NSBitmapImageRep allocWithZone:[self zone]] initWithBitmapDataPlanes:NULL
                                                                             pixelsWide:size.width
                                                                             pixelsHigh:size.height
                                                                          bitsPerSample:8
                                                                        samplesPerPixel:3
                                                                               hasAlpha:NO
                                                                               isPlanar:NO
                                                                         colorSpaceName:NSDeviceRGBColorSpace
                                                                            bytesPerRow:(size.width + 1) * 3
                                                                           bitsPerPixel:24];

        [morpher calcMorphBitmap:bitmap atDelta:delta];
        [cell setBitmap:bitmap];
        [cell setDelta:delta];
        [cell setDelegate:self];
        [previewMatrix display];

        [progressView lockFocus];
        [bitmap draw];
        [progressView unlockFocus];
        [[progressView window] flushWindow];
        [[NSDPSContext currentContext] flush];

        [bitmap release];
    }
    [morpher release];
    [[progressIndicator window] orderOut:nil];

    return;
}

- (void) calcFramesAndSaveToPath:(NSString *)dirPath count:(unsigned int)frameCount size:(NSSize)frameSize
{
    NSBitmapImageRep	*firstBitmap;
    NSBitmapImageRep	*lastBitmap;
    NSBitmapImageRep	*bitmap;
    NSData				*data;
    Morpher				*morpher;
    float				 delta;
    NSString			*path;
    int					 frameNum = 0;
    NSSize				 winSize;

    winSize = frameSize;
    winSize.height += 20;
    [progressIndicator setDoubleValue:0];
    [[progressIndicator window] setContentSize:winSize];
    [[progressIndicator window] makeKeyAndOrderFront:nil];
    [[progressIndicator window] display];

    bitmap = [[NSBitmapImageRep allocWithZone:[self zone]] initWithBitmapDataPlanes:NULL pixelsWide:frameSize.width pixelsHigh:frameSize.height bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:(frameSize.width + 1) * 3 bitsPerPixel:24];
    firstBitmap = [[[self imageAtDelta:0] representations] lastObject];
    lastBitmap = [[[self imageAtDelta:1] representations] lastObject];

    morpher = [[Morpher allocWithZone:[self zone]] initWithFirstBitmap:firstBitmap lastBitmap:lastBitmap morphLines:morphLines];

    for (frameNum = 0; frameNum < frameCount; ++frameNum)
    {
        delta = frameNum / (float) (frameCount - 1.0);
        [progressIndicator setDoubleValue:delta];
        [progressIndicator display];

        [morpher calcMorphBitmap:bitmap atDelta:delta];

        [progressView lockFocus];
        [bitmap draw];
        [progressView unlockFocus];
        [[progressView window] flushWindow];
        [[NSDPSContext currentContext] flush];

        
        data = [bitmap TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:20];
        path = [dirPath stringByAppendingPathComponent:[NSString stringWithFormat:@"Image_%d.tiff", frameNum]];
        [data writeToFile:path atomically:NO];
    }
    [morpher release];
    [[progressIndicator window] orderOut:nil];
}

- (void)windowDidBecomeMain:(NSNotification *)notification
{
    [[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:NOTIFICATION_SELECTION_CHANGED object:self]
                                postingStyle:NSPostASAP coalesceMask:NSNotificationCoalescingOnName forModes:nil];
}
@end
