//
//  MyDocument.m
//  PRICE
//
//  Created by Riccardo Mottola on Thu Dec 12 2002.
//  Copyright (c) 2002-2005 Carduus. All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under the terms of the version 2 of the GNU General Public License as published by the Free Software Foundation.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

#import "MyDocument.h"
#import <limits.h>

#import "PRGrayscaleFilter.h"
#import "PRConvolve55.h"
#import "PRTransforms.h"
#import "PRFourier.h"
#import "PRDFTLowPass.h"
#import "PRDFTHighPass.h"
#import "PREqualize.h"
#import "PRTraceEdges.h"
#import "PRCustTraceEdges.h"
#import "PRMedian.h"
#import "PRScale.h"
#import "PRCrop.h"
#import "PRBriCon.h"

@implementation MyDocument

// eliminated windowNibName since we use makeWindowControllers


- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
    [super windowControllerDidLoadNib:aController];
    // Add any code here that need to be executed once the windowController has loaded the document's window.
}

- (NSData *)dataRepresentationOfType:(NSString *)aType
{
    // Insert code here to write your document from the given data.  You can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead.
//    return nil;
    return [activeImage TIFFRepresentation];
}

- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
{
    // Insert code here to read your document from the given data.  You can also choose to override -loadFileWrapperRepresentation:ofType: or -readFromFile:ofType: instead.
    PRImage *tempImage;
    
    tempImage = [[PRImage alloc] initWithData:data];
    if (tempImage != nil)
    {
        NSBitmapImageRep *tmpImageRep;
        PRImage *newImage;
        NSBitmapImageRep *newImageRep;
        unsigned char *dataPtr;
        unsigned char *dataPtr2;
        unsigned int k;
        unsigned int w, h, s;

        /* if the loaded image is in BlackColorSpace we convert it to WhiteColorSpace */
        /* which is the only TIFF rep used internally and generated by PRICE */
        tmpImageRep = [[tempImage representations] objectAtIndex: 0];
        w = [tmpImageRep pixelsWide];
        h = [tmpImageRep pixelsHigh];
        if ([[tmpImageRep colorSpaceName] isEqualToString: NSCalibratedBlackColorSpace] || [[tmpImageRep colorSpaceName] isEqualToString: NSDeviceBlackColorSpace])
        {
            /* converting the colorspace */
            newImage = [[PRImage alloc] initWithSize:NSMakeSize(w, h)];
            newImageRep = [[NSBitmapImageRep alloc]
                    initWithBitmapDataPlanes:NULL
                    pixelsWide:w
                    pixelsHigh:h
                    bitsPerSample:8
                    samplesPerPixel:1
                    hasAlpha:NO
                    isPlanar:NO
                    colorSpaceName:NSCalibratedWhiteColorSpace
                    bytesPerRow:0
                    bitsPerPixel:0];
            s = w * h;
            dataPtr = [tmpImageRep bitmapData];
            dataPtr2 = [newImageRep bitmapData];
            for (k = 0; k < s; k++)
                *dataPtr2++ = UCHAR_MAX - *dataPtr++;
            [newImageRep setColorSpaceName:NSCalibratedWhiteColorSpace];
            [newImage addRepresentation:newImageRep];
            [newImageRep release];
            [tempImage release];
            tempImage = [newImage retain];
        }
    }

    oldImage = nil;
    /* returns a bool to be able to know if loading was successul */
    if (tempImage != nil)
    {
        /* we setActiveImage won't set the image info yet (why?)  */
        [self setActiveImage:tempImage];
        [tempImage release];
        return YES;
    } else
        return NO;
}


- (IBAction)saveDocumentAs:(id)sender
/* we override the super class to change the extensionand type of the file */
{
    NSString *filepath;
    
    filepath = [self fileName];
    filepath = [[filepath stringByDeletingPathExtension] stringByAppendingString: @".tiff"];
    [self setFileName:filepath];
    [self setFileType: @"tiff"];
    [super saveDocumentAs: sender];
}

- (void)makeWindowControllers
/* instantiate PRWindowController */
{
    windowController = [[PRWindowController alloc] initWithWindowNibName:@"PRWindow"];
    [self addWindowController:windowController];
    [windowController release];
    
    /* set undo levels */
    [[self undoManager] setLevelsOfUndo:1];
}

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

- (NSView *)view
{
    return [windowController view];
}

- (PRImage *)activeImage
/* method to access the active image */
{
    return activeImage;
}

- (void)setActiveImage: (PRImage *)theImage
/* method to set the active image */
{
    if (activeImage != nil)
        [activeImage release];
    activeImage = [theImage retain];
    /* window controller is still nil here the first time we load an image
     * thus the info must be manually set after the nib finished loading */
    [windowController setImageToDraw:activeImage];
}

- (void)copy:(id)sender
{
    NSPasteboard *pboard;

    pboard = [NSPasteboard generalPasteboard];

    [pboard declareTypes:[NSArray arrayWithObjects:NSTIFFPboardType, nil] owner:nil];
    [pboard setData:[activeImage TIFFRepresentation] forType:NSTIFFPboardType];
}

- (void)paste:(id)sender
{
    NSUndoManager *uMgr;
    NSPasteboard  *pboard;
    NSString      *type;
    NSData        *tempData;
    PRImage       *tempImage;

    pboard = [NSPasteboard generalPasteboard];
    type = [pboard availableTypeFromArray:[NSArray arrayWithObjects:NSTIFFPboardType, nil]];

    if (type != nil)
    {
        if ([type isEqualToString:NSTIFFPboardType])
        {
            /* get the clipboard data */
            tempData = [pboard dataForType:NSTIFFPboardType];
            if (tempData != nil)
            {
                uMgr = [self undoManager];
                /* save the method on the undo stack */
                [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
                [uMgr setActionName:@"Paste"];

                /* save the current image */
                [self saveCurrentImage];

                tempImage = [[PRImage alloc] initWithData:tempData];
                [self setActiveImage: tempImage];
                [tempImage release];
                [[windowController view] setFrameSize:[activeImage size]];
                [[windowController view] setNeedsDisplay:YES];
            } else
            {
                /* guidelines say I should put a panel */
                /* #### fixme */
                NSLog(@"something went wrong in paste");
            }
        } else
            NSLog(@"received a paste of unhandled type: %@", type);
    }
}

- (void)makeGrayscale:(id)sender
{
    PRGrayscaleFilter *filter;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Make Grayscale"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    filter = [[PRGrayscaleFilter alloc] init];
    [self setActiveImage: [filter filterImage:activeImage]];
    [filter release];
}

- (void)makeConvolve55:(int[5][5])mat: (int)offset :(float)scale :(BOOL)autoScale :(PRCProgress *)prPan
{
    PRConvolve55 *conv;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Convolve 5x5"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    conv = [[PRConvolve55 alloc] init];    
    [self setActiveImage: [conv convolveImage:activeImage :mat :offset :scale :autoScale :prPan]];
    [conv release];
}

- (void)makeDFT:(id)sender
{
    PRFourier *fourier;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"FFT"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    fourier = [[PRFourier alloc] init];
    [self setActiveImage: [fourier transformImage:activeImage]];
    [fourier release];
}

- (void)makeDFTLowPass :(BOOL)autoRange :(float) bandPassFreq :(float) bandStopFreq :(PRCProgress *)prPan
{
    PRDFTLowPass *dftLP;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"DFT LowPass"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    dftLP = [[PRDFTLowPass alloc] init];
    [self setActiveImage: [dftLP transformImage:activeImage :autoRange :bandPassFreq :bandStopFreq :prPan]];
    [dftLP release];
}

- (void)makeDFTHighPass :(BOOL)autoRange :(float) bandPassFreq :(float) bandStopFreq :(PRCProgress *)prPan
{
    PRDFTHighPass *dftHP;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"DFT HighPass"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    dftHP = [[PRDFTHighPass alloc] init];
    [self setActiveImage: [dftHP transformImage:activeImage :autoRange :bandPassFreq :bandStopFreq :prPan]];
    [dftHP release];
}

- (void)makeTransposed:(id)sender
{
    PRTransforms *tr;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Transpose"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    tr = [[PRTransforms alloc] init];
    [self setActiveImage: [tr transposeImage:activeImage]];
    [tr release];
    [[windowController view] setFrameSize:[activeImage size]];
    [[windowController view] setNeedsDisplay:YES];
}

- (void)makeRotated90 :(id)sender
{
    PRTransforms *tr;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Rotate 90"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    tr = [[PRTransforms alloc] init];
    [self setActiveImage: [tr rotateImage90:activeImage]];
    [tr release];
    [[windowController view] setFrameSize:[activeImage size]];
    [[windowController view] setNeedsDisplay:YES];
}

- (void)makeRotated180 :(id)sender
{
    PRTransforms *tr;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Rotate 180"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    tr= [[PRTransforms alloc] init];
    [self setActiveImage: [tr rotateImage180:activeImage]];
    [tr release];
}

- (void)makeRotated270 :(id)sender
{
    PRTransforms *tr;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Rotate 270"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    tr = [[PRTransforms alloc] init];
    [self setActiveImage: [tr rotateImage270:activeImage]];
    [tr release];
    [[windowController view] setFrameSize:[activeImage size]];
    [[windowController view] setNeedsDisplay:YES];
}

- (void)makeFlippedVert :(id)sender
{
    PRTransforms *tr;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Flip Vertical"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    
    tr = [[PRTransforms alloc] init];
    [self setActiveImage: [tr flipImageVert:activeImage]];
    [tr release];
}

- (void)makeFlippedHoriz :(id)sender
{
    PRTransforms *tr;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Flip Horizontal"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    
    tr = [[PRTransforms alloc] init];
    [self setActiveImage: [tr flipImageHoriz:activeImage]];
    [tr release];
}

- (void)makeEqualization :(int)colorSpace :(id)sender
{
    PREqualize *equal;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Equalize"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    equal = [[PREqualize alloc] init];
    [self setActiveImage: [equal equalizeImage:activeImage :colorSpace]];
    [equal release];
}

- (void)makeBriCon :(int)bri :(float)con
{
    PRBriCon *briCon;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Brightness & Contrast"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    briCon = [[PRBriCon alloc] init];
    [self setActiveImage: [briCon adjustImage :activeImage :bri :con]];
    [briCon release];
}


- (void)makeTraceEdges :(int)filterType :(BOOL)useTh :(float)thLev :(BOOL)doZeroC
{
    PRTraceEdges *traceEdges;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Trace Edges"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    traceEdges = [[PRTraceEdges alloc] init];
    [self setActiveImage: [traceEdges edgeImage :activeImage :filterType :useTh :thLev :doZeroC]];
    [traceEdges release];
}

- (void)makeCustTraceEdges :(int)filterType :(float)thLev :(BOOL)doZeroC :(BOOL)do1 :(enum medianForms)form1 :(int)size1 :(BOOL)sep1 :(BOOL)do2 :(enum medianForms)form2 :(int)size2 :(BOOL)sep2 :(BOOL)do3 :(enum medianForms)form3 :(int)size3 :(BOOL)sep3 :(PRCProgress *)prPan
{
    PRCustTraceEdges *traceEdges;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Custom Trace Edges"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    traceEdges = [[PRCustTraceEdges alloc] init];
    [self setActiveImage: [traceEdges edgeImage :activeImage :filterType :thLev :doZeroC :do1 :form1 :size1 :sep1 :do2 :form2 :size2 :sep2 :do3 :form3 :size3 :sep3 :prPan]];
    [traceEdges release];
}

- (void)makeMedian :(enum medianForms)form :(int)size :(BOOL)sep :(PRCProgress *)prPan
{
    PRMedian *median;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Median"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    median = [[PRMedian alloc] init];
    [self setActiveImage: [median medianImage :activeImage :form :size :sep :prPan]];
    [median release];
}

- (void)makeScale :(int)sizeX :(int)sizeY :(int)method :(PRCProgress *)prPan
{
    PRScale *scale;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Scale"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    scale = [[PRScale alloc] init];
    [self setActiveImage: [scale scaleImage :activeImage :sizeX :sizeY :method :prPan]];
    [scale release];
    [[windowController view] setFrameSize:[activeImage size]];
    [[windowController view] setNeedsDisplay:YES];
}

- (void)makeCrop :(int)sizeTop :(int)sizeBottom :(int)sizeLeft :(int)sizeRight
{
    PRCrop *crop;
    NSUndoManager *uMgr;
    
    uMgr = [self undoManager];
    /* save the method on the undo stack */
    [[uMgr prepareWithInvocationTarget: self] restoreLastImage];
    [uMgr setActionName:@"Crop"];
    
    /* save the current image */
    [self saveCurrentImage];
    
    /* instantiate and run the filter */
    crop = [[PRCrop alloc] init];
    [self setActiveImage: [crop cropImage :activeImage :sizeTop :sizeBottom :sizeRight :sizeLeft]];
    [crop release];
    [[windowController view] setFrameSize:[activeImage size]];
    [windowController scaleImage];
    [[windowController view] setNeedsDisplay:YES];
}

- (void)restoreLastImage
{
    PRImage *tempImage;
    
    tempImage = [[PRImage alloc] initWithData: [activeImage TIFFRepresentation]];
    [self setActiveImage: oldImage];
    [oldImage release];
    oldImage = tempImage;
    [[[self undoManager] prepareWithInvocationTarget: self] restoreLastImage];
    [[windowController view] setFrameSize:[activeImage size]];
    [windowController scaleImage];
    [[windowController view] setNeedsDisplay:YES];
}

- (void)saveCurrentImage
{
    if (activeImage != nil)
    {
        if (oldImage != nil)
            [oldImage release];
        oldImage = [[PRImage alloc] initWithData: [activeImage TIFFRepresentation]];
    }
}

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

@end
