/*
 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#import "PrinterView.h"

#import "java_awt_print_Pageable.h"
#import "java_awt_print_PageFormat.h"

#import <JavaNativeFoundation/JavaNativeFoundation.h>

#import "ThreadUtilities.h"
#import "GeomUtilities.h"


static JNF_CLASS_CACHE(sjc_CPrinterJob, "sun/lwawt/macosx/CPrinterJob");
static JNF_CLASS_CACHE(sjc_PageFormat, "java/awt/print/PageFormat");

@implementation PrinterView

- (id)initWithFrame:(NSRect)aRect withEnv:(JNIEnv*)env withPrinterJob:(jobject)printerJob
{
    self = [super initWithFrame:aRect];
    if (self)
    {
        fPrinterJob = JNFNewGlobalRef(env, printerJob);
        fCurPageFormat = NULL;
        fCurPainter = NULL;
        fCurPeekGraphics = NULL;
    }
    return self;
}

- (void)releaseReferences:(JNIEnv*)env
{
    if (fCurPageFormat != NULL)
    {
        JNFDeleteGlobalRef(env, fCurPageFormat);
        fCurPageFormat = NULL;
    }
    if (fCurPainter != NULL)
    {
        JNFDeleteGlobalRef(env, fCurPainter);
        fCurPainter = NULL;
    }
    if (fCurPeekGraphics != NULL)
    {
        JNFDeleteGlobalRef(env, fCurPeekGraphics);
        fCurPeekGraphics = NULL;
    }
}

- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage {
    fFirstPage = firstPage;
    fLastPage = lastPage;
}

- (void)drawRect:(NSRect)aRect
{
    AWT_ASSERT_NOT_APPKIT_THREAD;

    static JNF_MEMBER_CACHE(jm_printToPathGraphics, sjc_CPrinterJob, "printToPathGraphics", "(Lsun/print/PeekGraphics;Ljava/awt/print/PrinterJob;Ljava/awt/print/Printable;Ljava/awt/print/PageFormat;IJ)V");

    // Create and draw into a new CPrinterGraphics with the current Context.
    assert(fCurPageFormat != NULL);
    assert(fCurPainter != NULL);
    assert(fCurPeekGraphics != NULL);

    JNIEnv* env = [ThreadUtilities getJNIEnvUncached];

    if ([self cancelCheck:env])
    {
        [self releaseReferences:env];
        return;
    }

    NSPrintOperation* printLoop = [NSPrintOperation currentOperation];
    jint jPageIndex = [printLoop currentPage] - 1;

    jlong context = ptr_to_jlong([printLoop context]);
    CGContextRef cgRef = (CGContextRef)[[printLoop context] graphicsPort];
    CGContextSaveGState(cgRef); //04/28/2004: state needs to be saved here due to addition of lazy state management

    JNFCallVoidMethod(env, fPrinterJob, jm_printToPathGraphics, fCurPeekGraphics, fPrinterJob, fCurPainter, fCurPageFormat, jPageIndex, context); // AWT_THREADING Safe (AWTRunLoop)

    CGContextRestoreGState(cgRef);

    [self releaseReferences:env];
}

- (NSString*)printJobTitle
{
    AWT_ASSERT_NOT_APPKIT_THREAD;

    static JNF_MEMBER_CACHE(jm_getJobName, sjc_CPrinterJob, "getJobName", "()Ljava/lang/String;");

    JNIEnv* env = [ThreadUtilities getJNIEnvUncached];

    jobject o = JNFCallObjectMethod(env, fPrinterJob, jm_getJobName); // AWT_THREADING Safe (known object)
    id result = JNFJavaToNSString(env, o);
    (*env)->DeleteLocalRef(env, o);
    return result;
}

- (BOOL)knowsPageRange:(NSRangePointer)aRange
{
    AWT_ASSERT_NOT_APPKIT_THREAD;

    JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
    if ([self cancelCheck:env])
    {
        return NO;
    }

    aRange->location = fFirstPage + 1;

    if (fLastPage == java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES)
    {
        aRange->length = NSIntegerMax;
    }
    else
    {
        aRange->length = (fLastPage + 1) - fFirstPage;
    }

    return YES;
}

- (NSRect)rectForPage:(NSInteger)pageNumber
{
    AWT_ASSERT_NOT_APPKIT_THREAD;

    static JNF_MEMBER_CACHE(jm_getPageformatPrintablePeekgraphics, sjc_CPrinterJob, "getPageformatPrintablePeekgraphics", "(I)[Ljava/lang/Object;");
    static JNF_MEMBER_CACHE(jm_printAndGetPageFormatArea, sjc_CPrinterJob, "printAndGetPageFormatArea", "(Ljava/awt/print/Printable;Ljava/awt/Graphics;Ljava/awt/print/PageFormat;I)Ljava/awt/geom/Rectangle2D;");
    static JNF_MEMBER_CACHE(jm_getOrientation, sjc_PageFormat, "getOrientation", "()I");

    // Assertions removed, and corresponding JNFDeleteGlobalRefs added, for radr://3962543
    // Actual fix that will keep these assertions from being true is radr://3205462 ,
    // which will hopefully be fixed by the blocking AppKit bug radr://3056694
    //assert(fCurPageFormat == NULL);
    //assert(fCurPainter == NULL);
    //assert(fCurPeekGraphics == NULL);

    JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
    if(fCurPageFormat != NULL) {
        JNFDeleteGlobalRef(env, fCurPageFormat);
    }
    if(fCurPainter != NULL) {
        JNFDeleteGlobalRef(env, fCurPainter);
    }
    if(fCurPeekGraphics != NULL) {
        JNFDeleteGlobalRef(env, fCurPeekGraphics);
    }

    //+++gdb Check the pageNumber for validity (PageAttrs)

    jint jPageNumber = pageNumber - 1;

    NSRect result;

    if ([self cancelCheck:env])
    {
        return NSZeroRect;
    }

    jobjectArray objectArray = JNFCallObjectMethod(env, fPrinterJob, jm_getPageformatPrintablePeekgraphics, jPageNumber); // AWT_THREADING Safe (AWTRunLoopMode)
    if (objectArray != NULL) {
        // Get references to the return objects -> PageFormat, Printable, PeekGraphics
        // Cheat - we know we either got NULL or a 3 element array
        jobject pageFormat = (*env)->GetObjectArrayElement(env, objectArray, 0);
        fCurPageFormat = JNFNewGlobalRef(env, pageFormat);
        (*env)->DeleteLocalRef(env, pageFormat);

        jobject painter = (*env)->GetObjectArrayElement(env, objectArray, 1);
        fCurPainter = JNFNewGlobalRef(env, painter);
        (*env)->DeleteLocalRef(env, painter);

        jobject peekGraphics = (*env)->GetObjectArrayElement(env, objectArray, 2);
        fCurPeekGraphics = JNFNewGlobalRef(env, peekGraphics);
        (*env)->DeleteLocalRef(env, peekGraphics);

        // Actually print and get the PageFormatArea
        jobject pageFormatArea = JNFCallObjectMethod(env, fPrinterJob, jm_printAndGetPageFormatArea, fCurPainter, fCurPeekGraphics, fCurPageFormat, jPageNumber); // AWT_THREADING Safe (AWTRunLoopMode)
        if (pageFormatArea != NULL) {
            NSPrintingOrientation currentOrientation =
                    [[[NSPrintOperation currentOperation] printInfo] orientation];
            // set page orientation
            switch (JNFCallIntMethod(env, fCurPageFormat, jm_getOrientation)) {
                case java_awt_print_PageFormat_PORTRAIT:
                default:
                    if (currentOrientation != NSPortraitOrientation) {
                        [[[NSPrintOperation currentOperation] printInfo]
                                            setOrientation:NSPortraitOrientation];
                    }
                    break;

                case java_awt_print_PageFormat_LANDSCAPE:
                case java_awt_print_PageFormat_REVERSE_LANDSCAPE:
                    if (currentOrientation != NSLandscapeOrientation) {
                        [[[NSPrintOperation currentOperation] printInfo]
                                            setOrientation:NSLandscapeOrientation];
                    }
                    break;
                }
            result = JavaToNSRect(env, pageFormatArea);
            (*env)->DeleteLocalRef(env, pageFormatArea);
        } else {
            [self releaseReferences:env];
            result = NSZeroRect;
        }

        (*env)->DeleteLocalRef(env, objectArray);
    } else {
        [self releaseReferences:env];
        result = NSZeroRect;
    }

    return result;
}

- (BOOL)cancelCheck:(JNIEnv*)env
{
    AWT_ASSERT_NOT_APPKIT_THREAD;

    static JNF_MEMBER_CACHE(jm_cancelCheck, sjc_CPrinterJob, "cancelCheck", "()Z");

    return JNFCallBooleanMethod(env, fPrinterJob, jm_cancelCheck); // AWT_THREADING Safe (known object)
}

// This is called by -[PrintModel safePrintLoop]
- (void)complete:(JNIEnv*)env
{
    AWT_ASSERT_NOT_APPKIT_THREAD;

    static JNF_MEMBER_CACHE(jf_completePrintLoop, sjc_CPrinterJob, "completePrintLoop", "()V");
    JNFCallVoidMethod(env, fPrinterJob, jf_completePrintLoop);

    // Clean up after ourselves
    // Can't put these into -dealloc since that happens (potentially) after the JNIEnv is stale
    [self releaseReferences:env];
    if (fPrinterJob != NULL)
    {
        JNFDeleteGlobalRef(env, fPrinterJob);
        fPrinterJob = NULL;
    }
}

- (BOOL)isFlipped
{
    return TRUE;
}

@end
