// Resource.cc
// Copyright (c) 2002 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#include "XrmDatabaseHelper.hh"
#include "Resource.hh"
#include "I18n.hh"
#include "StringUtil.hh"

#include <iostream>
#ifdef HAVE_CASSERT
  #include <cassert>
#else
  #include <assert.h>
#endif

using std::cerr;
using std::endl;
using std::string;

namespace FbTk {

ResourceManager::ResourceManager(const char *filename, bool lock_db) :
 m_db_lock(0),
 m_database(0),
 m_filename(filename ? filename : "")
{
    ensureXrmIsInitialize();

    if (lock_db)
        lock();
}

ResourceManager::~ResourceManager() {
    if (m_database)
        delete m_database;
}

bool ResourceManager::m_init = false;

/**
  reloads all resources from resourcefile
  @return true on success else false
*/
bool ResourceManager::load(const char *filename) {
    m_filename = StringUtil::expandFilename(filename).c_str();

    // force reload (lock will ensure it exists)
    if (m_database) {
        delete m_database;
        m_database = 0;
    }

    lock();
    if (!m_database) {
        unlock();
        return false;
    }

    XrmValue value;
    char *value_type;

    //get list and go throu all the resources and load them
    ResourceList::iterator i = m_resourcelist.begin();
    ResourceList::iterator i_end = m_resourcelist.end();
    for (; i != i_end; ++i) {

        Resource_base *resource = *i;
        if (XrmGetResource(**m_database, resource->name().c_str(),
                           resource->altName().c_str(), &value_type, &value))
            resource->setFromString(value.addr);
        else {
            _FB_USES_NLS;
            cerr<<_FBTK_CONSOLETEXT(Error, FailedRead, "Failed to read", "Couldn't load a resource (following)")<<": "<<resource->name()<<endl;
            cerr<<_FBTK_CONSOLETEXT(Error, UsingDefault, "Setting default value", "Falling back to default value for resource")<<endl;
            resource->setDefaultValue();
        }
    }

    unlock();

    return true;
}

/**
 Saves all the resource to a file
 @return 0 on success  else negative value representing the error
*/
bool ResourceManager::save(const char *filename, const char *mergefilename) {
    assert(filename);

    // these must be local variables; otherwise, the memory gets released by
    // std::string, causing weird issues
    string file_str = StringUtil::expandFilename(filename), mergefile_str;
    filename = file_str.c_str();
    if (mergefilename) {
        mergefile_str = StringUtil::expandFilename(mergefilename);
        mergefilename = mergefile_str.c_str();
    }

    // empty database
    XrmDatabaseHelper database;

    string rc_string;
    ResourceList::iterator i = m_resourcelist.begin();
    ResourceList::iterator i_end = m_resourcelist.end();
    //write all resources to database
    for (; i != i_end; ++i) {
        Resource_base *resource = *i;
        rc_string = resource->name() + string(": ") + resource->getString();
        XrmPutLineResource(&*database, rc_string.c_str());
    }

    if (database==0)
        return false;

    //check if we want to merge a database
    if (mergefilename) {
        // force reload of file
        m_filename = mergefilename;
        if (m_database)
            delete m_database;
        m_database = 0;

        lock();

        if (!m_database) {
            unlock();
            return false;
        }

        XrmMergeDatabases(*database, &**m_database); // merge databases
        XrmPutFileDatabase(**m_database, filename); // save database to file

        // don't try to destroy the database (XrmMergeDatabases destroys it)
        *database = 0;
        unlock();
    } else // save database to file
        XrmPutFileDatabase(*database, filename);

    m_filename = filename;
    return true;
}

Resource_base *ResourceManager::findResource(const string &resname) {
   // find resource name
    ResourceList::iterator i = m_resourcelist.begin();
    ResourceList::iterator i_end = m_resourcelist.end();
    for (; i != i_end; ++i) {
        if ((*i)->name() == resname ||
            (*i)->altName() == resname)
            return *i;
    }
    return 0;
}

const Resource_base *ResourceManager::findResource(const string &resname) const {
   // find resource name
    ResourceList::const_iterator i = m_resourcelist.begin();
    ResourceList::const_iterator i_end = m_resourcelist.end();
    for (; i != i_end; ++i) {
        if ((*i)->name() == resname ||
            (*i)->altName() == resname)
            return *i;
    }
    return 0;
}

string ResourceManager::resourceValue(const string &resname) const {
    const Resource_base *res = findResource(resname);
    if (res != 0)
        return res->getString();

    return "";
}

void ResourceManager::setResourceValue(const string &resname, const string &value) {
    Resource_base *res = findResource(resname);
    if (res != 0)
        res->setFromString(value.c_str());

}

void ResourceManager::ensureXrmIsInitialize() {
    if (!m_init) {
        XrmInitialize();
        m_init = true;
    }
}

ResourceManager &ResourceManager::lock() {
    ++m_db_lock;
    // if the lock was zero, then load the database
    if ((m_db_lock == 1 || !m_database) &&
        m_filename != "") {
        m_database = new XrmDatabaseHelper(m_filename.c_str());

        // check that the database loaded ok
        if (m_database && *m_database == 0) {
            // didn't work
            delete m_database;
            m_database = 0;
        }
    }

    return *this;
}

void ResourceManager::unlock() {
    if (--m_db_lock == 0 && m_database) {
        delete m_database;
        m_database = 0;
    }
}

} // end namespace FbTk