/*
 * Copyright (c) 2012, 2015, 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.
 */

#include "jni_utils.h"
#include "com_sun_javafx_iio_ios_IosImageLoader.h"

#import "ImageLoader.h"

#include "debug.h"

#define BUFFER_SIZE 8 * 1024


// NOTE:
// It seems to be inefficient to decode the stream progressively.
// Thus we download the input stream to memory at once and decompress it then.

// Maybe we could get motivated here:
//http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/


#ifdef __cplusplus
extern "C" {
#endif

    static jmethodID InputStream_readID;
    static jmethodID InputStream_skipID;
    static jmethodID IosImageLoader_setInputParametersID;
    static jmethodID IosImageLoader_updateImageProgressID;

    JNIEXPORT jint JNICALL
    JNI_OnLoad_nativeiio(JavaVM *vm, void *reserved) {
#ifdef JNI_VERSION_1_8
    //min. returned JNI_VERSION required by JDK8 for builtin libraries
    JNIEnv *env;
    if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_8) != JNI_OK) {
        return JNI_VERSION_1_2;
    }
    return JNI_VERSION_1_8;
#else
    return JNI_VERSION_1_2;
#endif
    }


    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    initNativeLoading
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_initNativeLoading
    (JNIEnv *env, jclass klazz) {

        IIOLog(@"IosImageLoader_initNativeLoading");

        jclass cls_InputStream = (*env)->FindClass(env, "java/io/InputStream");
        if (cls_InputStream == NULL) {

            throwException(env,
                           "java/lang/ClassNotFoundException",
                           "Cannot find class java.io.InputStream");

            return;
        }

        InputStream_readID = (*env)->GetMethodID(env,
                                                 cls_InputStream,
                                                 "read",
                                                 "([BII)I");
        InputStream_skipID = (*env)->GetMethodID(env,
                                                 cls_InputStream,
                                                 "skip",
                                                 "(J)J");

        IosImageLoader_setInputParametersID = (*env)->GetMethodID(env,
                                                                  klazz,
                                                                  "setInputParameters",
                                                                  "(IIII)V");
        IosImageLoader_updateImageProgressID = (*env)->GetMethodID(env,
                                                                   klazz,
                                                                   "updateProgress",
                                                                   "(F)V");

        (*env)->DeleteLocalRef(env, cls_InputStream);
    }

    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    loadImage
     * Signature: (Ljava/lang/String;Z)J
     */
    JNIEXPORT jlong JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_loadImageFromURL
    (JNIEnv *env, jobject obj, jstring jStringURL, jboolean reportProgress) {

        IIOLog(@"IosImageLoader_loadImageFromURL");

        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        ImageLoader *loader = [[ImageLoader alloc] init];

        const char *urlNativeString = (*env)->GetStringUTFChars(env, jStringURL, 0);
        NSString *url = [NSString stringWithCString : urlNativeString
                                           encoding : NSUTF8StringEncoding];
        (*env)->ReleaseStringUTFChars(env, jStringURL, urlNativeString);

        IIOLog(@"Image URL: %@", url);

        BOOL isLoaded = [loader loadFromURL : url
                                     JNIEnv : env];

        if (!isLoaded) {
            [pool release];
            return 0L;
        }

        (*env)->CallVoidMethod(env,
                               obj,
                               IosImageLoader_setInputParametersID,
                               (jint) [loader width],
                               (jint) [loader height],
                               (jint) [loader nImages],
                               (jint) [loader loopCount]);

        (*env)->CallVoidMethod(env,
                               obj,
                               IosImageLoader_updateImageProgressID,
                               100.0f);

        [pool release];

        return ptr_to_jlong(loader);

    }

    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    loadImage
     * Signature: (Ljava/io/InputStream;Z)J
     */
    JNIEXPORT jlong JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_loadImage
    (JNIEnv *env, jobject obj, jobject inputStream, jboolean reportProgress) {

        IIOLog(@"IosImageLoader_loadImage");

        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        jbyteArray buffer = (*env)->NewByteArray(env, BUFFER_SIZE);
        if (buffer == NULL) {
            throwException(env,
                           "java/lang/OutOfMemoryError",
                           "Cannot initilialize memory buffer for native image loader");

            [pool release];
            return 0L;
        }

        ImageLoader *loader = [[ImageLoader alloc] init];

        int bytesRead = 0;
        jboolean iscopy = FALSE;
        jbyte *primitiveArray = (*env)->GetPrimitiveArrayCritical(env, buffer, &iscopy);

        do {
            bytesRead = (*env)->CallIntMethod(env,
                                              inputStream,
                                              InputStream_readID,
                                              buffer,
                                              0,
                                              (jint) BUFFER_SIZE);
            if (bytesRead != -1) {
                [loader addToBuffer : (const unsigned char *) primitiveArray
                             length : bytesRead];
            }
        } while (bytesRead != -1);

        BOOL isLoaded = [loader loadFromBuffer : env];

        (*env)->ReleasePrimitiveArrayCritical(env, buffer, primitiveArray, JNI_ABORT);

        if (!isLoaded) {
            [pool release];
            return 0L;
        }

        (*env)->CallVoidMethod(env,
                               obj,
                               IosImageLoader_setInputParametersID,
                               (jint) [loader width],
                               (jint) [loader height],
                               (jint) [loader nImages],
                               (jint) [loader loopCount]);

        (*env)->CallVoidMethod(env,
                               obj,
                               IosImageLoader_updateImageProgressID,
                               100.0f);

        [pool release];

        return ptr_to_jlong(loader);
    }


    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    resizeImage
     * Signature: (JII)V
     */
    JNIEXPORT void JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_resizeImage
    (JNIEnv *env, jobject obj, jlong loaderRef, jint width, jint height) {

        IIOLog(@"IosImageLoader_resizeImage");

        ImageLoader *loader = (ImageLoader *) jlong_to_ptr(loaderRef);

        [loader resize : (int) width
                       : (int) height];
    }

    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    getImageBuffer
     * Signature: (JI)[B
     */
    JNIEXPORT jbyteArray JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_getImageBuffer
    (JNIEnv *env, jobject obj, jlong loaderRef, jint imageIndex) {

        IIOLog(@"IosImageLoader_getImageBuffer for image number %ld", imageIndex);

        ImageLoader *loader = (ImageLoader *) jlong_to_ptr(loaderRef);

        return [loader getDecompressedBuffer : env
                                  imageIndex : imageIndex];

    }

    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    getNumberOfComponents
     * Signature: (J)I
     */
    JNIEXPORT jint JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_getNumberOfComponents
    (JNIEnv *env, jobject obj, jlong loaderRef) {

        IIOLog(@"IosImageLoader_getNumberOfComponents");

        ImageLoader *loader = (ImageLoader *) jlong_to_ptr(loaderRef);

        return (jint) [loader nComponents];
    }

    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    getColorSpaceCode
     * Signature: (J)I
     */
    JNIEXPORT jint JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_getColorSpaceCode
    (JNIEnv *env, jobject obj, jlong loaderRef) {

        IIOLog(@"IosImageLoader_getColorSpaceCode");

        ImageLoader *loader = (ImageLoader *) jlong_to_ptr(loaderRef);

        return (jint) [loader colorSpace];
    }

    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    getDelayTime
     * Signature: (J)I
     */
    JNIEXPORT jint JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_getDelayTime
    (JNIEnv *env, jobject obj, jlong loaderRef) {

        IIOLog(@"IosImageLoader_getDelayTime");

        ImageLoader *loader = (ImageLoader *) jlong_to_ptr(loaderRef);

        return (jint) [loader delayTime];
    }

    /*
     * Class:     com_sun_javafx_iio_ios_IosImageLoader
     * Method:    disposeLoader
     * Signature: (J)V
     */
    JNIEXPORT void JNICALL Java_com_sun_javafx_iio_ios_IosImageLoader_disposeLoader
    (JNIEnv *env, jclass klazz, jlong loaderRef) {

        IIOLog(@"IosImageLoader_disposeLoader");

        ImageLoader *loader = (ImageLoader *) jlong_to_ptr(loaderRef);

        [loader release];
    }

#ifdef __cplusplus
}
#endif
