/***************************************************************************
 *   Copyright (C) 2005 Nicolas Hadacek <hadacek@kde.org>                  *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/
#include "process.h"

#include <tqdatetime.h>

#if defined(NO_KDE)
#  include "common/nokde/nokde_kprocess.h"
#else
#  include <kprocess.h>
#endif
#include "purl.h"
#include "common/common/synchronous.h"

//----------------------------------------------------------------------------
Process::State Process::runSynchronously(Base &process, RunActions actions, uint timeout)
{
  Synchronous sync(timeout);
  TQObject::connect(&process, TQ_SIGNAL(done(int)), &sync, TQ_SLOT(done()));
  TQObject::connect(&process, TQ_SIGNAL(requestSynchronousStop()), &sync, TQ_SLOT(done()));
  if ( (actions & Start) && !process.start(0) ) return process.state();
  Q_ASSERT( process.isRunning() );
  if ( !sync.enterLoop() ) process.timeoutSlot();
  return process.state();
}

//----------------------------------------------------------------------------
Process::Base::Base(TQObject *parent, const char *name)
  : TQObject(parent, name), _state(Stopped)
{
  _process = new TDEProcess(this);
  connect(_process, TQ_SIGNAL(processExited(TDEProcess *)), TQ_SLOT(exited()));
  connect(_process, TQ_SIGNAL(receivedStdout(TDEProcess *, char *, int)), TQ_SLOT(receivedStdout(TDEProcess*, char *, int)));
  connect(_process, TQ_SIGNAL(receivedStderr(TDEProcess *, char *, int)), TQ_SLOT(receivedStderr(TDEProcess*, char *, int)));
  _timer = new TQTimer(this);
  connect(_timer, TQ_SIGNAL(timeout()), TQ_SLOT(timeoutSlot()));
}

Process::Base::~Base()
{
  _process->kill();
}

TQStringList Process::Base::arguments() const
{
  if ( _process==0 ) return TQStringList();
#if defined(NO_KDE)
  return _process->args();
#else
  TQStringList list;
  const TQValueList<TQCString> &args = _process->args();
  for (uint i=0; i<args.count(); i++) list.append(args[i]);
  return list;
#endif
}

void Process::Base::setup(const TQString &executable, const TQStringList &options, bool withWine)
{
  _state = Stopped;
  _timer->stop();
  _process->clearArguments();
  if (withWine) {
    _process->setEnvironment("WINEDEBUG", "-all");
    *_process << TQString("wine");
  }
  *_process << executable;
  *_process << options;
}

bool Process::Base::start(uint timeout)
{
  _state = Stopped;
  _timer->stop();
  _stdout = TQString();
  _stderr = TQString();
#if defined(NO_KDE)
  if ( !_process->start() ) {
#else
  if ( !_process->start(TDEProcess::NotifyOnExit, TDEProcess::All) ) {
#endif
    _state = StartFailed;
    return false;
  }
  _state = Running;
  if (timeout) _timer->start(timeout);
  return true;
}

void Process::Base::timeoutSlot()
{
  _state = Timedout;
  _process->kill();
  emit timeout();
}

int Process::Base::exitCode() const
{
  return _process->exitStatus();
}

void Process::Base::exited()
{
  _state = Exited;
  _timer->stop();
  emit done(exitCode());
}

bool Process::Base::isRunning() const
{
  return _process->isRunning();
}

void Process::Base::writeToStdin(const TQString &s)
{
  TQCString cs = s.latin1();
  _process->writeStdin(cs.data(), cs.length());
}

bool Process::Base::signal(int n)
{
  return _process->kill(n);
}

void Process::Base::setWorkingDirectory(const PURL::Directory &dir)
{
  _process->setWorkingDirectory(dir.path());
}

void Process::Base::setUseShell(bool useShell)
{
  _process->setUseShell(useShell);
}

bool Process::Base::isFilteredLine(const TQString &line)
{
  // "wine" returns all those "libGL warning" that mess up the output...
  return line.startsWith("libGL warning");
}

//----------------------------------------------------------------------------
void Process::StringOutput::receivedStdout(TDEProcess*, char *data, int len)
{
  _stdout += TQString::fromLatin1(data, len);
  emit stdoutDataReceived();
}

void Process::StringOutput::receivedStderr(TDEProcess*, char *data, int len)
{
  _stderr += TQString::fromLatin1(data, len);
  emit stderrDataReceived();
}

//----------------------------------------------------------------------------
void Process::LineBase::receivedStdout(TDEProcess*, char *data, int len)
{
  for (uint i=0; i<uint(len); i++) {
    if ( data[i]=='\r' ) continue;
    if ( data[i]=='\n' ) {
      if ( !isFilteredLine(_stdout) ) addStdoutLine(_stdout);
      _stdout = TQString();
    } else _stdout += TQString::fromLatin1(data + i, 1);
  }
  if ( !_process->isRunning() && !isFilteredLine(_stdout) ) addStdoutLine(_stdout);
  emit stdoutDataReceived();
}

void Process::LineBase::receivedStderr(TDEProcess*, char *data, int len)
{
  for (uint i=0; i<uint(len); i++) {
    if ( data[i]=='\r' ) continue;
    if ( data[i]=='\n' ) {
      if ( !isFilteredLine(_stderr) ) addStderrLine(_stderr);
      _stderr = TQString();
    } else _stderr += TQString::fromLatin1(data + i, 1);
  }
  if ( !_process->isRunning() && !isFilteredLine(_stderr) ) addStderrLine(_stderr);
  emit stderrDataReceived();
}

//----------------------------------------------------------------------------
bool Process::LineOutput::start(uint timeout)
{
  _stdoutLines.clear();
  _stderrLines.clear();
  return Process::LineBase::start(timeout);
}

#include "process.moc"
