/*
 * Copyright (c) 2002-2004 Christian Loose <christian.loose@kdemail.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program 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 program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "repository.h"

#include <tqdir.h>
#include <tqfile.h>
#include <tqstring.h>

#include <kapplication.h>
#include <kconfig.h>
#include <kdirwatch.h>
#include <kstandarddirs.h>

#include "sshagent.h"


struct Repository::Private
{
    Private() : compressionLevel(0) {}

    TQString     configFileName;

    TQString     workingCopy;
    TQString     location;

    TQString     client;
    TQString     rsh;
    TQString     server;
    int         compressionLevel;
    bool        retrieveCvsignoreFile;

    void readConfig();
    void readGeneralConfig();
};



Repository::Repository()
    : TQObject()
    , DCOPObject("CvsRepository")
    , d(new Private)
{
    d->readGeneralConfig();

    // other cvsservice instances might change the configuration file
    // so we watch it for changes
    d->configFileName = locate("config", "cvsservicerc");
    KDirWatch* fileWatcher = new KDirWatch(this);
    connect(fileWatcher, TQT_SIGNAL(dirty(const TQString&)),
            this, TQT_SLOT(slotConfigDirty(const TQString&)));
    fileWatcher->addFile(d->configFileName);
}


Repository::Repository(const TQString& repository)
    : TQObject()
    , DCOPObject()
    , d(new Private)
{
    d->location = repository;
    d->readGeneralConfig();
    d->readConfig();

    // other cvsservice instances might change the configuration file
    // so we watch it for changes
    d->configFileName = locate("config", "cvsservicerc");
    KDirWatch* fileWatcher = new KDirWatch(this);
    connect(fileWatcher, TQT_SIGNAL(dirty(const TQString&)),
            this, TQT_SLOT(slotConfigDirty(const TQString&)));
    fileWatcher->addFile(d->configFileName);
}


Repository::~Repository()
{
    delete d;
}


TQString Repository::cvsClient() const
{
    TQString client(d->client);

    // suppress reading of the '.cvsrc' file
    client += " -f";

    // we don't need the command line option if there is no compression level set
    if( d->compressionLevel > 0 )
    {
        client += " -z" + TQString::number(d->compressionLevel) + " ";
    }

    return client;
}


TQString Repository::clientOnly() const
{
    return d->client;
}


TQString Repository::rsh() const
{
    return d->rsh;
}


TQString Repository::server() const
{
    return d->server;
}


bool Repository::setWorkingCopy(const TQString& dirName)
{
    const TQFileInfo fi(dirName);
    const TQString path = fi.absFilePath();

    // is this really a cvs-controlled directory?
    const TQFileInfo cvsDirInfo(path + "/CVS");
    if( !cvsDirInfo.exists() || !cvsDirInfo.isDir() ||
        !TQFile::exists( cvsDirInfo.filePath() + "/Entries" ) ||
        !TQFile::exists( cvsDirInfo.filePath() + "/Repository" ) ||
        !TQFile::exists( cvsDirInfo.filePath() + "/Root" ) )
        return false;

    d->workingCopy = path;
    d->location    = TQString();

    // determine path to the repository
    TQFile rootFile(path + "/CVS/Root");
    if( rootFile.open(IO_ReadOnly) )
    {
        TQTextStream stream(&rootFile);
        d->location = stream.readLine();
    }
    rootFile.close();

    // add identities (ssh-add) to ssh-agent
    // TODO CL make sure this is called only once
    if( d->location.contains(":ext:", false) > 0 )
    {
        SshAgent ssh;
        ssh.addSshIdentities();
    }

    TQDir::setCurrent(path);
    d->readConfig();

    return true;
}


TQString Repository::workingCopy() const
{
    return d->workingCopy;
}


TQString Repository::location() const
{
    return d->location;
}


bool Repository::retrieveCvsignoreFile() const
{
    return d->retrieveCvsignoreFile;
}


void Repository::slotConfigDirty(const TQString& fileName)
{
    if( fileName == d->configFileName )
    {
        // reread the configuration data from disk
        kapp->config()->reparseConfiguration();
        d->readConfig();
    }
}


void Repository::Private::readGeneralConfig()
{
    KConfig* config = kapp->config();

    // get path to cvs client programm
    config->setGroup("General");
    client = config->readPathEntry("CVSPath", "cvs");
}


void Repository::Private::readConfig()
{
    KConfig* config = kapp->config();

    // Sometimes the location can be unequal to the entry in the CVS/Root.
    //
    // This can happen when the checkout was done with a repository name
    // like :pserver:user@cvs.kde.org:/home/kde. When cvs then saves the
    // name into the .cvspass file, it adds the default cvs port to it like
    // this :pserver:user@cvs.kde.org:2401/home/kde. This name is then also
    // used for the configuration group.
    //
    // In order to be able to read this group, we then have to manually add
    // the port number to it.
    TQString repositoryGroup = TQString::fromLatin1("Repository-") + location;
    if( !config->hasGroup(repositoryGroup) )
    {
        // find the position of the first path separator
        const int insertPos = repositoryGroup.find('/');
        if( insertPos > 0 )
        {
            // add port to location
            // (1) :pserver:user@hostname.com:/path
            if( repositoryGroup.at(insertPos - 1) == ':' )
                repositoryGroup.insert(insertPos, "2401");
            // (2) :pserver:user@hostname.com/path
            else
                repositoryGroup.insert(insertPos, ":2401");
        }
    }

    config->setGroup(repositoryGroup);

    // should we retrieve the CVSROOT/cvsignore file from the cvs server?
    retrieveCvsignoreFile = config->readBoolEntry("RetrieveCvsignore", false);
    
    // see if there is a specific compression level set for this repository
    compressionLevel = config->readNumEntry("Compression", -1);

    // use default global compression level instead?
    if( compressionLevel < 0 )
    {
        KConfigGroupSaver cs(config, "General");
        compressionLevel = config->readNumEntry("Compression", 0);
    }

    // get remote shell client to access the remote repository
    rsh = config->readPathEntry("rsh");

    // get program to start on the server side
    server = config->readEntry("cvs_server");
}


#include "repository.moc"
