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

#import <AppKit/AppKit.h>
#import "CoreTextSupport.h"


/*
 * Callback for CoreText which uses the CoreTextProviderStruct to
 * feed CT UniChars.  We only use it for one-off lines, and don't
 * attempt to fragment our strings.
 */
const UniChar *
CTS_Provider(CFIndex stringIndex, CFIndex *charCount,
             CFDictionaryRef *attributes, void *refCon)
{
    // if we have a zero length string we can just return NULL for the string
    // or if the index anything other than 0 we are not using core text
    // correctly since we only have one run.
    if (stringIndex != 0) {
        return NULL;
    }

    CTS_ProviderStruct *ctps = (CTS_ProviderStruct *)refCon;
    *charCount = ctps->length;
    *attributes = ctps->attributes;
    return ctps->unicodes;
}


#pragma mark --- Retain/Release CoreText State Dictionary ---

/*
 * Gets a Dictionary filled with common details we want to use for CoreText
 * when we are interacting with it from Java.
 */
static inline CFMutableDictionaryRef
GetCTStateDictionaryFor(const NSFont *font, BOOL useFractionalMetrics)
{
    NSNumber *gZeroNumber = [NSNumber numberWithInt:0];
    NSNumber *gOneNumber = [NSNumber numberWithInt:1];

    CFMutableDictionaryRef dictRef = (CFMutableDictionaryRef)
        [[NSMutableDictionary alloc] initWithObjectsAndKeys:
        font, NSFontAttributeName,
        // TODO(cpc): following attribute is private...
        //gOneNumber,  (id)kCTForegroundColorFromContextAttributeName,
        // force integer hack in CoreText to help with Java integer assumptions
        useFractionalMetrics ? gZeroNumber : gOneNumber, @"CTIntegerMetrics",
        gZeroNumber, NSLigatureAttributeName,
        gZeroNumber, NSKernAttributeName,
        NULL];
    CFRetain(dictRef); // GC
    [(id)dictRef release];

    return dictRef;
}

/*
 * Releases the CoreText Dictionary - in the future we should hold on
 * to these to improve performance.
 */
static inline void
ReleaseCTStateDictionary(CFDictionaryRef ctStateDict)
{
    CFRelease(ctStateDict); // GC
}

/*
 *    Transform Unicode characters into glyphs.
 *
 *    Fills the "glyphsAsInts" array with the glyph codes for the current font,
 *    or the negative unicode value if we know the character can be hot-substituted.
 *
 *    This is the heart of "Universal Font Substitution" in Java.
 */
void CTS_GetGlyphsAsIntsForCharacters
(const AWTFont *font, const UniChar unicodes[], CGGlyph glyphs[], jint glyphsAsInts[], const size_t count)
{
    CTFontGetGlyphsForCharacters((CTFontRef)font->fFont, unicodes, glyphs, count);

    size_t i;
    for (i = 0; i < count; i++) {
        CGGlyph glyph = glyphs[i];
        if (glyph > 0) {
            glyphsAsInts[i] = glyph;
            continue;
        }

        UniChar unicode = unicodes[i];
        const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font->fFont, &unicode, 1);
        if (fallback) {
            CTFontGetGlyphsForCharacters(fallback, &unicode, &glyph, 1);
            CFRelease(fallback);
        }

        if (glyph > 0) {
            glyphsAsInts[i] = -unicode; // set the glyph code to the negative unicode value
        } else {
            glyphsAsInts[i] = 0; // CoreText couldn't find a glyph for this character either
        }
    }
}

/*
 * Translates a Unicode into a CGGlyph/CTFontRef pair
 * Returns the substituted font, and places the appropriate glyph into "glyphRef"
 */
CTFontRef CTS_CopyCTFallbackFontAndGlyphForUnicode
(const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count) {
    CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font->fFont, charRef, count);
    if (fallback == NULL)
    {
        // use the original font if we somehow got duped into trying to fallback something we can't
        fallback = (CTFontRef)font->fFont;
        CFRetain(fallback);
    }

    CTFontGetGlyphsForCharacters(fallback, charRef, glyphRef, count);
    return fallback;
}

/*
 * Translates a Java glyph code int (might be a negative unicode value) into a CGGlyph/CTFontRef pair
 * Returns the substituted font, and places the appropriate glyph into "glyphRef"
 */
CTFontRef CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode
(const AWTFont *font, const jint glyphCode, CGGlyph *glyphRef)
{
    // negative glyph codes are really unicodes, which were placed there by the mapper
    // to indicate we should use CoreText to substitute the character
    if (glyphCode >= 0)
    {
        *glyphRef = glyphCode;
        CFRetain(font->fFont);
        return (CTFontRef)font->fFont;
    }

    UTF16Char character = -glyphCode;
    return CTS_CopyCTFallbackFontAndGlyphForUnicode(font, &character, glyphRef, 1);
}

// Breakup a 32 bit unicode value into the component surrogate pairs
void CTS_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]) {
    int value = uniChar - 0x10000;
    UTF16Char low_surrogate = (value & 0x3FF) | LO_SURROGATE_START;
    UTF16Char high_surrogate = (((int)(value & 0xFFC00)) >> 10) | HI_SURROGATE_START;
    charRef[0] = high_surrogate;
    charRef[1] = low_surrogate;
}
