#include "fontmanager.h"
#include "../config.h"
#include <ft2build.h>   
#include FT_FREETYPE_H

using namespace std;

cMutex cFontManager::mutex;

cFontManager::cFontManager(void) {
}

cFontManager::~cFontManager(void) {
    DeleteFonts();
}

void cFontManager::Debug(void) {
    dsyslog("skindesigner: fontmanager fonts available:");
    for (map < string, map< int, cFont* > >::iterator fts = fonts.begin(); fts != fonts.end(); fts++) {
        dsyslog("skindesigner: FontName %s", fts->first.c_str());
        for (map<int, cFont*>::iterator ftSizes = (fts->second).begin(); ftSizes != (fts->second).end(); ftSizes++) {
            int confHeight = ftSizes->first;
            int realHeight = (ftSizes->second)->Height();
            dsyslog("skindesigner: fontSize %d, fontHeight %d, ratio %f", confHeight, realHeight, (double)confHeight / (double)realHeight);
        }
    }
}

void cFontManager::ListAvailableFonts(void) {
    cStringList availableFonts;
    cFont::GetAvailableFontNames(&availableFonts);
    int numFonts = availableFonts.Size();
    esyslog("skindesigner: %d Fonts available:", numFonts);
    for (int i=0; i<numFonts; i++) {
        esyslog("skindesigner: font %d: %s", i, availableFonts[i]);
    }
}

void cFontManager::DeleteFonts() {
    cMutexLock MutexLock(&mutex);
    for(map<string, map<int,cFont*> >::iterator it = fonts.begin(); it != fonts.end(); it++) {
        for(map<int,cFont*>::iterator it2 = (it->second).begin(); it2 != (it->second).end(); it2++) {
            delete it2->second;
        }
    }
    fonts.clear();
}

int cFontManager::Width(string fontName, int fontSize, const char *text) {
    cMutexLock MutexLock(&mutex);
    if (!text)
        return 0;
    cFont *font = GetFont(fontName, fontSize);
    //if not already cached, load it new
    if (!font)
        InsertFont(fontName, fontSize);
    font = GetFont(fontName, fontSize);
    if (!font)
        return 0;
    int width = font->Width(text);
    return width;
}

int cFontManager::Height(string fontName, int fontSize) {
    cMutexLock MutexLock(&mutex);
    cFont *font = GetFont(fontName, fontSize);
    //if not already cached, load it new
    if (!font)
        InsertFont(fontName, fontSize);
    font = GetFont(fontName, fontSize);
    if (!font)
        return 0;
    return font->Height();
}

cFont *cFontManager::Font(string fontName, int fontSize) {
    cMutexLock MutexLock(&mutex);
    cFont *font = GetFont(fontName, fontSize);
    //if not already cached, load it new
    if (!font)
        InsertFont(fontName, fontSize);
    font = GetFont(fontName, fontSize);
    return font;
}

cFont *cFontManager::FontUncached(string fontName, int fontSize) {
    cMutexLock MutexLock(&mutex);
    cFont *font = CreateFont(fontName, fontSize);
    return font;
}

/********************************************************************************
* Private Functions 
********************************************************************************/

cFont *cFontManager::CreateFont(string name, int size) {
    cMutexLock MutexLock(&mutex);
    cFont *fontTmp = cFont::CreateFont(name.c_str(), size);
    if (!fontTmp)
        fontTmp = cFont::CreateFont(Setup.FontOsd, size);
    int realHeight = fontTmp->Height();
    delete fontTmp;
    cFont *font = cFont::CreateFont(name.c_str(), (double)size / (double)realHeight * (double)size);
    if (!font)
        font = cFont::CreateFont(Setup.FontOsd, (double)size / (double)realHeight * (double)size);
    return font;
}

void cFontManager::InsertFont(string name, int size) {
    if (FontExists(name, size))
        return;
    cFont *newFont = CreateFont(name, size);
    if (!newFont)
        return;
    map < string, map< int, cFont* > >::iterator hit = fonts.find(name);
    if (hit != fonts.end()) {
        (hit->second).insert(pair<int, cFont*>(size, newFont));
    } else {
        map<int, cFont*> fontsizes;
        fontsizes.insert(pair<int, cFont*>(size, newFont));
        fonts.insert(pair<string, map<int, cFont*> >(name, fontsizes));
    }
}

bool cFontManager::FontExists(string name, int size) {
    map < string, map< int, cFont* > >::iterator hit = fonts.find(name);
    if (hit == fonts.end())
        return false;
    map< int, cFont* >::iterator hit2 = (hit->second).find(size);
    if (hit2 == (hit->second).end())
        return false;
    return true;
}

cFont *cFontManager::GetFont(string name, int size) {
    map< string, map<int,cFont*> >::iterator hitName = fonts.find(name);
    if (hitName == fonts.end())
        return NULL;
    map<int,cFont*>::iterator hitSize = (hitName->second).find(size);
    if (hitSize == (hitName->second).end())
        return NULL;
    return hitSize->second;
}

int cFontManager::GetFontHeight(const char *name, int height, int charWidth) {
    FT_Library library;
    FT_Face face;
    cString fontFileName = cFont::GetFontFileName(name);
    
    int descender = 0;
    int y_ppem = 0;
    int error = FT_Init_FreeType(&library);
    if (error) return 0;
    error = FT_New_Face(library, fontFileName, 0, &face);
    if (error) return 0;
    error = FT_Set_Char_Size(face, charWidth * 64, height * 64, 0, 0);
    if (error) return 0;

    descender = face->size->metrics.descender/64;
    y_ppem = face->size->metrics.y_ppem;
    int realHeight = y_ppem + descender;

    FT_Done_Face(face);
    FT_Done_FreeType(library);

    return realHeight;
}

bool cFontManager::FontInstalled(string fontName) {
    cStringList availableFonts;
    cFont::GetAvailableFontNames(&availableFonts);
    int numFonts = availableFonts.Size();
    string compare = fontName + ":";
    for (int i=0; i<numFonts; i++) {
        string currentFont = availableFonts[i];
        if (currentFont.find(compare) == 0) {
            return true;
        }
    }
    return false;
}