/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 */
#include "common.h"
#include "Pixels.h"
#include "com_sun_glass_ui_Pixels_Format.h"
#include "com_sun_glass_ui_win_WinPixels.h"

Bitmap::Bitmap(int width, int height)
{
    BYTE *mpixels = new BYTE[width * height];
    memset(mpixels, 0, width * height);
    Attach(::CreateBitmap(width, height, 1, 1, mpixels));
    delete[] mpixels;
    ASSERT((HBITMAP)*this);
}

Bitmap::Bitmap(int width, int height, void **data)
{
    ASSERT(width && height && data);

    BITMAPV5HEADER bmi = {0};
    bmi.bV5Width = width;
    bmi.bV5Height = -height;
    bmi.bV5Planes = 1;
    bmi.bV5BitCount = 32;

    bmi.bV5Size = sizeof(BITMAPV5HEADER);
    bmi.bV5Compression = BI_BITFIELDS;
    bmi.bV5XPelsPerMeter = 72;
    bmi.bV5YPelsPerMeter = 72;
    bmi.bV5RedMask   = 0x00FF0000;
    bmi.bV5GreenMask = 0x0000FF00;
    bmi.bV5BlueMask  = 0x000000FF;
    bmi.bV5AlphaMask = 0xFF000000;

    Attach(::CreateDIBSection(
        NULL,
        (BITMAPINFO*)&bmi,
        DIB_RGB_COLORS,
        data,
        NULL,
        0));

    ASSERT((HBITMAP)*this);
}

Bitmap::Bitmap(Pixels & pixels)
{
    Attach(::CreateBitmap(pixels.GetWidth(), pixels.GetHeight(), 1, 32, pixels.GetBits()));
    ASSERT((HBITMAP)*this);
}

DIBitmap::DIBitmap(Pixels & pixels)
{
    int const width = pixels.GetWidth();
    int const height = pixels.GetHeight();
    void * const bits = pixels.GetBits();

    void *bitmapBits = NULL;
    BITMAPINFOHEADER bmi;

    ::ZeroMemory(&bmi, sizeof(bmi));
    bmi.biSize = sizeof(bmi);
    bmi.biWidth = width;
    bmi.biHeight = -height;
    bmi.biPlanes = 1;
    bmi.biBitCount = 32;
    bmi.biCompression = BI_RGB;

    HBITMAP hBitmap = ::CreateDIBSection(NULL, (BITMAPINFO *)&bmi, DIB_RGB_COLORS, &bitmapBits, NULL, 0);

    if (bitmapBits) {
        memcpy(bitmapBits, bits, width * height * 4);
        Attach(hBitmap);
    }
    ASSERT((HBITMAP)*this);
}

HICON Pixels::CreateIcon(JNIEnv *env, jobject jPixels, BOOL fIcon, jint x, jint y)
{
    Pixels pixels(env, jPixels);

    Bitmap mask(pixels.GetWidth(), pixels.GetHeight());
    Bitmap bitmap(pixels);
    
    ICONINFO iconInfo;
    memset(&iconInfo, 0, sizeof(ICONINFO));
    iconInfo.hbmMask = mask;
    iconInfo.hbmColor = bitmap;
    iconInfo.fIcon = fIcon;
    iconInfo.xHotspot = x;
    iconInfo.yHotspot = y;
    HICON hIcon = ::CreateIconIndirect(&iconInfo);
    ASSERT(hIcon);

    ::GdiFlush();

    return hIcon;
}

Pixels::Pixels(JNIEnv *env, jobject jPixels)
{
    env->CallVoidMethod(jPixels, javaIDs.Pixels.attachData, ptr_to_jlong(this));
    CheckAndClearException(env);
}

void Pixels::AttachInt(JNIEnv *env, jint w, jint h, jobject buf, jintArray array, jint offset)
{
    width = w;
    height = h;
    ints.Attach(env, buf, array, offset);
}

void Pixels::AttachByte(JNIEnv *env, jint w, jint h, jobject buf, jbyteArray array, jint offset)
{
    width = w;
    height = h;
    bytes.Attach(env, buf, array, offset);
}

void * Pixels::GetBits()
{
    if (ints) {
        return ints.GetPtr();
    }
    if (bytes) {
        return bytes.GetPtr();
    }
    return NULL;
}

HANDLE BaseBitmap::GetGlobalDIB()
{
    HBITMAP hBitmap = (HBITMAP)*this;
    BITMAP bm;
    ::GetObject(hBitmap, sizeof(bm), &bm);

    BITMAPINFOHEADER bi = {0};
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bm.bmWidth;
    bi.biHeight = bm.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = bm.bmWidth * bm.bmHeight * 4;

    HANDLE hDIB = ::GlobalAlloc(GHND, bi.biSize + bi.biSizeImage);

    HDC hDC = ::GetDC(NULL);
    LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)::GlobalLock(hDIB);

    *lpbi = bi;
    ::GetDIBits(hDC, hBitmap, 0, bi.biHeight,
            (LPSTR)lpbi + (DWORD)lpbi->biSize, (LPBITMAPINFO)lpbi,
            DIB_RGB_COLORS);

    ::GlobalUnlock(hDIB);
    ::ReleaseDC(NULL, hDC);

    return hDIB;
}

extern "C" {

/*
 * Class:     com_sun_glass_ui_win_WinPixels
 * Method:    _initIDs
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinPixels__1initIDs
    (JNIEnv *env, jclass cls)
{
    javaIDs.Pixels.attachData = env->GetMethodID(cls, "attachData", "(J)V");
    ASSERT(javaIDs.Pixels.attachData);

    return com_sun_glass_ui_Pixels_Format_BYTE_BGRA_PRE;
}

/*
 * Class:     com_sun_glass_ui_win_WinPixels
 * Method:    _attachInt
 * Signature: (JIILjava/nio/IntBuffer;[II)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinPixels__1attachInt
    (JNIEnv *env, jobject pixels, jlong ptr, jint w, jint h, jobject buf, jintArray array, jint offset)
{
    ((Pixels*)jlong_to_ptr(ptr))->AttachInt(env, w, h, buf, array, offset);
}

/*
 * Class:     com_sun_glass_ui_win_WinPixels
 * Method:    _attachByte
 * Signature: (JIILjava/nio/ByteBuffer;[BI)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinPixels__1attachByte
    (JNIEnv *env, jobject pixels, jlong ptr, jint w, jint h, jobject buf, jbyteArray array, jint offset)
{
    ((Pixels*)jlong_to_ptr(ptr))->AttachByte(env, w, h, buf, array, offset);
}

/*
 * Class:     com_sun_glass_ui_win_WinPixels
 * Method:    _fillDirectByteBuffer
 * Signature: (Ljava/nio/ByteBuffer;)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinPixels__1fillDirectByteBuffer
    (JNIEnv *env, jobject jPixels, jobject bb)
{
    Pixels pixels(env, jPixels);

    memcpy(env->GetDirectBufferAddress(bb), pixels.GetBits(),
            pixels.GetWidth() * pixels.GetHeight() * 4);
}

} // extern "C"

