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

/*
 * These routines are used for display string with multi font.
 */

#ifdef HEADLESS
    #error This file should not be included in headless library
#endif

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <jni.h>
#include <jni_util.h>
#include <jvm.h>
#include "awt_Font.h"
#include "awt_p.h"
#include "multi_font.h"

extern XFontStruct *loadFont(Display *, char *, int32_t);

extern struct FontIDs fontIDs;
extern struct PlatformFontIDs platformFontIDs;
extern struct XFontPeerIDs xFontPeerIDs;

/*
 * make string with str + string representation of num
 * This string is used as tag string of Motif Compound String and FontList.
 */
static void
makeTag(char *str, int32_t num, char *buf)
{
    int32_t len = strlen(str);

    strcpy(buf, str);
    buf[len] = '0' + num % 100;
    buf[len + 1] = '\0';
}

static int32_t
awtJNI_GetFontDescriptorNumber(JNIEnv * env
                               ,jobject font
                               ,jobject fd)
{
    int32_t i = 0, num;
    /* initialize to NULL so that DeleteLocalRef will work. */
    jobjectArray componentFonts = NULL;
    jobject peer = NULL;
    jobject temp = NULL;
    jboolean validRet = JNI_FALSE;

    if ((*env)->EnsureLocalCapacity(env, 2) < 0 || (*env)->ExceptionCheck(env))
        goto done;

    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
    if (peer == NULL)
        goto done;

    componentFonts = (jobjectArray)
        (*env)->GetObjectField(env,peer,platformFontIDs.componentFonts);

    if (componentFonts == NULL)
        goto done;

    num = (*env)->GetArrayLength(env, componentFonts);

    for (i = 0; i < num; i++) {
        temp = (*env)->GetObjectArrayElement(env, componentFonts, i);

        if ((*env)->IsSameObject(env, fd, temp)) {
            validRet = JNI_TRUE;
            break;
        }
        (*env)->DeleteLocalRef(env, temp);
    }

 done:
    (*env)->DeleteLocalRef(env, peer);
    (*env)->DeleteLocalRef(env, componentFonts);

    if (validRet)
        return i;

    return 0;
}

jobject
awtJNI_GetFMFont(JNIEnv * env, jobject this)
{
    return JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
                                "()Ljava/awt/Font;").l;
}

jboolean
awtJNI_IsMultiFont(JNIEnv * env, jobject this)
{
    jobject peer = NULL;
    jobject fontConfig = NULL;

    if (this == NULL) {
        return JNI_FALSE;
    }

    if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
        return JNI_FALSE;
    }

    peer = (*env)->CallObjectMethod(env,this,fontIDs.getPeer);
    if (peer == NULL) {
        return JNI_FALSE;
    }

    fontConfig = (*env)->GetObjectField(env,peer,platformFontIDs.fontConfig);
    (*env)->DeleteLocalRef(env, peer);

    if (fontConfig == NULL) {
        return JNI_FALSE;
    }
    (*env)->DeleteLocalRef(env, fontConfig);

    return JNI_TRUE;
}

jboolean
awtJNI_IsMultiFontMetrics(JNIEnv * env, jobject this)
{
    jobject peer = NULL;
    jobject fontConfig = NULL;
    jobject font = NULL;

    if (JNU_IsNull(env, this)) {
        return JNI_FALSE;
    }
    if ((*env)->EnsureLocalCapacity(env, 3) < 0) {
        return JNI_FALSE;
    }

    font = JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
                                "()Ljava/awt/Font;").l;
    if (JNU_IsNull(env, font) || (*env)->ExceptionCheck(env)) {
        return JNI_FALSE;
    }

    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
    (*env)->DeleteLocalRef(env, font);

    if (peer == NULL) {
        return JNI_FALSE;
    }

    fontConfig = (*env)->GetObjectField(env,peer,platformFontIDs.fontConfig);
    (*env)->DeleteLocalRef(env, peer);
    if (fontConfig == NULL) {
        return JNI_FALSE;
    }
    (*env)->DeleteLocalRef(env, fontConfig);

    return JNI_TRUE;
}

/* #define FONT_DEBUG 2 */

XFontSet
awtJNI_MakeFontSet(JNIEnv * env, jobject font)
{
    jstring xlfd = NULL;
    char *xfontset = NULL;
    int32_t size;
    int32_t length = 0;
    char *realxlfd = NULL, *ptr = NULL, *prev = NULL;
    char **missing_list = NULL;
    int32_t missing_count;
    char *def_string = NULL;
    XFontSet xfs;
    jobject peer = NULL;
    jstring xfsname = NULL;
#ifdef FONT_DEBUG
    char xx[1024];
#endif

    if ((*env)->EnsureLocalCapacity(env, 2) < 0)
        return 0;

    size = (*env)->GetIntField(env, font, fontIDs.size) * 10;

    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
    xfsname = (*env)->GetObjectField(env, peer, xFontPeerIDs.xfsname);

    if (JNU_IsNull(env, xfsname))
        xfontset = "";
    else
        xfontset = (char *)JNU_GetStringPlatformChars(env, xfsname, NULL);

    realxlfd = malloc(strlen(xfontset) + 50);

    prev = ptr = xfontset;
    while ((ptr = strstr(ptr, "%d"))) {
        char save = *(ptr + 2);

        *(ptr + 2) = '\0';
        jio_snprintf(realxlfd + length, strlen(xfontset) + 50 - length,
                     prev, size);
        length = strlen(realxlfd);
        *(ptr + 2) = save;

        prev = ptr + 2;
        ptr += 2;
    }
    strcpy(realxlfd + length, prev);

#ifdef FONT_DEBUG
    strcpy(xx, realxlfd);
#endif
    xfs = XCreateFontSet(awt_display, realxlfd, &missing_list,
                         &missing_count, &def_string);
#if FONT_DEBUG >= 2
    fprintf(stderr, "XCreateFontSet(%s)->0x%x\n", xx, xfs);
#endif

#if FONT_DEBUG
    if (missing_count != 0) {
        int32_t i;
        fprintf(stderr, "XCreateFontSet missing %d fonts:\n", missing_count);
        for (i = 0; i < missing_count; ++i) {
            fprintf(stderr, "\t\"%s\"\n", missing_list[i]);
        }
        fprintf(stderr, "  requested \"%s\"\n", xx);
#if FONT_DEBUG >= 3
        exit(-1);
#endif
    }
#endif

    free((void *)realxlfd);

    if (xfontset && !JNU_IsNull(env, xfsname))
        JNU_ReleaseStringPlatformChars(env, xfsname, (const char *) xfontset);

    (*env)->DeleteLocalRef(env, peer);
    (*env)->DeleteLocalRef(env, xfsname);
    return xfs;
}

/*
 * get multi font string width with multiple X11 font
 *
 * ASSUMES: We are not running on a privileged thread
 */
int32_t
awtJNI_GetMFStringWidth(JNIEnv * env, jcharArray s, int offset, int sLength, jobject font)
{
    char *err = NULL;
    unsigned char *stringData = NULL;
    char *offsetStringData = NULL;
    int32_t stringCount, i;
    int32_t size;
    struct FontData *fdata = NULL;
    jobject fontDescriptor = NULL;
    jbyteArray data = NULL;
    int32_t j;
    int32_t width = 0;
    int32_t length;
    XFontStruct *xf = NULL;
    jobjectArray dataArray = NULL;
    if ((*env)->EnsureLocalCapacity(env, 3) < 0)
        return 0;

    if (!JNU_IsNull(env, s) && !JNU_IsNull(env, font))
    {
        jobject peer;
        peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);

        dataArray = (*env)->CallObjectMethod(
                                 env,
                                 peer,
                                 platformFontIDs.makeConvertedMultiFontChars,
                                 s, offset, sLength);

        if ((*env)->ExceptionOccurred(env))
        {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }

        (*env)->DeleteLocalRef(env, peer);

        if(dataArray == NULL)
        {
            return 0;
        }
    } else {
        return 0;
    }

    fdata = awtJNI_GetFontData(env, font, &err);
    if ((*env)->ExceptionCheck(env)) {
        (*env)->DeleteLocalRef(env, dataArray);
        return 0;
    }

    stringCount = (*env)->GetArrayLength(env, dataArray);

    size = (*env)->GetIntField(env, font, fontIDs.size);

    for (i = 0; i < stringCount; i+=2)
    {
        fontDescriptor = (*env)->GetObjectArrayElement(env, dataArray, i);
        data = (*env)->GetObjectArrayElement(env, dataArray, i + 1);

        /* Bail if we've finished */
        if (fontDescriptor == NULL || data == NULL) {
            (*env)->DeleteLocalRef(env, fontDescriptor);
            (*env)->DeleteLocalRef(env, data);
            break;
        }

        j = awtJNI_GetFontDescriptorNumber(env, font, fontDescriptor);
        if ((*env)->ExceptionCheck(env)) {
            (*env)->DeleteLocalRef(env, fontDescriptor);
            (*env)->DeleteLocalRef(env, data);
            break;
        }

        if (fdata->flist[j].load == 0) {
            xf = loadFont(awt_display,
                          fdata->flist[j].xlfd, size * 10);
            if (xf == NULL) {
                (*env)->DeleteLocalRef(env, fontDescriptor);
                (*env)->DeleteLocalRef(env, data);
                continue;
            }
            fdata->flist[j].load = 1;
            fdata->flist[j].xfont = xf;
            if (xf->min_byte1 == 0 && xf->max_byte1 == 0)
                fdata->flist[j].index_length = 1;
            else
                fdata->flist[j].index_length = 2;
        }
        xf = fdata->flist[j].xfont;

        stringData =
            (unsigned char *)(*env)->GetPrimitiveArrayCritical(env, data,NULL);
        if (stringData == NULL) {
            (*env)->DeleteLocalRef(env, fontDescriptor);
            (*env)->DeleteLocalRef(env, data);
            (*env)->ExceptionClear(env);
            JNU_ThrowOutOfMemoryError(env, "Could not get string data");
            break;
        }

        length = (stringData[0] << 24) | (stringData[1] << 16) |
            (stringData[2] << 8) | stringData[3];
        offsetStringData = (char *)(stringData + (4 * sizeof(char)));

        if (fdata->flist[j].index_length == 2) {
            width += XTextWidth16(xf, (XChar2b *)offsetStringData, length/2);
        } else {
            width += XTextWidth(xf, offsetStringData, length);
        }

        (*env)->ReleasePrimitiveArrayCritical(env, data, stringData, JNI_ABORT);
        (*env)->DeleteLocalRef(env, fontDescriptor);
        (*env)->DeleteLocalRef(env, data);
    }
    (*env)->DeleteLocalRef(env, dataArray);

    return width;
}
