/***************************************************************************
 *   Copyright (C) 2003 by Sylvain Joyeux                                  *
 *   sylvain.joyeux@m4x.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 "aptcache.h"
#include "apt.h"
#include "debug.h"

#include "regexps.h"

#include <qstringlist.h>
#include <qregexp.h>

#include <kdebug.h>

#include <stdlib.h>
#include <signal.h>
#include <errno.h>


AptCache::AptCache()
{
  connect(&m_process, SIGNAL(receivedStderr(KProcess*, char*, int)),
    this, SLOT(receivedStdErr(KProcess*, char*, int )));
  connect(&m_process, SIGNAL(receivedStdout(KProcess*, char*, int)),
    this, SLOT(receivedStdOut(KProcess*, char*, int )));
}
AptCache::~AptCache() {}




static QStringList received(QString& buffer, char* input, int input_len)
{
  buffer += QString::fromLatin1(input, input_len);
  QStringList ret = QStringList::split('\n', buffer, true);
  if (!buffer.endsWith("\n"))
  {
    buffer = ret.last();
    ret.pop_back();
  }
  else
    buffer = "";

  return ret;
}
void AptCache::receivedStdErr( KProcess * /*process*/, char * buffer, int len )
{
  static QRegExp rx_we("(W|E):\\s+(.*)");

  QStringList lines = received(m_received_err, buffer, len);
  for (QStringList::ConstIterator i = lines.begin(); i != lines.end(); ++i)
  {
    if (rx_we.exactMatch(*i))
    {
      if (rx_we.cap(1) == "E") emit token("error", rx_we.cap(2));
      else emit token("warning", rx_we.cap(2));
    }
    else
    {
      kdDebug() << "Unmatched error : " << *i << endl;
    }
  }
}
void AptCache::receivedStdOut( KProcess * /*process*/, char * buffer, int len )
{
  QStringList lines = received(m_received_out, buffer, len);
  (this->*m_receive)(lines);
}




void AptCache::clear()
{
  m_process.clearArguments();
  m_attribute = "";
  m_received_err = "";
  m_received_out = "";
}

bool AptCache::search(const QString& expression)
{
  clear();

  m_process.setEnvironment("LANGUAGE", "C");
  m_process << "apt-cache" << "search";
  m_process << QStringList::split(" ", expression);
  m_receive = &AptCache::receiveSearch;
  return m_process.start(KProcess::Block, KProcess::Stdout );
}

void AptCache::receiveSearch(const QStringList& lines)
{
  static QRegExp rx_parse("([^ ]+) - (.*)");

  QStringList::ConstIterator i;
  for (i = lines.begin(); i != lines.end(); ++i)
  {
    if ((*i).isEmpty()) continue;

    if (!rx_parse.exactMatch(*i))
    {
      kdDebug(DEBUG_ZONE) << "Parsing error. Line is " << *i << endl;
      continue;
    }

    emit token("package", rx_parse.cap(1));
      emit token("short_desc", rx_parse.cap(2));

    kdDebug(DEBUG_ZONE) << "Found package : " << rx_parse.cap(1) << " - " << rx_parse.cap(2) << endl;
  }
}

bool AptCache::show(const QString& package)
{
  clear();

  m_process.setEnvironment("LANGUAGE", "C");
  m_process << "apt-cache" << "show" << package;
  m_receive = &AptCache::receiveShow;
  return m_process.start(KProcess::Block, KProcess::Stdout );
}

void AptCache::receiveShow(const QStringList& lines)
{
  static bool pkgfield = false, insert_newline = false;
  static int indent = 0;

  static QRegExp rx_attribute("([\\w-]+): (.*)");
  static const QString pkg_fields[] =
    { "Suggests", "Replaces", "Depends", "Conflicts", QString::null };

  QStringList::ConstIterator i;
  for (i = lines.begin(); i != lines.end(); ++i)
  {
    QString data(*i);
    if (data.isEmpty()) continue;

    if (rx_attribute.exactMatch(*i))
    {
      m_attribute = rx_attribute.cap(1);
      data = rx_attribute.cap(2);

      if (m_attribute != "Package")
        emit token("field", m_attribute);

      insert_newline = pkgfield = false;
      indent = 0;

      const QString * test_field;
      for (test_field = pkg_fields; !test_field -> isNull(); ++test_field)
        if (*test_field == m_attribute)
        {
          pkgfield = true;
          break;
        }
    }

    if (m_attribute == "Package")
      emit token("package", data);
    else if (pkgfield)
      parse_pkgfield(data);
    else
    {
      int new_indent = data.find( QRegExp("[^\\s]") );

      // new_indent > 0 means that we are in a multi-line
      // field. Those lines always begin with " ", so we want
      // to drop it.
      if (new_indent > 0) --new_indent;

      if (new_indent != indent)
      {
        emit token("indent", QString::number(new_indent) );
        indent = new_indent;
        insert_newline = false;
      }

      if (data == " .")
      {
        if (insert_newline)
          emit token("data", "\n");
      }
      else
      {
        if (insert_newline)
          emit token("data", "\n" + data);
        else
          emit token("data", data);
      }

      insert_newline = true;
    }
  }
}

void AptCache::parse_pkgfield(const QString& data)
{
  QStringList split(QStringList::split(",", data));
  for (QStringList::ConstIterator i = split.begin(); i != split.end(); ++i)
  {
    if (i != split.begin()) emit token("data", ", ");

    QStringList bar(QStringList::split("|", *i));
    for (QStringList::ConstIterator j = bar.begin(); j != bar.end(); ++j)
    {
      if (j != bar.begin()) emit token("data", " | ");
      QString pkg, remaining;

      int paren = (*j).find('(');
      if (paren != -1)
      {
        pkg = (*j).left(paren - 1);
        remaining = (*j).right((*j).length() - paren + 1);
      }
      else
      {
        pkg = (*j);
      }

      pkg = pkg.stripWhiteSpace();
      remaining = remaining.stripWhiteSpace();

      emit token("package_link", pkg);
      if (!remaining.isEmpty()) emit token("data", " " + remaining);
    }
  }
}

bool AptCache::policy( const QString & package )
{
  clear();

  m_process.setEnvironment("LANGUAGE", "C");
  m_process << "apt-cache" << "policy" << package;
  m_receive = &AptCache::receivePolicy;
  return m_process.start(KProcess::Block, KProcess::Stdout );
}

void AptCache::receivePolicy(const QStringList& lines)
{
  static QRegExp rx_pkgname("(\\w[\\w+-.]+):");
  static QRegExp rx_location("^\\s*\\d+\\s[^\\d]");

  for(QStringList::ConstIterator l = lines.begin(); l != lines.end(); ++l)
  {
    if ((*l).isEmpty()) continue;

    QString data( (*l).stripWhiteSpace() );
    if (rx_pkgname.exactMatch(*l))
      emit token("package", rx_pkgname.cap(1));
    else if (data.startsWith("Installed:", false))
    {
      data = data.right(data.length() - 11);
      emit token("installed", data);
      m_installed = data;
    }
    else if (data.startsWith("Candidate:", false))
    {
      data = data.right(data.length() - 11);
      emit token("candidate", data);
      m_candidate = data;
    }
    else if (data.startsWith("Version table:", false))
      emit token("version_table", QString::null);
    else if (rx_location.search(data) > -1)
      emit token("location", data);
    else
    {
      if (data.startsWith("*** "))
        data = data.right( data.length() - 4 );

      if (match_dversion(data.section(' ', 0, 0)))
        emit token("version", data);
    }
  }
}

QString AptCache::policy_installed() const
{ return m_installed; }
QString AptCache::policy_candidate() const
{ return m_candidate; }


#include "aptcache.moc"