/*
    This file is part of the KDE games library
    Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de)
    Copyright (C) 2003 Nicolas Hadacek <hadacek@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/
/*
    $Id$
*/

#include <config.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>

#include <kapplication.h>
#include <ksimpleconfig.h>
#include <kglobal.h>
#include <kstdguiitem.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kdebug.h>
#include <kstaticdeleter.h>

#include "khighscore.h"
#include "kconfigrawbackend.h"
#include "kfilelock.h"

#define GROUP "KHighscore"

class KHighscorePrivate
{
public:
    KHighscorePrivate() {}

    TQString  group;
    bool     global;
};

KFileLock *KHighscore::_lock = 0;
KRawConfig *KHighscore::_config = 0;
static KStaticDeleter<KFileLock> lockSD;
static KStaticDeleter<KRawConfig> configSD;


KHighscore::KHighscore(TQObject* parent)
    : TQObject(parent)
{
    init(true);
}

KHighscore::KHighscore(bool forceLocal, TQObject* parent)
    : TQObject(parent)
{
    init(forceLocal);
}

void KHighscore::init(bool forceLocal)
{
    d = new KHighscorePrivate;
#ifdef HIGHSCORE_DIRECTORY
    d->global = !forceLocal;
    if ( d->global && _lock==0 )
        kdFatal(11002) << "KHighscore::init should be called before!!" << endl;
#else
    d->global = false;
    Q_UNUSED(forceLocal);
#endif
    readCurrentConfig();
}

bool KHighscore::isLocked() const
{
    return (d->global ? _lock->isLocked() : true);
}

void KHighscore::readCurrentConfig()
{
    if ( d->global ) _config->reparseConfiguration();
}

void KHighscore::init(const char *appname)
{
#ifdef HIGHSCORE_DIRECTORY
    const TQString filename =  TQString::fromLocal8Bit("%1/%2.scores")
                              .arg(HIGHSCORE_DIRECTORY).arg(appname);
    int fd = open(filename.local8Bit(), O_RDWR);
    if ( fd<0 ) kdFatal(11002) << "cannot open global highscore file \""
                               << filename << "\"" << endl;
    lockSD.setObject(_lock, new KFileLock(fd));
    configSD.setObject(_config, new KRawConfig(fd, true)); // read-only

    // drop the effective gid
    int gid = getgid();
    setregid(gid, gid);
#else
    Q_UNUSED(appname);
#endif
}

bool KHighscore::lockForWriting(TQWidget *widget)
{
    if ( isLocked() ) return true;

    bool first = true;
    for (;;) {
        kdDebug(11002) << "try locking" << endl;
        // lock the highscore file (it should exist)
        int result = _lock->lock();
        bool ok = ( result==0 );
        kdDebug(11002) << "locking system-wide highscore file res="
                       <<  result << " (ok=" << ok << ")" << endl;
        if (ok) {
            readCurrentConfig();
            _config->setReadOnly(false);
            return true;
        }

        if ( !first ) {
            KGuiItem item = KStdGuiItem::cont();
            item.setText(i18n("Retry"));
            int res = KMessageBox::warningContinueCancel(widget, i18n("Cannot access the highscore file. Another user is probably currently writing to it."), TQString(), item, "ask_lock_global_highscore_file");
            if ( res==KMessageBox::Cancel ) break;
        } else sleep(1);
        first = false;
    }
    return false;
}

void KHighscore::writeAndUnlock()
{
    if ( !d->global ) {
        kapp->config()->sync();
        return;
    }
    if ( !isLocked() ) return;

    kdDebug(11002) << "unlocking" << endl;
    _config->sync(); // write config
    _lock->unlock();
    _config->setReadOnly(true);
}

KHighscore::~KHighscore()
{
    writeAndUnlock();
    delete d;
}

KConfig* KHighscore::config() const
{
    return (d->global ? _config : kapp->config());
}

void KHighscore::writeEntry(int entry, const TQString& key, const TQVariant& value)
{
 Q_ASSERT( isLocked() );
 KConfigGroupSaver cg(config(), group());
 TQString confKey = TQString("%1_%2").arg(entry).arg(key);
 cg.config()->writeEntry(confKey, value);
}

void KHighscore::writeEntry(int entry, const TQString& key, int value)
{
 Q_ASSERT( isLocked() );
 KConfigGroupSaver cg(config(), group());
 TQString confKey = TQString("%1_%2").arg(entry).arg(key);
 cg.config()->writeEntry(confKey, value);
}

void KHighscore::writeEntry(int entry, const TQString& key, const TQString &value)
{
 Q_ASSERT (isLocked() );
 KConfigGroupSaver cg(config(), group());
 TQString confKey = TQString("%1_%2").arg(entry).arg(key);
 cg.config()->writeEntry(confKey, value);
}

TQVariant KHighscore::readPropertyEntry(int entry, const TQString& key, const TQVariant& pDefault) const
{
 KConfigGroupSaver cg(config(), group());
 TQString confKey = TQString("%1_%2").arg(entry).arg(key);
 return cg.config()->readPropertyEntry(confKey, pDefault);
}

TQString KHighscore::readEntry(int entry, const TQString& key, const TQString& pDefault) const
{
 KConfigGroupSaver cg(config(), group());
 TQString confKey = TQString("%1_%2").arg(entry).arg(key);
 return cg.config()->readEntry(confKey, pDefault);
}

int KHighscore::readNumEntry(int entry, const TQString& key, int pDefault) const
{
 KConfigGroupSaver cg(config(), group());
 TQString confKey = TQString("%1_%2").arg(entry).arg(key);
 return cg.config()->readNumEntry(confKey, pDefault);
}

bool KHighscore::hasEntry(int entry, const TQString& key) const
{
 KConfigGroupSaver cg(config(), group());
 TQString confKey = TQString("%1_%2").arg(entry).arg(key);
 return cg.config()->hasKey(confKey);
}

TQStringList KHighscore::readList(const TQString& key, int lastEntry) const
{
 TQStringList list;
 for (int i = 1; hasEntry(i, key) && ((lastEntry > 0) ? (i <= lastEntry) : true); i++) {
	list.append(readEntry(i, key));
 }
 return list;
}

void KHighscore::writeList(const TQString& key, const TQStringList& list)
{
 for (int unsigned i = 1; i <= list.count(); i++) {
	writeEntry(i, key, list[i - 1]);
 }
}

void KHighscore::setHighscoreGroup(const TQString& group)
{
 d->group = group;
}

const TQString& KHighscore::highscoreGroup() const
{
 return d->group;
}

TQString KHighscore::group() const
{
    if ( highscoreGroup().isNull() )
        return (d->global ? TQString() : GROUP);
    return (d->global ? highscoreGroup()
            : TQString("%1_%2").arg(GROUP).arg(highscoreGroup()));
}

bool KHighscore::hasTable() const
{ return config()->hasGroup(group()); }

void KHighscore::sync()
{
    writeAndUnlock();
}

#include "khighscore.moc"