/* -*- c++ -*-
*
* corelauncher.cpp
*
* Copyright (C) 2004 Petter Stokke <ummo@hellokitty.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/

#include "corelauncher.h"

#include "coreterminationdialog.h"
#include "hostmanager.h"
#include "hostiface.h"

#include <kdebug.h>
#include <klocale.h>
#include <dcopclient.h>
#include <kprocio.h>
#include <kmessagebox.h>
#include <qdir.h>

CoreLauncher::CoreLauncher(QObject* parent, const char* name)
    : QObject(parent, name)
{
    m_cores.setAutoDelete(true);
    m_hosts = new HostManager(this);
    connect(m_hosts, SIGNAL(hostListUpdated()), this, SLOT(hostListUpdated()));
    m_dcop = DCOPClient::mainClient();
    m_dcop->setNotifications(true);
    connect(m_dcop, SIGNAL(applicationRegistered(const QCString&)), this, SLOT(applicationRegistered(const QCString&)));
    connect(m_dcop, SIGNAL(applicationRemoved(const QCString&)), this, SLOT(applicationRemoved(const QCString&)));
    QCString kName = "kmldonkey";
    QCStringList apps = m_dcop->registeredApplications();
    QCStringList::iterator it;
    m_kmldonkeyRunning = false;
    for (it = apps.begin(); it != apps.end(); ++it) {
	if (*it == kName) {
	    m_kmldonkeyRunning = true;
	    break;
	}
    }

    launchCores(m_kmldonkeyRunning ? -1 : HostInterface::AtKDEStart);
}

CoreLauncher::~CoreLauncher()
{
    shutdownCores(-1);
}

bool CoreLauncher::isCoreRunning(const QString& id) const
{
    return m_cores[id];
}

bool CoreLauncher::isValidCore(const QString& id) const
{
    return m_hosts->validHostName(id);
}

QStringList CoreLauncher::coreNames() const
{
    return m_hosts->hostList();
}

void CoreLauncher::startCore(const QString& id)
{
    HostInterface* i = m_hosts->hostProperties(id);
    if (i) startCore(i);
}

void CoreLauncher::startCore(HostInterface* i)
{
    if (m_cores[i->name()]) return;
    CoreProcess* core = new CoreProcess();
    connect(core, SIGNAL(processExited(KProcess*)), this, SLOT(processExited(KProcess*)));
    m_cores.replace(i->name(), core);
    core->startCore(i);
}

void CoreLauncher::stopCore(const QString& id)
{
    if (m_cores[id])
	m_cores[id]->killCore();
}

void CoreLauncher::stopCore(HostInterface* i)
{
    stopCore(i->name());
}

void CoreLauncher::launchCores(int filter)
{
    QStringList l(m_hosts->hostList(HostInterface::Managed));
    QStringList::Iterator it;
    for (it = l.begin(); it != l.end(); ++it) {
	if (m_cores[*it]) continue;
	HostInterface* i = m_hosts->hostProperties(*it);
	if (filter == -1 || i->startupMode() == filter)
	    startCore(i);
    }
}

void CoreLauncher::shutdownCores(int filter)
{
    QDictIterator<CoreProcess> it(m_cores);
    for (; it.current(); ++it) {
	HostInterface* i = m_hosts->hostProperties(it.currentKey());
	if (filter == -1 || (!i && filter == -2) || (i && i->startupMode() == filter)
	    || (!m_kmldonkeyRunning && filter == -2 && i->startupMode() == HostInterface::AtKMLDonkeyStart)) {
	    it.current()->killCore();
	    if (filter == -1)
		it.current()->detach();
	}
    }
}

void CoreLauncher::processExited(KProcess* c)
{
    CoreProcess* core = dynamic_cast<CoreProcess*>(c);
    if (!core) return;
    m_cores.take(core->id());
    if (core->normalExit())
	kdDebug() << "Process '" << core->id() << "' terminated with return code " << core->exitStatus() << endl;
    else
	kdDebug() << "Process '" << core->id() << "' was terminated by a signal." << endl;
    if (!core->isDying()) {
	CoreTerminationDialog* dlg = new CoreTerminationDialog(core);
	connect(dlg, SIGNAL(restartCore(const QString&)), SLOT(startCore(const QString&)));
	dlg->show();
    }
    delete core;
}

void CoreLauncher::hostListUpdated()
{
    shutdownCores(-2);
    launchCores(m_kmldonkeyRunning ? -1 : HostInterface::AtKDEStart);
}

void CoreLauncher::applicationRegistered(const QCString& appId)
{
    if (appId == QCString("kmldonkey")) {
	m_kmldonkeyRunning = true;
	launchCores(HostInterface::AtKMLDonkeyStart);
    }
}

void CoreLauncher::applicationRemoved(const QCString& appId)
{
    if (appId == QCString("kmldonkey")) {
	m_kmldonkeyRunning = false;
	shutdownCores(HostInterface::AtKMLDonkeyStart);
    }
}



CoreProcess::CoreProcess()
    : KProcIO()
    , m_id(QString::null)
    , m_dying(false)
{
    connect(this, SIGNAL(readReady(KProcIO*)), this, SLOT(outputReady(KProcIO*)));
}

const QString& CoreProcess::id() const
{
    return m_id;
}

bool CoreProcess::isDying() const
{
    return m_dying;
}

void CoreProcess::startCore(HostInterface* i)
{
    m_id = i->name();
    kdDebug() << "Starting process " << m_id << endl;
    *this << i->binaryPath().path();
    kdDebug() << "Set executable path: '" << i->binaryPath().path() << "'" << endl;
    if (!i->rootDirectory().isLocalFile() || !i->rootDirectory().hasPath()) {
	setWorkingDirectory(QDir::home().canonicalPath());
	kdDebug() << "Set working directory (defaulting): '" << QDir::home().canonicalPath() << "'" << endl;
    } else {
	setWorkingDirectory(i->rootDirectory().path());
	kdDebug() << "Set working directory: '" << i->rootDirectory().path() << "'" << endl;
    }
    kdDebug() << "Starting process..." << endl;
    bool rv = start(KProcess::NotifyOnExit, true);
    kdDebug() << "Process started: " << (rv ? "true" : "false") << endl;
}

void CoreProcess::killCore()
{
    m_dying = true;
    kdDebug() << "Process termination requested for " << m_id << endl;
    bool rv = kill();
    kdDebug() << "Termination: " << (rv ? "true" : "false") << endl;
}

QString CoreProcess::output() const
{
    return m_output.join("\n");
}

void CoreProcess::outputReady(KProcIO*)
{
    QString l;
    while (readln(l) != -1) {
	kdDebug() << "Output from process '" << m_id << "': '" << l << "'" << endl;
	m_output.append(l);
	while (m_output.count() > 128)
	    m_output.pop_front();
    }
}





#include "corelauncher.moc"
