#include "config.h"
#include "extensions/helpers.h"
#include "extensions/imageloader.h"

cDesignerConfig::cDesignerConfig() {
    tmplGlobals = NULL;
    epgImagePathSet = false;
    skinPathSet = false;
    installerSkinPathSet = false;
    logoPathSet = false;
    //Common
    cacheImagesInitial = 1;
    numLogosPerSizeInitial = 30;
    limitLogoCache = 1;
    numLogosMax = 200;
    debugImageLoading = 0;
    replaceDecPoint = false;
    //settings for rerun display
    rerunAmount = 10;
    rerunDistance = 2;
    rerunMaxChannel = 0;
    //max number of custom int and string tokens
    numCustomTokens = 10;
    //remember current skin and theme, osd size and osd fonts
    SetSkin();
    SetOSDSize();
    SetOSDFonts();
    osdLanguage = "";
    setupCloseDoReload = false;
}

cDesignerConfig::~cDesignerConfig() {
    ClearSkinSetups();
    for (vector<cTheme*>::iterator it = themes.begin(); it != themes.end(); it++) {
        delete *it;
    }
}

void cDesignerConfig::SetPathes(void) {
    if (!skinPathSet)
        skinPath = cString::sprintf("%s/skins/", cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
    if (!installerSkinPathSet)
        installerSkinPath = cString::sprintf("%s/installerskins/", cPlugin::ConfigDirectory(PLUGIN_NAME_I18N));
    if (!logoPathSet) 
        logoPath = cString::sprintf("%s/logos/", cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
    if (!epgImagePathSet)
        epgImagePath = cString::sprintf("%s/epgimages/", cPlugin::CacheDirectory(PLUGIN_NAME_I18N));

    dsyslog("skindesigner: using Skin Directory %s", *skinPath);
#ifndef DO_NOT_USE_SKININSTALLER
    dsyslog("skindesigner: using Installer Skin Directory %s", *installerSkinPath);
#endif
    dsyslog("skindesigner: using common ChannelLogo Directory %s", *logoPath);
    dsyslog("skindesigner: using EPG Images Directory %s", *epgImagePath);

    vdrThemesPath = cPlugin::ConfigDirectory(PLUGIN_NAME_I18N);
    size_t found = vdrThemesPath.find("/plugins");
    if (found != string::npos)
        vdrThemesPath = vdrThemesPath.substr(0, found);
    vdrThemesPath += "/themes/";
}

void cDesignerConfig::SetSkinPath(cString path) {
    skinPath = CheckSlashAtEnd(*path);
    skinPathSet = true;
}

void cDesignerConfig::SetInstallerSkinPath(cString path) {
    installerSkinPath = CheckSlashAtEnd(*path);
    installerSkinPathSet = true;
}

void cDesignerConfig::SetLogoPath(cString path) {
    logoPath = CheckSlashAtEnd(*path);
    logoPathSet = true;
}

void cDesignerConfig::SetEpgImagePath(cString path) {
    epgImagePath = CheckSlashAtEnd(*path);
    epgImagePathSet = true;
}

bool cDesignerConfig::GetThemeColor(string &name, tColor &col) {
    if (!tmplGlobals)
        return false;
    return tmplGlobals->GetColor(name, col);
}

void cDesignerConfig::StoreTheme(cTheme *theme) {
    themes.push_back(theme);
}

void cDesignerConfig::ReadSkinFolder(cString &skinFolder, vector<string> *container) {
    DIR *folder = NULL;
    struct dirent *dirEntry;
    folder = opendir(skinFolder);
    if (!folder) {
        esyslog("skindesigner: no skins found in %s", *skinFolder);
        return;
    }
    while (dirEntry = readdir(folder)) {
        string dirEntryName = dirEntry->d_name;
        int dirEntryType = dirEntry->d_type;
        cString subfolder = cString::sprintf("%s%s", *skinFolder, dirEntryName.c_str());
        if (!dirEntryName.compare(".") || !dirEntryName.compare("..") || !dirEntryName.compare("skinrepositories"))
            continue;
        if (dirEntryType != DT_DIR  && dirEntryType != DT_LNK && !DirectoryOk(*subfolder, false))
            continue;
        container->push_back(dirEntryName);
    }
    dsyslog("skindesigner %ld skins found in %s", container->size(), *skinFolder);    
}



void cDesignerConfig::ReadSkins(void) {
    ReadSkinFolder(skinPath, &deliveredSkins);
#ifndef DO_NOT_USE_SKININSTALLER
    ReadSkinFolder(installerSkinPath, &installerSkins);
#endif
    for (vector<string>::iterator it = deliveredSkins.begin(); it != deliveredSkins.end(); it++) {
        skins.push_back(*it);
    }
#ifndef DO_NOT_USE_SKININSTALLER
    for (vector<string>::iterator it = installerSkins.begin(); it != installerSkins.end(); it++) {
        string instSkin = *it;
        bool found = false;
        for (vector<string>::iterator it2 = deliveredSkins.begin(); it2 != deliveredSkins.end(); it2++) {
            if (!instSkin.compare(*it2)) {
                found = true;
                break;
            }
        }
        if (!found)
            skins.push_back(instSkin);
    }
#endif
}

void cDesignerConfig::ClearSkinSetups(void) {
    for (map < string, cSkinSetup* >::iterator it = skinSetups.begin(); it != skinSetups.end(); it++) {
        delete it->second;
    }
    skinSetups.clear();
}

void cDesignerConfig::DebugSkinSetups(void) {
    dsyslog("skindesigner: skin setups:");
    InitSetupIterator();
    cSkinSetup *skinSetup = NULL;
    while (skinSetup = GetNextSkinSetup()) {
        skinSetup->Debug();
    }    
}

void cDesignerConfig::DebugSkinSetupParameters(void) {
    dsyslog("skindesigner: skin setup parameters:");
    for (vector<pair<string, int> >::iterator it = skinSetupParameters.begin(); it != skinSetupParameters.end(); it++) {
        dsyslog("skindesigner: parameter %s value %d", (*it).first.c_str(), (*it).second);
    }
}

void cDesignerConfig::ReadSkinSetup(string skin) {
    cSkinSetup *skinSetup = new cSkinSetup(skin);
    if (skinSetup->ReadFromXML()) {
        //skinSetup->Debug();
        skinSetups.insert(pair<string, cSkinSetup* >(skin, skinSetup));
    }
}

bool cDesignerConfig::GetSkin(string &skin) {
    if (skinIterator == skins.end()) {
        return false;
    }
    skin = *skinIterator;
    skinIterator++;
    return true;
}

cString cDesignerConfig::GetSkinPath(string skin) {
    for (vector<string>::iterator it = deliveredSkins.begin(); it != deliveredSkins.end(); it++) {
        if (!skin.compare(*it)) {
            return skinPath;
        }
    }
    return installerSkinPath;
}

void cDesignerConfig::AddNewSkinRef(string skin) {
    cTheme *theme = new cTheme();
    StoreTheme(theme);
    cSkinDesigner *newSkin = new cSkinDesigner(skin, theme);
    AddSkin(newSkin);
    skins.push_back(skin);
    installerSkins.push_back(skin);
    ReadSkinSetup(skin);
    map < string, cSkinSetup* >::iterator hit = skinSetups.find(skin);
    if (hit != skinSetups.end())
        (hit->second)->TranslateSetup();
}

bool cDesignerConfig::SkinActive(string skin) {
    cSkin *activeSkin = Skins.Current();
    if (activeSkin && !skin.compare(activeSkin->Name()))
        return true;
    return false;
}


void cDesignerConfig::DeleteSkin(string skin) {
    dsyslog("skindesigner: deleting skin %s", skin.c_str());
    cSkin *deleteSkin = NULL;
    bool found = false;
    for (deleteSkin = Skins.First(); deleteSkin; deleteSkin = Skins.Next(deleteSkin)) {
        if (!skin.compare(deleteSkin->Name())) {
            found = true;
            break;
        }
    }
    if (!found || !deleteSkin) {
        esyslog("skindesigner: skin %s to delete not found", skin.c_str());
        return;
    }
    vector<cSkinDesigner*>::iterator delIt;
    for (delIt = skinRefs.begin(); delIt != skinRefs.end(); delIt++) {
        if (*delIt == deleteSkin)
            break;
    }
    if (delIt != skinRefs.end())
        skinRefs.erase(delIt);

    vector<string>::iterator delIt2; 
    for (delIt2 = installerSkins.begin(); delIt2 != installerSkins.end(); delIt2++) {
        if (!skin.compare(*delIt2))
            break;
    }
    if (delIt2 != installerSkins.end())
        installerSkins.erase(delIt2);

    vector<string>::iterator delIt3; 
    for (delIt3 = skins.begin(); delIt3 != skins.end(); delIt3++) {
        if (!skin.compare(*delIt3))
            break;
    }
    if (delIt3 != skins.end())
        skins.erase(delIt3);

    Skins.Del(deleteSkin);

    stringstream deletePath;
    deletePath << *installerSkinPath << skin;
    string delPath = deletePath.str();
    if (FolderExists(delPath)) {
        dsyslog("skindesigner: deleting skin %s", delPath.c_str());
        cString command = cString::sprintf("rm -rf %s", delPath.c_str());
        int ok = system(*command);
        if (!ok) {
            esyslog("skindesigner: error deleting skin %s", delPath.c_str());
        } 
    }
}

cSkinDesigner* cDesignerConfig::GetNextSkinRef(void) {
    if (skinRefsIterator == skinRefs.end()) {
        return NULL;
    }
    cSkinDesigner *skin = *skinRefsIterator;
    skinRefsIterator++;
    return skin;    
}

cSkinSetup* cDesignerConfig::GetSkinSetup(string &skin) {
    map< string, cSkinSetup* >::iterator hit = skinSetups.find(skin);
    if (hit != skinSetups.end()) {
        return hit->second;
    }
    return NULL;
}

cSkinSetup* cDesignerConfig::GetNextSkinSetup(void) {
    if (setupIt == skinSetups.end()) {
        return NULL;
    }
    cSkinSetup* skinSetup = setupIt->second;
    setupIt++;
    return skinSetup;
}

cSkinSetupMenu* cDesignerConfig::GetSkinSetupMenu(string &skin, string &menu) {
    cSkinSetup *skinSetup = GetSkinSetup(skin);
    if (!skinSetup)
        return NULL;
    return skinSetup->GetMenu(menu);
}


void cDesignerConfig::TranslateSetup(void) {
    for (map< string, cSkinSetup* >::iterator it = skinSetups.begin(); it != skinSetups.end(); it++) {
        (it->second)->TranslateSetup();
    }
}

void cDesignerConfig::UpdateSkinSetupParameter(string name, int value) {
    vector<pair<string,int> >::iterator hit;
    for (hit = skinSetupParameters.begin(); hit != skinSetupParameters.end(); hit++) {
        if (!name.compare((*hit).first)) {
            skinSetupParameters.erase(hit);
            break;
        }
    }
    skinSetupParameters.push_back(pair<string,int>(name, value));
}

void cDesignerConfig::SetSkinSetupParameters(void) {
    for (vector < pair <string, int> >::iterator it = skinSetupParameters.begin(); it != skinSetupParameters.end(); it++) {
        string name = (*it).first;
        int value = (*it).second;

        InitSkinIterator();
        string activeSkin = "";
        bool skinFound = false;
        while (GetSkin(activeSkin)) {
            stringstream checkString;
            checkString << activeSkin << ".";
            size_t hit = name.find(checkString.str());
            if (hit != 0)
                continue;
            skinFound = true;
            break;
        }
        if (skinFound) {
            cSkinSetup* skinSetup = GetSkinSetup(activeSkin);
            if (skinSetup != NULL) {
                string paramName = name.substr(activeSkin.size()+1);
                cSkinSetupParameter *skinSetupParam = skinSetup->GetParameter(paramName);
                if (skinSetupParam) {
                    skinSetupParam->value = value;
                    continue;
                }
            }
        }
        esyslog("skindesigner: ERROR Unknown Setup Parameter %s", name.c_str());
    }
}

void cDesignerConfig::ReadSkinRepos(void) {
#ifndef DO_NOT_USE_SKININSTALLER
    skinRepos.Init(*installerSkinPath);
    skinRepos.Read(*installerSkinPath);
    dsyslog("skindesigner: read %d skinrepositories from %s", skinRepos.Count(), *installerSkinPath);
#endif
}

bool cDesignerConfig::CheckVersion(string name, string &neededVersion) {
    cSkinRepo *repo = skinRepos.GetRepo(name);
    if (!repo)
        return false;
    neededVersion = repo->MinSDVersion();

    splitstring minVer(neededVersion.c_str());
    vector<string> tokensMinVer = minVer.split('.', 1);
    if (tokensMinVer.size() != 3) {
        esyslog("skindesigner: incorrect minimumskindesignerversion definition: %s", neededVersion.c_str());
        return false;
    }
    splitstring ver(version.c_str());
    vector<string> tokensVer = ver.split('.', 1);
    if (tokensVer.size() != 3) {
        esyslog("skindesigner: incorrect version definition: %s", version.c_str());
        return false;
    }

    int minVerMajor = atoi(tokensMinVer[0].c_str());
    int minVerMinor = atoi(tokensMinVer[1].c_str());
    int minVerMikro = atoi(tokensMinVer[2].c_str());
    int verMajor = atoi(tokensVer[0].c_str());
    int verMinor = atoi(tokensVer[1].c_str());
    int verMikro = atoi(tokensVer[2].c_str());

    if (minVerMajor < verMajor)
        return true;
    if (minVerMajor > verMajor)
        return false;
    if (minVerMinor < verMinor)
        return true;
    if (minVerMinor > verMinor)
        return false;
    if (minVerMikro <= verMikro)
        return true;
    return false;
}

bool cDesignerConfig::SkinInstalled(string name) {
    for (vector<string>::iterator it = installerSkins.begin(); it != installerSkins.end(); it++) {
        if (!name.compare(*it))
            return true;
    }
    return false;
}

void cDesignerConfig::UpdateGlobals(void) {
    string activeSkin = Setup.OSDSkin;
    cSkinSetup *skinSetupActiveSkin = GetSkinSetup(activeSkin);
    if (skinSetupActiveSkin && tmplGlobals) {
        dsyslog("skindesigner: globals for skin %s adapted to skin setup", activeSkin.c_str());
        skinSetupActiveSkin->AddToGlobals(tmplGlobals);
    }
}

void cDesignerConfig::CheckDecimalPoint(void) {
    struct lconv *pLocInfo;
    pLocInfo = localeconv();
    string decimalPoint = pLocInfo->decimal_point;
    if (decimalPoint.compare(".")) {
        dsyslog("skindesigner: using decimal point %s", decimalPoint.c_str());
        replaceDecPoint = true;
        decPoint = decimalPoint[0];
    }
}

void cDesignerConfig::SetSkin(void) {
    osdSkin =  Setup.OSDSkin;
    osdTheme = Setup.OSDTheme;
}

bool cDesignerConfig::SkinChanged(void) {
    bool changed = false;
    if (osdSkin.compare(Setup.OSDSkin) != 0) {
        dsyslog("skindesigner: skin changed from %s to %s", osdSkin.c_str(), Setup.OSDSkin);
        changed = true;
    }
    if (osdTheme.compare(Setup.OSDTheme) != 0) {
        dsyslog("skindesigner: theme changed from %s to %s", osdTheme.c_str(), Setup.OSDTheme);
        changed = true;
    }
    if (changed)
        SetSkin();
    return changed;
}

void cDesignerConfig::SetOSDSize(void) {
    osdSize.SetWidth(cOsd::OsdWidth());
    osdSize.SetHeight(cOsd::OsdHeight());
    osdSize.SetX(cOsd::OsdLeft());
    osdSize.SetY(cOsd::OsdTop());
}

bool cDesignerConfig::OsdSizeChanged(void) {
   if ((osdSize.Width() != cOsd::OsdWidth()) ||
        (osdSize.Height() != cOsd::OsdHeight()) ||
        (osdSize.X() != cOsd::OsdLeft()) ||
        (osdSize.Y() != cOsd::OsdTop())) {
        dsyslog("skindesigner: osd size changed");
        dsyslog("skindesigner: old osd size: top %d left %d size %d * %d", osdSize.X(), osdSize.Y(), osdSize.Width(), osdSize.Height());
        SetOSDSize();
        dsyslog("skindesigner: new osd size: top %d left %d size %d * %d", osdSize.X(), osdSize.Y(), osdSize.Width(), osdSize.Height());
        return true;
    }
    return false; 
}

void cDesignerConfig::SetOSDFonts(void) {
    fontFix = Setup.FontFix;
    fontOsd = Setup.FontOsd;
    fontSml = Setup.FontSml;
}

bool cDesignerConfig::OsdFontsChanged(void) {
    bool changed = false;
    if (fontFix.compare(Setup.FontFix) != 0) {
        changed = true;
    }
    if (fontOsd.compare(Setup.FontOsd) != 0) {
        changed = true;
    }
    if (fontSml.compare(Setup.FontSml) != 0) {
        changed = true;
    }
    if (changed)
        SetOSDFonts();
    return changed;
}

bool cDesignerConfig::OsdLanguageChanged(void) {
    if (osdLanguage.compare(Setup.OSDLanguage)) {
        osdLanguage = Setup.OSDLanguage;
        return true;
    }
    return false;
}

cString cDesignerConfig::GetSkinRessourcePath(void) {
    return cString::sprintf("%s%s", *skinPath, osdSkin.c_str());
}

cString cDesignerConfig::CheckSlashAtEnd(std::string path) {
    try {
        if (!(path.at(path.size()-1) == '/'))
            return cString::sprintf("%s/", path.c_str());
    } catch (...) {return path.c_str();}
    return path.c_str();
}

bool cDesignerConfig::SetupParse(const char *Name, const char *Value) {
    bool pluginSetupParam = true;
    if      (!strcasecmp(Name, "DebugImageLoading"))       debugImageLoading = atoi(Value);
    else if (!strcasecmp(Name, "CacheImagesInitial"))      cacheImagesInitial = atoi(Value);
    else if (!strcasecmp(Name, "LimitChannelLogoCache"))   limitLogoCache = atoi(Value);
    else if (!strcasecmp(Name, "NumberLogosInitially"))    numLogosPerSizeInitial = atoi(Value);
    else if (!strcasecmp(Name, "NumberLogosMax"))          numLogosMax = atoi(Value);
    else if (!strcasecmp(Name, "RerunAmount"))             rerunAmount = atoi(Value);
    else if (!strcasecmp(Name, "RerunDistance"))           rerunDistance = atoi(Value);
    else if (!strcasecmp(Name, "RerunMaxChannel"))         rerunMaxChannel = atoi(Value);
    else if (!strcasecmp(Name, "NumCustomTokens"))         numCustomTokens = atoi(Value);
    else pluginSetupParam = false;

    if (!pluginSetupParam) {
        skinSetupParameters.push_back(pair <string, int>(Name, atoi(Value)));
    }

    return true;
}