//
// Lol Engine
//
// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
//   This program is free software; you can redistribute it and/or
//   modify it under the terms of the Do What The Fuck You Want To
//   Public License, Version 2, as published by Sam Hocevar. See
//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
//

#if defined HAVE_CONFIG_H
#   include "config.h"
#endif

#include <cmath>
#include <cstdio>

#if defined __APPLE__ && defined __MACH__
#
#elif defined USE_SDL
#   include <SDL.h>
#   include <SDL_image.h>
#elif defined ANDROID_NDK
#   include <jni.h>
#   include <android/log.h>
#endif

#include "core.h"

namespace lol
{

#if defined ANDROID_NDK
extern JNIEnv *g_env;
extern jobject g_ctx;
#endif

/*
 * Image implementation class
 */

class ImageData
{
    friend class Image;

private:
    vec2i size;
    Image::format_t format;

#if defined __APPLE__ && defined __MACH__
    uint8_t *pixels;
#elif defined USE_SDL
    SDL_Surface *img;
#elif defined ANDROID_NDK
    jobject bmp;
    jintArray array;
    jint *pixels;
#else
    uint8_t *pixels;
#endif
};

/*
 * Public Image class
 */

Image::Image(char const *path)
  : data(new ImageData())
{
#if defined __APPLE__ && defined __MACH__
    NSString *path = [[NSBundle mainBundle] pathForResource:@"ascii" ofType:@"png"];
    NSData *pngdata = [[NSData alloc] initWithContentsOfFile:path];
    UIImage *image = [[UIImage alloc] initWithData:pngdata];
    if (!image)
    {
#if !LOL_RELEASE
        fprintf(stderr, "ERROR: could not load %s\n", path);
#endif
        exit(1);
    }

    int w = CGImageGetWidth(image.CGImage);
    int h = CGImageGetHeight(image.CGImage);
    data->size = vec2i(w, h);
    data->format = FORMAT_RGBA;

    CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
    data->pixels = (uint8_t *)malloc(w * h * 4);
    CGContextRef ctx =
            CGBitmapContextCreate(data->pixels, w, h, 8, 4 * w, cspace,
                    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(cspace);
    CGContextClearRect(ctx, CGRectMake(0, 0, w, h));
    CGContextTranslateCTM(ctx, 0, h - h);
    CGContextDrawImage(ctx, CGRectMake(0, 0, w, h), image.CGImage);
    CGContextRelease(ctx);
    [image release];
    [pngdata release];
#elif defined USE_SDL
    for (char const *name = path; *name; name++)
        if ((data->img = IMG_Load(name)))
            break;

    if (!data->img)
    {
#if !LOL_RELEASE
        fprintf(stderr, "ERROR: could not load %s\n", path);
#endif
        SDL_Quit();
        exit(1);
    }

    data->size = vec2i(data->img->w, data->img->h);
    data->format = data->img->format->Amask ? FORMAT_RGBA : FORMAT_RGB;
#elif defined ANDROID_NDK
    jclass cls = g_env->GetObjectClass(g_ctx);
    jmethodID mid;

    mid = g_env->GetMethodID(cls, "openImage",
                             "(Ljava/lang/String;)Landroid/graphics/Bitmap;");
    jstring name = g_env->NewStringUTF(path);
    data->bmp = g_env->CallObjectMethod(g_ctx, mid, name);
    g_env->DeleteLocalRef(name);
    g_env->NewGlobalRef(data->bmp);

    /* Get image dimensions */
    mid = g_env->GetMethodID(cls, "getWidth", "(Landroid/graphics/Bitmap;)I");
    data->size.x = g_env->CallIntMethod(g_ctx, mid, data->bmp);
    mid = g_env->GetMethodID(cls, "getHeight", "(Landroid/graphics/Bitmap;)I");
    data->size.y = g_env->CallIntMethod(g_ctx, mid, data->bmp);

    /* Get pixels */
    data->array = g_env->NewIntArray(data->size.x * data->size.y);
    g_env->NewGlobalRef(data->array);
    mid = g_env->GetMethodID(cls, "getPixels", "(Landroid/graphics/Bitmap;[I)V");
    g_env->CallVoidMethod(g_ctx, mid, data->bmp, data->array);

    data->pixels = g_env->GetIntArrayElements(data->array, 0);
    for (int n = 0; n < data->size.x * data->size.y; n++)
    {
        uint32_t u = data->pixels[n];
        u = (u & 0xff00ff00) | ((u & 0xff0000) >> 16) | ((u & 0xff) << 16);
        data->pixels[n] = u;
    }
    data->format = FORMAT_RGBA;
#else
    data->size = 256;
    data->format = FORMAT_RGBA;
    data->pixels = (uint8_t *)malloc(256 * 256 * 4 * sizeof(*data->pixels));
    uint8_t *parser = data->pixels;
    for (int j = 0; j < 256; j++)
        for (int i = 0; i < 256; i++)
        {
            *parser++ = ((i ^ j) & 1) * 0xff;
            *parser++ = (uint8_t)i;
            *parser++ = (uint8_t)j;
            *parser++ = (((i >> 4) ^ (j >> 4)) & 1) * 0xff;
        }
#endif
}

vec2i Image::GetSize() const
{
    return data->size;
}

Image::format_t Image::GetFormat() const
{
    return data->format;
}

void * Image::GetData() const
{
#if defined __APPLE__ && defined __MACH__
    return data->pixels;
#elif defined USE_SDL
    return data->img->pixels;
#elif defined ANDROID_NDK
    return data->pixels;
#else
    return data->pixels;
#endif
}

Image::~Image()
{
#if defined __APPLE__ && defined __MACH__
    free(data->pixels);
#elif defined USE_SDL
    SDL_FreeSurface(data->img);
#elif defined ANDROID_NDK
    jclass cls = g_env->GetObjectClass(g_ctx);
    jmethodID mid;

    g_env->ReleaseIntArrayElements(data->array, data->pixels, 0);
    g_env->DeleteGlobalRef(data->array);

    /* Free image */
    mid = g_env->GetMethodID(cls, "closeImage", "(Landroid/graphics/Bitmap;)V");
    g_env->CallVoidMethod(g_ctx, mid, data->bmp);
    g_env->DeleteGlobalRef(data->bmp);
#else
    free(data->pixels);
#endif
    delete data;
}

} /* namespace lol */