// Font.cpp:  ActionScript Font handling, for Gnash.
// 
//   Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
//   Free Software Foundation, Inc
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//


// Based on the public domain work of Thatcher Ulrich <tu@tulrich.com> 2003

#include "Font.h"

#include <utility> 
#include <memory>

#include "log.h"
#include "ShapeRecord.h"
#include "DefineFontTag.h"
#include "FreetypeGlyphsProvider.h"

namespace gnash {

namespace {

/// Reverse lookup of Glyph in CodeTable.
//
/// Inefficient, which is probably why TextSnapshot was designed like it
/// is.
class CodeLookup
{
public:
    CodeLookup(const int glyph) : _glyph(glyph) {}

    bool operator()(const std::pair<const std::uint16_t, int>& p) const {
        return p.second == _glyph;
    }

private:
    const int _glyph;
};

}

Font::GlyphInfo::GlyphInfo()
    :
    advance(0)
{}

Font::GlyphInfo::GlyphInfo(std::unique_ptr<SWF::ShapeRecord> the_glyph,
        float advance)
    :
    glyph(std::move(the_glyph)),
    advance(advance)
{}

Font::Font(std::unique_ptr<SWF::DefineFontTag> ft)
    :
    _fontTag(ft.release()),
    _name(_fontTag->name()),
    _unicodeChars(_fontTag->unicodeChars()),
    _shiftJISChars(_fontTag->shiftJISChars()),
    _ansiChars(_fontTag->ansiChars()),
    _italic(_fontTag->italic()),
    _bold(_fontTag->bold())
{
    if (_fontTag->hasCodeTable()) {
        _embeddedCodeTable = _fontTag->getCodeTable();
    }
}

Font::Font(std::string name, bool bold, bool italic)
    :
    _name(std::move(name)),
    _unicodeChars(false),
    _shiftJISChars(false),
    _ansiChars(true),
    _italic(italic),
    _bold(bold)
{
    assert(!_name.empty());
}

Font::~Font()
{
}

SWF::ShapeRecord*
Font::get_glyph(int index, bool embedded) const
{
    // What to do if embedded is true and this is a
    // device-only font?
    const GlyphInfoRecords& lookup = (embedded && _fontTag) ? 
            _fontTag->glyphTable() : _deviceGlyphTable;

    if (index >= 0 && (size_t)index < lookup.size()) {
        return lookup[index].glyph.get();
    }

    // TODO: should we log an error here ?
    return nullptr;
}

void
Font::addFontNameInfo(const FontNameInfo& fontName)
{
    if (!_displayName.empty() || !_copyrightName.empty())
    {
        IF_VERBOSE_MALFORMED_SWF(
            log_swferror(_("Attempt to set font display or copyright name "
                    "again. This should mean there is more than one "
                    "DefineFontName tag referring to the same Font. Don't "
                    "know what to do in this case, so ignoring."));
        );
        return;
    }

    _displayName = fontName.displayName;
    _copyrightName = fontName.copyrightName;
}


Font::GlyphInfoRecords::size_type
Font::glyphCount() const
{
        assert(_fontTag);
        return _fontTag->glyphTable().size();
}


void
Font::setFlags(std::uint8_t flags)
{
    _shiftJISChars = flags & (1 << 6);
    _unicodeChars = flags & (1 << 5);
    _ansiChars = flags & (1 << 4);
    _italic = flags & (1 << 1);
    _bold = flags & (1 << 0);
}


void
Font::setCodeTable(std::unique_ptr<CodeTable> table)
{
    if (_embeddedCodeTable) {
        IF_VERBOSE_MALFORMED_SWF(
            log_swferror(_("Attempt to add an embedded glyph CodeTable to "
                    "a font that already has one. This should mean there "
                    "are several DefineFontInfo tags, or a DefineFontInfo "
                    "tag refers to a font created by DefineFont2 or "
                    "DefineFont3. Don't know what should happen in this "
                    "case, so ignoring."));
        );
        return;
    }
    _embeddedCodeTable.reset(table.release());
}

    
void
Font::setName(const std::string& name)
{
    _name = name;
}


std::uint16_t
Font::codeTableLookup(int glyph, bool embedded) const
{
    const CodeTable& ctable = (embedded && _embeddedCodeTable) ? 
        *_embeddedCodeTable : _deviceCodeTable;
    
    CodeTable::const_iterator it = std::find_if(ctable.begin(), ctable.end(),
            CodeLookup(glyph));

    if (it == ctable.end()) {
        // NB: this occurs with a long and complex SWF (bug #32537)
        // that defines the same font twice and ends up with a glyph
        // table shorter than the number of glyphs. We don't know
        // whether it's a SWF or a Gnash bug.
        log_error(_("Failed to find glyph %s in %s font %s"),
                glyph, embedded ? "embedded" : "device", _name);
        return 0;
    }
    return it->first;
}

int
Font::get_glyph_index(std::uint16_t code, bool embedded) const
{
    const CodeTable& ctable = (embedded && _embeddedCodeTable) ? 
        *_embeddedCodeTable : _deviceCodeTable;

    int glyph_index = -1;
    CodeTable::const_iterator it = ctable.find(code);
    if (it != ctable.end()) {
        glyph_index = it->second;
        return glyph_index;
    }

    // Try adding an os font, if possible
    if (!embedded) {
        glyph_index = const_cast<Font*>(this)->add_os_glyph(code);
    }
    return glyph_index;
}

float
Font::get_advance(int glyph_index, bool embedded) const
{
    // What to do if embedded is true and this is a
    // device-only font?
    const GlyphInfoRecords& lookup = (embedded && _fontTag) ? 
            _fontTag->glyphTable() : _deviceGlyphTable;

    if (glyph_index < 0) {
        // Default advance.
        return 512.0f;
    }

    assert(static_cast<size_t>(glyph_index) < lookup.size());
    assert(glyph_index >= 0);
    
    return lookup[glyph_index].advance;
}


// Return the adjustment in advance between the given two
// DisplayObjects.  Normally this will be 0; i.e. the 
float
Font::get_kerning_adjustment(int last_code, int code) const
{
    kerning_pair k;
    k.m_char0 = last_code;
    k.m_char1 = code;
    kernings_table::const_iterator it = m_kerning_pairs.find(k);
    if (it != m_kerning_pairs.end()) {
        float adjustment = it->second;
        return adjustment;
    }
    return 0;
}

size_t
Font::unitsPerEM(bool embed) const
{
    // the EM square is 1024 x 1024 for DefineFont up to 2
    // and 20 as much for DefineFont3 up
    if (embed) {
        if ( _fontTag && _fontTag->subpixelFont() ) return 1024 * 20.0;
        else return 1024;
    }
    
    FreetypeGlyphsProvider* ft = ftProvider();
    if (!ft) {
        log_error(_("Device font provider was not initialized, "
                    "can't get unitsPerEM"));
        return 0; 
    }

    return ft->unitsPerEM();
}

int
Font::add_os_glyph(std::uint16_t code)
{
    FreetypeGlyphsProvider* ft = ftProvider();
    if (!ft) return -1;

    assert(_deviceCodeTable.find(code) == _deviceCodeTable.end());

    float advance;

    // Get the vectorial glyph
    std::unique_ptr<SWF::ShapeRecord> sh = ft->getGlyph(code, advance);

    if (!sh.get()) {
        log_error(_("Could not create shape "
                "glyph for DisplayObject code %u (%c) with "
		    "device font %s (%p)"), code, code, _name, ft);
        return -1;
    }

    // Find new glyph offset
    int newOffset = _deviceGlyphTable.size();

    // Add the new glyph id
    _deviceCodeTable[code] = newOffset;

    _deviceGlyphTable.emplace_back(std::move(sh), advance);

    return newOffset;
}

bool
Font::matches(const std::string& name, bool bold, bool italic) const
{
    return (_bold == bold && _italic == italic && name ==_name);
}

float
Font::leading() const {
    return _fontTag ? _fontTag->leading() : 0.0f;
}

FreetypeGlyphsProvider*
Font::ftProvider() const 
{
    if (_ftProvider.get()) return _ftProvider.get();

    if (_name.empty()) {
        log_error(_("No name associated with this font, can't use device "
		    "fonts (should I use a default one?)"));
        return nullptr;
    }

    _ftProvider = FreetypeGlyphsProvider::createFace(_name, _bold, _italic);
    
    if (!_ftProvider.get()) {
        log_error(_("Could not create a freetype face %s"), _name);
        return nullptr;
    }
    
    return _ftProvider.get();
}

float
Font::ascent(bool embedded) const
{
    if (embedded && _fontTag) return _fontTag->ascent();
    FreetypeGlyphsProvider* ft = ftProvider();
    if (ft) return ft->ascent();
    return 0;
}

float
Font::descent(bool embedded) const
{
    if (embedded && _fontTag) return _fontTag->descent();
    FreetypeGlyphsProvider* ft = ftProvider();
    if (ft) return ft->descent();
    return 0;
}

} // namespace gnash

// Local Variables:
// mode: C++
// indent-tabs-mode: t
// End:
