/*
 * Copyright (C) Research In Motion Limited 2009. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "FontPlatformData.h"

#include "SharedBuffer.h"

#include "FontDescription.h"
#include "SurfaceOpenVG.h"
#include "VGUtils.h"


#include <wtf/MathExtras.h>
#include <wtf/Hasher.h>
#include <wtf/text/CString.h>

#if USE(FREETYPE)
#include FT_OUTLINE_H
#endif

namespace WebCore {

#if USE(FREETYPE)
// FreeType outline decomposer callbacks.

typedef FontPlatformDataPrivate::GlyphDataVG GlyphDataVG;

struct PathDataVG {
    GlyphDataVG* glyph;
    int scale;
};

// These functions store the path data in bytes instead of floats
// to conserve memory. They involve some loss of precision, and
// they scale by the font size to try to avoid overflow.

int addVGMoveToSegmentByte(const FT_Vector* to, void* vdata)
{
    PathDataVG* data = static_cast<PathDataVG*>(vdata);
    data->glyph->commands.append(VG_MOVE_TO_ABS);
    data->glyph->bytePoints.append((VGbyte)(to->x / data->scale));
    data->glyph->bytePoints.append((VGbyte)(-to->y / data->scale));
    return 0;
}

int addVGLineToSegmentByte(const FT_Vector* to, void* vdata)
{
    PathDataVG* data = static_cast<PathDataVG*>(vdata);
    data->glyph->commands.append(VG_LINE_TO_ABS);
    data->glyph->bytePoints.append((VGbyte)(to->x / data->scale));
    data->glyph->bytePoints.append((VGbyte)(-to->y / data->scale));
    return 0;
}

int addVGConicToSegmentByte(const FT_Vector* control, const FT_Vector* to, void* vdata)
{
    PathDataVG* data = static_cast<PathDataVG*>(vdata);
    data->glyph->commands.append(VG_QUAD_TO_ABS);
    data->glyph->bytePoints.append((VGbyte)(control->x / data->scale));
    data->glyph->bytePoints.append((VGbyte)(-control->y / data->scale));
    data->glyph->bytePoints.append((VGbyte)(to->x / data->scale));
    data->glyph->bytePoints.append((VGbyte)(-to->y / data->scale));
    return 0;
}

int addVGCubicToSegmentByte(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* vdata)
{
    PathDataVG* data = static_cast<PathDataVG*>(vdata);
    data->glyph->commands.append(VG_CUBIC_TO_ABS);
    data->glyph->bytePoints.append((VGbyte)(control1->x / data->scale));
    data->glyph->bytePoints.append((VGbyte)(-control1->y / data->scale));
    data->glyph->bytePoints.append((VGbyte)(control2->x / data->scale));
    data->glyph->bytePoints.append((VGbyte)(-control2->y / data->scale));
    data->glyph->bytePoints.append((VGbyte)(to->x / data->scale));
    data->glyph->bytePoints.append((VGbyte)(-to->y / data->scale));
    return 0;
}

// These functions store the path data in floats.

int addVGMoveToSegment(const FT_Vector* to, void* vdata)
{
    PathDataVG* data = static_cast<PathDataVG*>(vdata);
    data->glyph->commands.append(VG_MOVE_TO_ABS);
    data->glyph->floatPoints.append(to->x / 64.f);
    data->glyph->floatPoints.append(to->y / -64.f);
    return 0;
}

int addVGLineToSegment(const FT_Vector* to, void* vdata)
{
    PathDataVG* data = static_cast<PathDataVG*>(vdata);
    data->glyph->commands.append(VG_LINE_TO_ABS);
    data->glyph->floatPoints.append(to->x / 64.f);
    data->glyph->floatPoints.append(to->y / -64.f);
    return 0;
}

int addVGConicToSegment(const FT_Vector* control, const FT_Vector* to, void* vdata)
{
    PathDataVG* data = static_cast<PathDataVG*>(vdata);
    data->glyph->commands.append(VG_QUAD_TO_ABS);
    data->glyph->floatPoints.append(control->x / 64.f);
    data->glyph->floatPoints.append(control->y / -64.f);
    data->glyph->floatPoints.append(to->x / 64.f);
    data->glyph->floatPoints.append(to->y / -64.f);
    return 0;
}

int addVGCubicToSegment(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* vdata)
{
    PathDataVG* data = static_cast<PathDataVG*>(vdata);
    data->glyph->commands.append(VG_CUBIC_TO_ABS);
    data->glyph->floatPoints.append(control1->x / 64.f);
    data->glyph->floatPoints.append(control1->y / -64.f);
    data->glyph->floatPoints.append(control2->x / 64.f);
    data->glyph->floatPoints.append(control2->y / -64.f);
    data->glyph->floatPoints.append(to->x / 64.f);
    data->glyph->floatPoints.append(to->y / -64.f);
    return 0;
}
#endif


#if USE(FREETYPE)
FT_Library FontPlatformDataPrivate::s_ftLibrary = 0;
#endif

DLL_PUBLIC FontPlatformDataPrivate::~FontPlatformDataPrivate()
{
    clear();
}

void FontPlatformDataPrivate::clear()
{
#ifdef OPENVG_VERSION_1_1
    if (m_vgFont) {
        vgDestroyFont(m_vgFont);
        m_vgFont = 0;
    }
#endif
#if USE(FREETYPE)
    if (m_ftFace) {
        FT_Done_Face(m_ftFace);
        m_ftFace = 0;
    }
    if (m_pattern) {
        FcPatternDestroy(m_pattern);
        m_pattern = 0;
    }
    if (m_buffer) {
        m_buffer->deref();
        m_buffer = 0;
    }
#endif
    m_glyphData.clear();
}

void FontPlatformDataPrivate::setFont(FcPattern* pattern)
{
    clear();
	m_pattern = pattern;
	if (m_pattern)
		FcPatternReference(m_pattern);

#if USE(FREETYPE)
    FT_Error error;

	// load the face with fontconfig
	if (pattern) {
		FcResult fcresult = FcPatternGetFTFace(pattern, FC_FT_FACE, 0, &m_ftFace);
		if (fcresult != FcResultMatch) {
			const char* filename = NULL;
			int index = 0;
			fcresult = FcPatternGetString(pattern, FC_FILE, 0, (FcChar8**)&filename);
			if (fcresult == FcResultMatch)
				fcresult = FcPatternGetInteger(pattern, FC_INDEX, 0, &index);
			if (fcresult == FcResultMatch) {
				error = FT_New_Face(FontPlatformData::ftLibrary(), filename, 0, &m_ftFace);
				ASSERT(!error);
			}
		}

		error = FT_Set_Pixel_Sizes(m_ftFace, 0, m_size);
		ASSERT_UNUSED(error, !error);
	}
#endif
}

#if USE(FREETYPE)
void FontPlatformDataPrivate::setBuffer(SharedBuffer* buffer)
{
    clear();
    m_buffer = buffer;
    m_buffer->ref();
    FT_Face freeTypeFace;
    FT_Error error;
    if (!(error = FT_New_Memory_Face(FontPlatformData::ftLibrary(),
                reinterpret_cast<const FT_Byte*>(buffer->data()),
                buffer->size(), 0, &freeTypeFace))) {
        m_ftFace = freeTypeFace;
        error = FT_Set_Pixel_Sizes(m_ftFace, 0, m_size);
        ASSERT_UNUSED(error, !error);
    }
}
#endif

FontPlatformDataPrivate& FontPlatformDataPrivate::operator=(const FontPlatformDataPrivate& other)
{
    if (this == &other)
        return *this;
    m_size = other.m_size;
    m_bold = other.m_bold;
    m_italic = other.m_italic;
    if (other.m_pattern)
        setFont(other.m_pattern);
    else if (other.m_buffer)
        setBuffer(other.m_buffer);
    return *this;
}

FontPlatformDataPrivate::FontPlatformDataPrivate(const FontPlatformDataPrivate& other)
    : RefCounted<FontPlatformDataPrivate>()
    , m_pattern(0)
#if USE(FREETYPE)
    , m_ftFace(0)
    , m_buffer(0)
#endif
#ifdef OPENVG_VERSION_1_1
    , m_vgFont(0)
#endif
{
    *this = other;
}

#if 0
void FontPlatformDataPrivate::attachFontFile(const char* filename)
{
#if USE(FREETYPE)
    FT_Error error = FT_Attach_File(m_ftFace, filename);
    ASSERT_UNUSED(error, !error);
#endif
}
#endif


#if USE(FREETYPE)
// static method
FT_Library FontPlatformData::ftLibrary()
{
    if (!FontPlatformDataPrivate::s_ftLibrary) {
        FT_Error error;

        error = FT_Init_FreeType(&FontPlatformDataPrivate::s_ftLibrary);
        ASSERT_UNUSED(error, !error);
    }
    return FontPlatformDataPrivate::s_ftLibrary;
}
#endif


FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType)
    : m_private(WTF::HashTableDeletedValue)
{
    FontPlatformData::init();
}

#if 0
FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName)
{
	m_private = adoptRef(new FontPlatformDataPrivate(fontDescription.computedPixelSize(), false, false));

    FontPlatformData::init();

    CString familyNameString = familyName.string().utf8();
    const char* fcfamily = familyNameString.data();
    int fcslant = FC_SLANT_ROMAN;
    // FIXME: Map all FontWeight values to fontconfig weights.
    int fcweight = FC_WEIGHT_NORMAL;
    double fcsize = fontDescription.computedPixelSize();
    if (fontDescription.italic())
        fcslant = FC_SLANT_ITALIC;
    if (fontDescription.weight() >= FontWeight600)
        fcweight = FC_WEIGHT_BOLD;

    int type = fontDescription.genericFamily();

	FT_Face font_face;
	FcPattern* pattern_match = NULL;
    FcPattern* pattern = FcPatternCreate();
    if (!pattern || !FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fcfamily)))
        goto freePattern;

    switch (type) {
    case FontDescription::SerifFamily:
        fcfamily = "serif";
        break;
    case FontDescription::SansSerifFamily:
        fcfamily = "sans-serif";
        break;
    case FontDescription::MonospaceFamily:
        fcfamily = "monospace";
        break;
    case FontDescription::StandardFamily:
        fcfamily = "sans-serif";
        break;
    case FontDescription::NoFamily:
    default:
        fcfamily = NULL;
        break;
    }

    if (!fcfamily || !FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fcfamily)))
        goto freePattern;
    if (!FcPatternAddInteger(pattern, FC_WEIGHT, fcweight))
        goto freePattern;
    if (!FcPatternAddInteger(pattern, FC_SLANT, fcslant))
        goto freePattern;
    if (!FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fcsize))
        goto freePattern;

	FcConfigSubstitute(NULL, pattern, FcMatchPattern);
	FcDefaultSubstitute(pattern);

	FcResult fcresult;
	pattern_match = FcFontMatch(NULL, pattern, &fcresult);
	// FIXME: should we set some default font?
	if (!pattern_match)
		goto freePattern;
	m_private->setFont(pattern_match);

freePattern:
	if (pattern)
		FcPatternDestroy(pattern);
}
#endif

DLL_PUBLIC FontPlatformData::FontPlatformData(const FontDescription& fontDescription, FcPattern* pattern, const String& familyName)
{
	m_private = adoptRef(new FontPlatformDataPrivate(fontDescription.computedPixelSize(), 
			fontDescription.weight() >= FontWeight600, 
			fontDescription.italic()));

	m_private->setFont(pattern);
}

DLL_PUBLIC FontPlatformData::FontPlatformData(const FontDescription&, PassRefPtr<PrivateFont>)
{
	m_private = adoptRef(new FontPlatformDataPrivate(0, false, false));
	// not implemented
}

DLL_PUBLIC FontPlatformData::FontPlatformData(float size, bool bold, bool italic)
{
    FontPlatformData::init();
#if 0
    if (size <= FLT_EPSILON) // see FontDataCacheKeyTraits::emptyValue()
        m_private = 0;
    else
#endif
	{
        m_private = adoptRef(new FontPlatformDataPrivate(size, bold, italic));
    }
}

DLL_PUBLIC FontPlatformData::FontPlatformData(SharedBuffer* buffer, float size, bool bold, bool italic)
{
    m_private = adoptRef(new FontPlatformDataPrivate(size, bold, italic));
    m_private->setBuffer(buffer);
}

DLL_PUBLIC FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other)
{
    if (this == &other)
        return *this;
    *m_private = *other.m_private;
    m_charReplaceMap = other.m_charReplaceMap;
    return *this;
}

DLL_PUBLIC FontPlatformData::FontPlatformData(const FontPlatformData& other)
{	
	m_private = adoptRef(new FontPlatformDataPrivate(0, 
			other.bold(), other.italic()));
    *this = other;
}

bool FontPlatformData::init()
{
#if 0
    static bool initialized = false;
    if (initialized)
        return true;
#if 0
// this method is ported over from cairo
    if (!FcInit()) {
        fprintf(stderr, "Can't init font config library\n");
        return false;
    }
#endif
    initialized = true;
#endif
    return true;
}

DLL_PUBLIC bool FontPlatformData::operator==(const FontPlatformData& other) const
{
    if (!m_private || !other.m_private || m_private.isHashTableDeletedValue() || other.m_private.isHashTableDeletedValue())
        return m_private == other.m_private;

    return m_private->m_size == other.m_private->m_size
        && m_private->m_bold == other.m_private->m_bold
        && m_private->m_italic == other.m_private->m_italic
        && m_private->m_pattern == other.m_private->m_pattern
#if USE(FREETYPE)
        && m_private->m_buffer == other.m_private->m_buffer
#endif
    ;
}

bool FontPlatformData::isValid() const
{
    return (bool) m_private;
}

DLL_PUBLIC void FontPlatformData::invalidate() const
{
    if (m_private->m_vgFont) {
        vgDestroyFont(m_private->m_vgFont);
        m_private->m_vgFont = 0;
    }
    m_private->m_glyphData.clear();
}

bool FontPlatformData::decodeGlyph(UChar32 character) const
{
    ASSERT(m_private);
    unsigned int key = character;
    if (m_private->m_glyphData.contains(key))
        return true;
	if (!m_private->m_ftFace)
		return false;

#if USE(FREETYPE)
	FT_UInt glyph_index = FT_Get_Char_Index(m_private->m_ftFace, character);
	if(glyph_index == 0)
		return false;

    FT_Error error = FT_Load_Glyph(m_private->m_ftFace, glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
    if(error)
    	return false;

    FT_GlyphSlot slot = m_private->m_ftFace->glyph;

    GlyphDataVG& data = m_private->m_glyphData.set(key, GlyphDataVG()).iterator->second;
    data.loaded = false;
    data.escapement[0] = slot->advance.x / 64.f;
    data.escapement[1] = slot->advance.y / 64.f;

#ifdef OPENVG_VERSION_1_1
    SurfaceOpenVG::makeCompatibleCurrent();

    if (slot->format == FT_GLYPH_FORMAT_OUTLINE && slot->outline.n_contours > 0) {
        FT_Outline_Funcs callbacks;
        memset(&callbacks, 0, sizeof(callbacks));

        int scale = (int)m_private->m_size;
        if (scale < 1)
            scale = 1;
        PathDataVG path = {&data, scale};

        data.commands.reserveCapacity(slot->outline.n_contours + 1);

        // this implementation can use bytes for path data to conserve memory
        bool useBytes = VGUtils::shouldConserveFontMemory;

        if (useBytes) {
            data.bytePoints.reserveCapacity(slot->outline.n_points * 2);

            callbacks.move_to = addVGMoveToSegmentByte;
            callbacks.line_to = addVGLineToSegmentByte;
            callbacks.conic_to = addVGConicToSegmentByte;
            callbacks.cubic_to = addVGCubicToSegmentByte;
            error = FT_Outline_Decompose(&slot->outline, &callbacks, &path);
            ASSERT_UNUSED(error, !error);

        } else {
            data.floatPoints.reserveCapacity(slot->outline.n_points * 2);

            callbacks.move_to = addVGMoveToSegment;
            callbacks.line_to = addVGLineToSegment;
            callbacks.conic_to = addVGConicToSegment;
            callbacks.cubic_to = addVGCubicToSegment;
            error = FT_Outline_Decompose(&slot->outline, &callbacks, &path);
            ASSERT_UNUSED(error, !error);
        }

        data.commands.append(VG_CLOSE_PATH);

        return true;
    }

    // If we didn't get an outline, try a bitmap rendering instead.
    if (slot->format != FT_GLYPH_FORMAT_BITMAP)
        error = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);

    if (slot->bitmap.width && slot->bitmap.rows) {
        VGImage image = vgCreateImage(VG_sL_8,
            slot->bitmap.width, slot->bitmap.rows, WEBKIT_VG_IMAGE_QUALITY);
        ASSERT_VG_NO_ERROR();

        Vector<VGubyte> buffer(slot->bitmap.width * slot->bitmap.rows);
        int currentOffset;

        for (int y = 0; y < slot->bitmap.rows; ++y) {
            for (int x = 0; x < slot->bitmap.width; ++x) {
                currentOffset = (y * slot->bitmap.pitch) + x;
                buffer[currentOffset] = 255 - slot->bitmap.buffer[currentOffset];
            }
        }

        // we don't lazy load bitmap rendered glyphs; we load them right away.

        vgImageSubData(image, buffer.data(), slot->bitmap.pitch, VG_sL_8,
            0, 0, slot->bitmap.width, slot->bitmap.rows);
        ASSERT_VG_NO_ERROR();

        VGfloat glyphOrigin[2];
        glyphOrigin[0] = roundf(slot->metrics.horiBearingX / -64.f);
        glyphOrigin[1] = roundf(slot->metrics.horiBearingY / 64.f);

        vgSetGlyphToImage(vgFont(), character, image, glyphOrigin, data.escapement);
        vgDestroyImage(image);
        data.loaded = true;
        ASSERT_VG_NO_ERROR();

        return true;
    }

    // If we neither got a path nor an image out of the glyph, use an
    // "empty image" instead.
    VGfloat glyphOrigin[] = {0.0f, 0.0f};
    vgSetGlyphToImage(vgFont(), character, VG_INVALID_HANDLE, glyphOrigin, data.escapement);
    data.loaded = true;
    ASSERT_VG_NO_ERROR();

    return true;
#else // OPENVG_VERSION_1_1
    return true;
#endif

#else // USE(FREETYPE)
    return false;
#endif
}

void FontPlatformData::uploadGlyph(UChar32 character) const
{
    ASSERT(m_private);
    unsigned int key = character;
    if (!m_private->m_glyphData.contains(key))
        return;
    GlyphDataVG& data = m_private->m_glyphData.find(key)->second;
    if (data.loaded)
        return;

    int scale = (int)m_private->m_size;
    if (scale < 1)
        scale = 1;

    bool useBytes = VGUtils::shouldConserveFontMemory;
    VGPath path = VG_INVALID_HANDLE;

    if (useBytes) {
        path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_8,
            scale / 64.0f, 0.0f /* bias */,
            data.commands.size(), data.bytePoints.size(),
            VG_PATH_CAPABILITY_APPEND_TO);
        ASSERT_VG_NO_ERROR();

        vgAppendPathData(path, data.commands.size(), data.commands.data(), data.bytePoints.data());
        ASSERT_VG_NO_ERROR();

    } else {
        path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
            1.0f /* scale */, 0.0f /* bias */,
            data.commands.size(), data.floatPoints.size(),
            VG_PATH_CAPABILITY_APPEND_TO);
        ASSERT_VG_NO_ERROR();
 
        vgAppendPathData(path, data.commands.size(), data.commands.data(), data.floatPoints.data());
        ASSERT_VG_NO_ERROR();
    }        
    VGfloat glyphOrigin[] = {0.0f, 0.0f};
    vgSetGlyphToPath(vgFont(), character, path, VG_FALSE /*isHinted*/, glyphOrigin, data.escapement);
    vgDestroyPath(path);
    data.loaded = true;
    ASSERT_VG_NO_ERROR();
}

float FontPlatformData::widthForGlyph(UChar32 character) const
{
    ASSERT(m_private);
    unsigned int key = character;
    return m_private->m_glyphData.contains(key)
        ? m_private->m_glyphData.get(key).escapement[0]
        : 0.f;
}

float FontPlatformData::kerningDistance(UChar32 leftCharacter, UChar32 rightCharacter) const
{
    ASSERT(m_private);
#if USE(FREETYPE)
    ASSERT(m_private->m_ftFace);
	if (!m_private->m_ftFace)
		return 0.f;

    if (!FT_HAS_KERNING(m_private->m_ftFace))
        return 0.f;

    FT_Vector kerning;

    FT_Error error = FT_Get_Kerning(m_private->m_ftFace,
        FT_Get_Char_Index(m_private->m_ftFace, leftCharacter),
        FT_Get_Char_Index(m_private->m_ftFace, rightCharacter),
        FT_KERNING_DEFAULT, &kerning);
    ASSERT_UNUSED(error, !error);

    return kerning.x / 64.f;
#else
    return 0.f;
#endif
}

#if USE(FREETYPE)
FT_Face FontPlatformData::ftFace() const
{
    ASSERT(m_private);
    return m_private->m_ftFace;
}
#endif

#ifdef OPENVG_VERSION_1_1
VGFont FontPlatformData::vgFont() const
{
    ASSERT(m_private);

    if (!m_private->m_vgFont) {
        SurfaceOpenVG::makeCompatibleCurrent();
        m_private->m_vgFont = vgCreateFont(0);
        ASSERT_VG_NO_ERROR();
    }
    return m_private->m_vgFont;
}
#endif

#ifndef NDEBUG
String FontPlatformData::description() const
{
    ASSERT(m_private);
    return String();
}
#endif

int FontPlatformData::size() const
{
    ASSERT(m_private);
    return m_private->m_size;
}

}
