/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 */

// http://developer.apple.com/documentation/Carbon/reference/CTFontRef/Reference/reference.html

#import <Cocoa/Cocoa.h>

#import "common.h"
#import "com_sun_rain_graphics_Font.h"

static jclass jFontClass = NULL;
static jclass jFontMetricsClass = NULL;
static jclass jRectangleClass = NULL;

static CGContextRef cgContext = NULL;

static inline CTFontRef getMacFont(JNIEnv *env, jobject jfont)
{
    return (CTFontRef)jlong_to_ptr((*env)->GetLongField(env, jfont, (*env)->GetFieldID(env, jFontClass, "ptr", "J")));
}

static inline NSString* getNSString(JNIEnv* env, jstring jstring)
{
    NSString* string = nil;
    
    const jchar* jstrChars = (*env)->GetStringChars(env, jstring, NULL);
    string = [[[NSString alloc] initWithCharacters: jstrChars length: (*env)->GetStringLength(env, jstring)] autorelease];
    (*env)->ReleaseStringChars(env, jstring, jstrChars);
    
    return string;
}

static CGContextRef CreateBitmapContext(int pixelsWide, int pixelsHigh)
{
    int bitmapBytesPerRow = (pixelsWide * 4);
    int bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    void *bitmapData = malloc(bitmapByteCount);
    CGContextRef context = CGBitmapContextCreate(bitmapData, pixelsWide, pixelsHigh, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    
    return context;
}

/*
 * Class:     com_sun_rain_graphics_Font
 * Method:    _initIDs
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_sun_rain_graphics_Font__1initIDs
  (JNIEnv *env, jclass jClass)
{
    jFontClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/sun/rain/graphics/Font"));
    jFontMetricsClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/sun/rain/graphics/Font$Metrics"));
    jRectangleClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/sun/rain/shapes/Rectangle"));
    
    cgContext = CreateBitmapContext(1, 1);
}

/*
 * Class:     com_sun_rain_graphics_Font
 * Method:    _createFont
 * Signature: (Ljava/lang/String;IFLcom/sun/rain/graphics/Font/Metrics;)J
 */
JNIEXPORT jlong JNICALL Java_com_sun_rain_graphics_Font__1createFont
  (JNIEnv *env, jobject jfont, jstring name, jint type, jfloat size, jobject jmetrics)
{
	CTFontRef ctFont = NULL;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
		NSString *fontName = getNSString(env, name);
		ctFont = CTFontCreateWithName((CFStringRef)fontName, (float)size, NULL);
		if (ctFont == NULL)
		{
			NSLog(@"Java_com_sun_rain_graphics_Font__1createFont: NULL font");
		}
	}
    [pool drain];
    
    (*env)->SetFloatField(env, jmetrics, (*env)->GetFieldID(env, jFontMetricsClass, "ascent", "F"), CTFontGetAscent(ctFont));
    (*env)->SetFloatField(env, jmetrics, (*env)->GetFieldID(env, jFontMetricsClass, "descent", "F"), CTFontGetDescent(ctFont));
    (*env)->SetFloatField(env, jmetrics, (*env)->GetFieldID(env, jFontMetricsClass, "leading", "F"), CTFontGetLeading(ctFont));
    
    (*env)->SetFloatField(env, jmetrics, (*env)->GetFieldID(env, jFontMetricsClass, "x", "F"), CTFontGetBoundingBox(ctFont).origin.x);
    (*env)->SetFloatField(env, jmetrics, (*env)->GetFieldID(env, jFontMetricsClass, "y", "F"), CTFontGetBoundingBox(ctFont).origin.y);
    (*env)->SetFloatField(env, jmetrics, (*env)->GetFieldID(env, jFontMetricsClass, "width", "F"), CTFontGetBoundingBox(ctFont).size.width);
    (*env)->SetFloatField(env, jmetrics, (*env)->GetFieldID(env, jFontMetricsClass, "height", "F"), CTFontGetBoundingBox(ctFont).size.height);

    return ptr_to_jlong(ctFont);
}

/*
 * Class:     com_sun_rain_graphics_Font
 * Method:    _getBoundingBox
 * Signature: (Ljava/lang/String;Lcom/sun/rain/shapes/Rectangle;)Lcom/sun/rain/shapes/Rectangle;
 */
JNIEXPORT jobject JNICALL Java_com_sun_rain_graphics_Font__1getBoundingBox
  (JNIEnv *env, jobject jfont, jstring string, jobject jbbox)
{
    CTFontRef ctFont = getMacFont(env, jfont);
    if (ctFont == NULL)
    {
        NSLog(@"Java_com_sun_rain_graphics_Font__1getBoundingBox: NULL font");
        return jbbox;
    }
    
    const char* chars = (*env)->GetStringUTFChars(env, string, 0);
    
    CFStringRef keys[] = { kCTFontAttributeName };
    CFTypeRef values[] = { ctFont };
    CFDictionaryRef attr = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, chars, kCFStringEncodingASCII);
    CFAttributedStringRef attrString = CFAttributedStringCreate(NULL, cfString, attr);
    CFRelease(cfString);
    CFRelease(attr);
    
    CTLineRef line = CTLineCreateWithAttributedString(attrString);
    
    CGRect bbox = CTLineGetImageBounds(line, cgContext);
    (*env)->SetFloatField(env, jbbox, (*env)->GetFieldID(env, jRectangleClass, "x", "F"), bbox.origin.x);
    (*env)->SetFloatField(env, jbbox, (*env)->GetFieldID(env, jRectangleClass, "y", "F"), bbox.origin.y);
    (*env)->SetFloatField(env, jbbox, (*env)->GetFieldID(env, jRectangleClass, "width", "F"), bbox.size.width);
    (*env)->SetFloatField(env, jbbox, (*env)->GetFieldID(env, jRectangleClass, "height", "F"), bbox.size.height);
    
    CFRelease(line);
    CFRelease(attrString);
    
    (*env)->ReleaseStringUTFChars(env, string, chars);
    
    return jbbox;
}
