/*
    PPExportPanelAccessoryViewController.m

    Copyright 2013-2017 Josh Freeman
    http://www.twilightedge.com

    This file is part of PikoPixel for Mac OS X and GNUstep.
    PikoPixel is a graphical application for drawing & editing pixel-art images.

    PikoPixel is free software: you can redistribute it and/or modify it under
    the terms of the GNU Affero General Public License as published by the
    Free Software Foundation, either version 3 of the License, or (at your
    option) any later version approved for PikoPixel by its copyright holder (or
    an authorized proxy).

    PikoPixel 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 Affero General Public License for more
    details.

    You should have received a copy of the GNU Affero General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#import "PPExportPanelAccessoryViewController.h"

#import "PPDocument.h"
#import "NSImage_PPUtilities.h"
#import "NSTextField_PPUtilities.h"
#import "PPGeometry.h"


#define kExportPanelAccessoryViewNibName                @"ExportPanelAccessoryView"

#define kMinExportScalingFactor                         1
#define kMaxExportScalingFactor                         (kMaxCanvasZoomFactor + 10.0f)

#define kMarginPaddingForScrollerlessScrollView         2


@interface PPExportPanelAccessoryViewController (PrivateMethods)

- (id) initWithPPDocument: (PPDocument *) ppDocument;

- (void) addAsObserverForNSViewNotificationsFromPreviewClipView;
- (void) removeAsObserverForNSViewNotificationsFromPreviewClipView;
- (void) handlePreviewClipViewNotification_BoundsDidChange: (NSNotification *) notification;

- (void) setupMaxScalingFactorForCurrentDocumentSize;

- (void) setSavePanelRequiredFileTypeFromFileFormatPopUp;

- (void) updateBackgroundImageCheckboxVisibility;
- (void) updateGridButtonVisibility;

- (void) updatePreviewImage;
- (void) clearPreviewImage;
- (void) resizePreviewViewForImageWithSize: (NSSize) previewImageSize;
- (void) scrollPreviewToNormalizedCenter;

@end


#if PP_SDK_REQUIRES_PROTOCOLS_FOR_DELEGATES_AND_DATASOURCES

@interface PPExportPanelAccessoryViewController (RequiredProtocols) <NSTextFieldDelegate>
@end

#endif  // PP_SDK_REQUIRES_PROTOCOLS_FOR_DELEGATES_AND_DATASOURCES


@implementation PPExportPanelAccessoryViewController

+ (PPExportPanelAccessoryViewController *) controllerForPPDocument: (PPDocument *) ppDocument
{
    return [[[self alloc] initWithPPDocument: ppDocument] autorelease];
}

- (id) initWithPPDocument: (PPDocument *) ppDocument
{
    self = [super init];

    if (!self)
        goto ERROR;

    if (!ppDocument || PPGeometry_IsZeroSize([ppDocument canvasSize]))
    {
        goto ERROR;
    }

    if (![NSBundle loadNibNamed: kExportPanelAccessoryViewNibName owner: self])
    {
        goto ERROR;
    }

    _ppDocument = ppDocument;   // unretained to prevent retain loop

    _previewScrollViewInitialFrame = [[_previewImageView enclosingScrollView] frame];
    _previewScrollerWidth =
                _previewScrollViewInitialFrame.size.width
                    - [[[_previewImageView enclosingScrollView] contentView] frame].size.width;
    _previewViewNormalizedCenter = NSMakePoint(0.5f, 0.5f);

    [self addAsObserverForNSViewNotificationsFromPreviewClipView];

    _scalingFactor = kMinExportScalingFactor;
    _maxScalingFactor = kMaxExportScalingFactor;

    [_scalingFactorTextField setIntValue: _scalingFactor];
    [_scalingFactorTextField setDelegate: self];

    [_scalingFactorSlider setMinValue: kMinExportScalingFactor];
    [_scalingFactorSlider setMaxValue: _maxScalingFactor];
    [_scalingFactorSlider setIntValue: _scalingFactor];

    return self;

ERROR:
    [self release];

    return nil;
}

- (id) init
{
    return [self initWithPPDocument: nil];
}

- (void) dealloc
{
    [self setupWithSavePanel: nil];

    [self removeAsObserverForNSViewNotificationsFromPreviewClipView];

    [_exportAccessoryView release];

    [super dealloc];
}

- (void) setupWithSavePanel: (NSSavePanel *) savePanel
{
    if (_savePanel)
    {
        [_savePanel setAccessoryView: nil];

        [_savePanel release];
        _savePanel = nil;
    }

    if (savePanel)
    {
        _savePanel = [savePanel retain];

        [self setupMaxScalingFactorForCurrentDocumentSize];

        [self updateBackgroundImageCheckboxVisibility];
        [self updateGridButtonVisibility];

        [self updatePreviewImage];

        [_savePanel setAccessoryView: _exportAccessoryView];

        [self setSavePanelRequiredFileTypeFromFileFormatPopUp];
    }
    else
    {
        [self clearPreviewImage];
    }
}

- (NSString *) selectedFileTypeName
{
    return [[_fileFormatPopUpButton selectedItem] title];
}

- (NSString *) selectedFileType
{
    return [[_fileFormatPopUpButton selectedItem] toolTip];
}

- (bool) getScalingFactor: (unsigned *) returnedScalingFactor
            backgroundPatternFlag: (bool *) returnedBackgroundPatternFlag
            backgroundImageFlag: (bool *) returnedBackgroundImageFlag
            andGridFlag: (bool *) returnedGridFlag;
{
    if (!returnedScalingFactor || !returnedBackgroundPatternFlag
        || !returnedBackgroundImageFlag || !returnedGridFlag)
    {
        goto ERROR;
    }

    *returnedScalingFactor = _scalingFactor;

    *returnedBackgroundPatternFlag = ([_backgroundPatternCheckbox intValue]) ? YES : NO;

    *returnedBackgroundImageFlag = ([_backgroundImageCheckbox intValue]) ? YES : NO;

    *returnedGridFlag = ([_gridCheckbox intValue]) ? YES : NO;

    return YES;

ERROR:
    return NO;
}

#pragma mark Actions

- (IBAction) scalingFactorSliderMoved: (id) sender
{
    int sliderValue = [_scalingFactorSlider intValue];

    if (_scalingFactor != sliderValue)
    {
        _scalingFactor = sliderValue;
        [_scalingFactorTextField setIntValue: _scalingFactor];

        [self updatePreviewImage];
        [self updateGridButtonVisibility];
    }
}

- (IBAction) canvasSettingCheckboxClicked: (id) sender
{
    [self updatePreviewImage];
}

- (IBAction) fileFormatPopupMenuItemSelected: (id) sender
{
    [self setSavePanelRequiredFileTypeFromFileFormatPopUp];
}

#pragma mark NSControl delegate methods (Scaling factor textField)

- (void) controlTextDidChange: (NSNotification *) notification
{
    int newScalingFactor = [_scalingFactorTextField ppClampIntValueToMax: _maxScalingFactor
                                                        min: kMinExportScalingFactor
                                                        defaultValue: _scalingFactor];

    if (newScalingFactor != _scalingFactor)
    {
        _scalingFactor = newScalingFactor;

        [_scalingFactorSlider setIntValue: _scalingFactor];

        [self updatePreviewImage];
        [self updateGridButtonVisibility];
    }
}

#pragma mark NSView notifications (Preview imageview’s clipview)

- (void) addAsObserverForNSViewNotificationsFromPreviewClipView
{
    NSClipView *clipView = [[_previewImageView enclosingScrollView] contentView];

    if (!clipView)
        return;

    [clipView setPostsBoundsChangedNotifications: YES];

    [[NSNotificationCenter defaultCenter]
                                    addObserver: self
                                    selector:
                                        @selector(
                                            handlePreviewClipViewNotification_BoundsDidChange:)
                                    name: NSViewBoundsDidChangeNotification
                                    object: clipView];
}

- (void) removeAsObserverForNSViewNotificationsFromPreviewClipView
{
    NSClipView *clipView = [[_previewImageView enclosingScrollView] contentView];

    [[NSNotificationCenter defaultCenter] removeObserver: self
                                            name: NSViewBoundsDidChangeNotification
                                            object: clipView];
}

- (void) handlePreviewClipViewNotification_BoundsDidChange: (NSNotification *) notification
{
    NSRect documentVisibleRect;

    if (_shouldPreservePreviewNormalizedCenter || PPGeometry_IsZeroSize(_previewImageSize))
    {
        return;
    }

    documentVisibleRect = [[_previewImageView enclosingScrollView] documentVisibleRect];

    _previewViewNormalizedCenter =
        NSMakePoint(((documentVisibleRect.origin.x + documentVisibleRect.size.width / 2.0)
                            / _previewImageSize.width),
                        ((documentVisibleRect.origin.y + documentVisibleRect.size.height / 2.0)
                            / _previewImageSize.height));
}

#pragma mark Private methods

- (void) setupMaxScalingFactorForCurrentDocumentSize
{
    NSSize canvasSize;
    int maxScalingFactor;

    canvasSize = [_ppDocument canvasSize];

    if (PPGeometry_IsZeroSize(canvasSize))
    {
        goto ERROR;
    }

    maxScalingFactor =
            MIN(kMaxExportScalingFactor,
                floorf(kMaxCanvasExportDimension / MAX(canvasSize.width, canvasSize.height)));

    if (_maxScalingFactor != maxScalingFactor)
    {
        _maxScalingFactor = maxScalingFactor;

        if (_scalingFactor > _maxScalingFactor)
        {
            _scalingFactor = _maxScalingFactor;

            [_scalingFactorTextField setIntValue: _scalingFactor];
        }

        [_scalingFactorSlider setMaxValue: _maxScalingFactor];
        [_scalingFactorSlider setIntValue: _scalingFactor];
    }

    return;

ERROR:
    return;
}

- (void) setSavePanelRequiredFileTypeFromFileFormatPopUp
{
    NSString *selectedFileType;
    NSArray *allowedFileTypes = nil;

    selectedFileType = [self selectedFileType];

    if ([selectedFileType length])
    {
        allowedFileTypes = [NSArray arrayWithObject: selectedFileType];
    }

    [_savePanel setAllowedFileTypes: allowedFileTypes];
}

- (void) updateBackgroundImageCheckboxVisibility
{
    bool documentHasBackgroundImage = ([_ppDocument backgroundImage]) ? YES : NO;

    [_backgroundImageCheckbox setEnabled: documentHasBackgroundImage];

    if (!documentHasBackgroundImage)
    {
        [_backgroundImageCheckbox setIntValue: 0];
    }
}

- (void) updateGridButtonVisibility
{
    bool enableGridButton = (_scalingFactor >= kMinScalingFactorToDrawGrid) ? YES : NO;

    [_gridCheckbox setEnabled: enableGridButton];
}

- (void) updatePreviewImage
{
    NSAutoreleasePool *autoreleasePool;
    NSBitmapImageRep *previewBitmap;
    NSImage *previewImage;
    NSSize previewImageSize;

    // use a local autorelease pool to make sure old images & bitmaps get dealloc'd during
    // slider tracking
    autoreleasePool = [[NSAutoreleasePool alloc] init];

    previewBitmap = [_ppDocument mergedVisibleLayersBitmapUsingExportPanelSettings];
    previewImage = [NSImage ppImageWithBitmap: previewBitmap];
    previewImageSize = (previewImage) ? [previewImage size] : NSZeroSize;

    if (!NSEqualSizes(previewImageSize, _previewImageSize))
    {
        [_exportSizeTextField setStringValue: [NSString stringWithFormat: @"%dx%d",
                                                                (int) previewImageSize.width,
                                                                (int) previewImageSize.height]];

        [self resizePreviewViewForImageWithSize: previewImageSize];
    }

    [_previewImageView setImage: previewImage];

    [autoreleasePool release];
}

- (void) clearPreviewImage
{
    [_previewImageView setImage: nil];
}

- (void) resizePreviewViewForImageWithSize: (NSSize) previewImageSize
{
    NSScrollView *previewScrollView;
    NSSize contentViewSize;
    NSRect newPreviewScrollViewFrame;
    int viewMarginPadding;

    _shouldPreservePreviewNormalizedCenter = YES;

    previewScrollView = [_previewImageView enclosingScrollView];

    [_previewImageView setFrameSize: previewImageSize];
    [previewScrollView setFrame: _previewScrollViewInitialFrame];

    contentViewSize = [[previewScrollView contentView] frame].size;

    newPreviewScrollViewFrame = _previewScrollViewInitialFrame;

    if (previewImageSize.width < contentViewSize.width)
    {
        if (previewImageSize.height > contentViewSize.height)
        {
            viewMarginPadding = _previewScrollerWidth;
        }
        else
        {
            viewMarginPadding = kMarginPaddingForScrollerlessScrollView;
        }

        newPreviewScrollViewFrame.size.width = previewImageSize.width + viewMarginPadding;
    }

    if (previewImageSize.height < contentViewSize.height)
    {
        if (previewImageSize.width > contentViewSize.width)
        {
            viewMarginPadding = _previewScrollerWidth;
        }
        else
        {
            viewMarginPadding = kMarginPaddingForScrollerlessScrollView;
        }

        newPreviewScrollViewFrame.size.height = previewImageSize.height + viewMarginPadding;
    }

    newPreviewScrollViewFrame =
        PPGeometry_CenterRectInRect(newPreviewScrollViewFrame, _previewScrollViewInitialFrame);

    [previewScrollView setFrame: newPreviewScrollViewFrame];

    // Changing scrollview frame causes drawing artifacts (10.4) - fix by redrawing superview
    [[previewScrollView superview] setNeedsDisplayInRect: _previewScrollViewInitialFrame];

    _previewImageSize = previewImageSize;

    [self scrollPreviewToNormalizedCenter];

    _shouldPreservePreviewNormalizedCenter = NO;
}

- (void) scrollPreviewToNormalizedCenter
{
    NSSize clipViewSize = [[[_previewImageView enclosingScrollView] contentView] bounds].size;
    NSPoint centerPoint = NSMakePoint(_previewViewNormalizedCenter.x * _previewImageSize.width
                                        - clipViewSize.width / 2.0f,
                                    _previewViewNormalizedCenter.y * _previewImageSize.height
                                        - clipViewSize.height / 2.0f);

    [_previewImageView scrollPoint: centerPoint];
}

@end
