#include "FontMapping.h"

#include <fstream>

#define DEFAULT_FONT_FILE "/usr/local/share/fonts/default.ttf"

#define DEFAULT_CONFIG_FILE "/misc/fontmapping.lua"

#define DBG_FONT_MAPPING_MODULE     0


FontMapping* FontMapping::instance = NULL;

int FontMapping::referenceCounter = 0;

FontMapping::FontMapping ()
{
}

FontMapping::~FontMapping ()
{
    fontMap.clear ();
    releaseInstance();
}

FontMapping* FontMapping::getInstance ()
{
#if DBG_FONT_MAPPING_MODULE
    printf ("FontMapping::getInstance(): referenceCounter='%d'.\n", referenceCounter);
#endif
    referenceCounter++;
    if (instance == NULL)
    {
        instance = new FontMapping ();
        instance->loadMapping ();
    }
    return instance;
}

FontMapping* FontMapping::releaseInstance ()
{
#if DBG_FONT_MAPPING_MODULE
    printf ("FontMapping::releaseInstance(): referenceCounter='%d'.\n", referenceCounter);
#endif
    if (! --referenceCounter)
    {
        instance->fontMap.clear ();
        delete instance;
        instance = NULL;
    }
    return instance;
}

/**
 *      Builds the mapping. Called only on startup.
 *
 */
void FontMapping::loadMapping ()
{
    std::ifstream file (DEFAULT_CONFIG_FILE);
    std::string line;

    if ( !file.good ())
    {
#if DBG_FONT_MAPPING_MODULE
        printf ("FontMapping::loadMapping: Warning: Could not open file '%s'.\n", DEFAULT_CONFIG_FILE);
#endif
        return;
    }

    fontMap.clear ();

    while (std::getline (file, line))
    {
        // remove white spaces from the beginning and ending of the line:
        line = trim (line);

        // blank lines are ignored:
        if (line.empty ()) continue;

        // lines with "--" are comments, but it only matches the 1st '-':
        if (line [0] == '-') continue;

        // 'fontEntry' followed by '{' is a new entry, but it only matches "fontEntry":
        if (line.find ("fontEntry") < std::string::npos)
        {
            std::string face;
            std::string style;
            std::string fontfile;

            // Warning: the fontEntry values are case sensitive!
            while (std::getline (file, line))
            {
                size_t posEqual;
                std::string name;
                std::string value;

                posEqual=line.find ('=');
                if (posEqual == std::string::npos) continue ;
                name = trim (line.substr (0, posEqual));
                value = trim (line.substr (posEqual+1));

                if (name == "face")
                    face = trim (value);
                else if (name == "style")
                    style = trim (value);
                else if (name == "file")
                    fontfile = trim (value);

                if (!face.empty () && !style.empty () && !fontfile.empty ())
                {
                    registerFont (face, style, fontfile);
                    break;
                }
            }
        }

        // lines with '}' are the end of an entry:
        if (line [0] == '}') continue;
    }

#if DBG_FONT_MAPPING_MODULE
    // dump do map<Key,T>:
    printf ("FontMapping::loadMapping: fontMap criado para '%s' cotem:\n", DEFAULT_CONFIG_FILE);
    for (FontMapping_t::iterator it = fontMap.begin () ; it != fontMap.end (); it++)
        printf ("\t%s = %s\n", (*it).first.c_str (), (*it).second.c_str ());
#endif

}

/**
 *      Gets the font file from the mapping.
 *      This is a public function that can be called from anywhere in the code
 *      to retrieve path to font files from the fontMap.
 *
 *      Usage:
 *            std::string filename (FontMapping::getInstance ()->fontToFile ("face", "style"));
 *      Example:
 *            std::string filename (FontMapping::getInstance ()->fontToFile ("LiberationSerif", "Bold"));
 *
 */
std::string FontMapping::fontToFile (std::string face, std::string style)
{
    std::string key = createKey (face, style);
    if (fontMap.count (key) > 0)
    {
        return fontMap[key];
    }
    else
    {
#if DBG_FONT_MAPPING_MODULE
        printf ("FontMapping::fontToFile: Could not find font '%s'. Using '%s'.\n", key.c_str (), DEFAULT_FONT_FILE);
#endif
        return DEFAULT_FONT_FILE;
    }
}

/**
 *      Only inserts font file in the mapping for future use.
 *      This is a public function that can be called from anywhere in the code
 *      to add lines to the fontMap.
 *
 *      Usage:
 *            FontMapping::getInstance ()->registerFont ("face", "style", "full path");
 *      Example:
 *            FontMapping::getInstance ()->registerFont ("LiberationSerif", "Bold", "/usr/share/fonts/LiberationSerif-Bold.ttf");
 *
 *      Important: If a fonEntry already exists, it is not updated in the fontMap.
 *
 */
void FontMapping::registerFont (std::string face, std::string style, std::string file)
{
    std::string key = createKey (face, style);
    fontMap[key] = file;
}

/**
 *      Given a 'face' and a 'style', create a key to identify this font in the mapping.
 *
 */
std::string FontMapping::createKey (std::string face, std::string style)
{
    std::string key;

    if  (style.empty ())
    {
        key = face + "-Regular";
    }
    else
    {
        key = face + "-" + style;
    }

    std::transform(key.begin(), key.end(), key.begin(), (int (*)(int)) std::tolower);

    return key;
}

std::string FontMapping::trim (std::string const& source, char const* delims)
{
    std::string result (source);
    std::string::size_type index = result.find_last_not_of (delims);

    if (index != std::string::npos)
        result.erase (++index);
    index = result.find_first_not_of (delims);
    if (index != std::string::npos)
        result.erase (0, index);
    else
        result.erase ();

    return result;
}

