/***************************************************************************
    copyright            : (C) 2006 by Robby Stephenson
    email                : robby@periapsis.org
 ***************************************************************************/

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

#include "dialog.h"
#include "providerloader.h"
#include "../gui/listview.h"
#include "../latin1literal.h"
#include "../tellico_utils.h"
#include "../tellico_debug.h"

#include <klocale.h>
#include <kpushbutton.h>
#include <kconfig.h>
#include <kiconloader.h>
#include <kstatusbar.h>
#include <kio/job.h>
#include <kio/netaccess.h>
#include <kaccelmanager.h>
#include <knewstuff/entry.h>
#include <knewstuff/provider.h>
#include <ktempfile.h>

#include <tqlabel.h>
#include <tqtextedit.h>
#include <tqlayout.h>
#include <tqwhatsthis.h>
#include <tqregexp.h>
#include <tqvbox.h>
#include <tqimage.h>
#include <tqtimer.h>
#include <tqprogressbar.h>

#if KDE_IS_VERSION(3,4,90)
#define ENTRYNAME(e) e->name(m_lang)
#define ENTRYSUMM(e) e->summary(m_lang)
#define ENTRYEMAIL(e) e->authorEmail()
#else
#define ENTRYNAME(e) e->name()
#define ENTRYSUMM(e) e->summary()
#define ENTRYEMAIL(e) TQString()
#endif

namespace {
  static const int NEW_STUFF_MIN_WIDTH = 600;
  static const int NEW_STUFF_MIN_HEIGHT = 400;
  static const int PROGRESS_STATUS_ID = 0;
}

using Tellico::NewStuff::Dialog;

class Dialog::Item : public GUI::ListViewItem {
public:
  Item(GUI::ListView* parent) : GUI::ListViewItem(parent) {}

  InstallStatus status() const { return m_status; }
  void setStatus(InstallStatus status) {
    m_status = status;
    if(m_status  == Current) {
      setPixmap(0, SmallIcon(TQString::fromLatin1("ok")));
    } else if(m_status == OldVersion) {
      setPixmap(0, SmallIcon(TQString::fromLatin1("reload")));
    }
  }

  TQString key(int col, bool asc) const {
    if(col == 2 || col == 3) {
      TQString s;
      s.sprintf("%08d", text(col).toInt());
      return s;
    } else if(col == 4) {
      TQString s;
      TQDate date = KGlobal::locale()->readDate(text(col));
      s.sprintf("%08d", date.year() * 366 + date.dayOfYear());
      return s;
    }
    return GUI::ListViewItem::key(col, asc);
  }

private:
  InstallStatus m_status;
};

Dialog::Dialog(NewStuff::DataType type_, TQWidget* parent_)
    : KDialogBase(KDialogBase::Plain, i18n("Get Hot New Stuff"), 0, (KDialogBase::ButtonCode)0, parent_)
    , m_manager(new Manager(TQT_TQOBJECT(this)))
    , m_type(type_)
    , m_timer(new TQTimer(this))
    , m_cursorSaver(new GUI::CursorSaver())
    , m_tempPreviewImage(0)
    , m_lastPreviewItem(0) {

  m_lang = KGlobal::locale()->language();

  TQFrame* frame = plainPage();
  TQBoxLayout* boxLayout = new TQVBoxLayout(frame, 0, KDialog::spacingHint());

  m_split = new TQSplitter(Qt::Vertical, frame);
  boxLayout->addWidget(m_split);

  m_listView = new GUI::ListView(m_split);
  m_listView->setAllColumnsShowFocus(true);
  m_listView->setSelectionMode(TQListView::Single);
  m_listView->addColumn(i18n("Name"));
  m_listView->addColumn(i18n("Version"));
  m_listView->addColumn(i18n("Rating"));
  m_listView->addColumn(i18n("Downloads"));
  m_listView->addColumn(i18n("Release Date"));
  m_listView->setSorting(2, false);
  m_listView->setResizeMode(TQListView::AllColumns);
  connect(m_listView, TQT_SIGNAL(clicked(TQListViewItem*)), TQT_SLOT(slotSelected(TQListViewItem*)));
  TQWhatsThis::add(m_listView, i18n("This is a list of all the items available for download. "
                                   "Previously installed items have a checkmark icon, while "
                                   "items with new version available have an update icon"));

  TQWidget* widget = new TQWidget(m_split);
  TQBoxLayout* boxLayout2 = new TQVBoxLayout(widget, 0, KDialog::spacingHint());

  m_iconLabel = new TQLabel(widget);
  m_iconLabel->setAlignment(TQt::AlignTop | TQt::AlignLeft);
  m_iconLabel->setMargin(0);

  m_nameLabel = new TQLabel(widget);
  TQFont font = m_nameLabel->font();
  font.setBold(true);
  font.setItalic(true);
  m_nameLabel->setFont(font);
  TQWhatsThis::add(m_nameLabel, i18n("The name and license of the selected item"));

  m_infoLabel = new TQLabel(widget);
  TQWhatsThis::add(m_infoLabel, i18n("The author of the selected item"));

  m_install = new KPushButton(i18n("Install"), widget);
  m_install->setIconSet(SmallIconSet(TQString::fromLatin1("knewstuff")));
  m_install->setEnabled(false);
  connect(m_install, TQT_SIGNAL(clicked()), TQT_SLOT(slotInstall()));

  // the button's text changes later
  // I don't want it resizing, so figure out the maximum size and set that
  m_install->polish();
  int maxWidth = m_install->sizeHint().width();
  int maxHeight = m_install->sizeHint().height();
  m_install->setGuiItem(KGuiItem(i18n("Update"), SmallIconSet(TQString::fromLatin1("knewstuff"))));
  maxWidth = TQMAX(maxWidth, m_install->sizeHint().width());
  maxHeight = TQMAX(maxHeight, m_install->sizeHint().height());
  m_install->setMinimumWidth(maxWidth);
  m_install->setMinimumHeight(maxHeight);

  TQPixmap pix;
  if(m_type == EntryTemplate) {
    pix = DesktopIcon(TQString::fromLatin1("looknfeel"), KIcon::SizeLarge);
    TQWhatsThis::add(m_install, i18n("Download and install the selected template."));
  } else {
    pix = UserIcon(TQString::fromLatin1("script"));
    TQWhatsThis::add(m_install, i18n("Download and install the selected script. Some scripts "
                                    "may need to be configured after being installed."));
  }
  m_iconLabel->setPixmap(pix);

  TQBoxLayout* boxLayout3 = new TQHBoxLayout(boxLayout2);

  TQBoxLayout* boxLayout4 = new TQVBoxLayout(boxLayout3);
  boxLayout4->addWidget(m_iconLabel);
  boxLayout4->addStretch(10);

  boxLayout3->addSpacing(4);

  TQBoxLayout* boxLayout5 = new TQVBoxLayout(boxLayout3);
  boxLayout5->addWidget(m_nameLabel);
  boxLayout5->addWidget(m_infoLabel);
  boxLayout5->addStretch(10);

  boxLayout3->addStretch(10);

  TQBoxLayout* boxLayout6 = new TQVBoxLayout(boxLayout3);
  boxLayout6->addWidget(m_install);
  boxLayout6->addStretch(10);

  m_descLabel = new TQTextEdit(widget);
  m_descLabel->setReadOnly(true);
  m_descLabel->setTextFormat(TQt::RichText);
  m_descLabel->setPaper(colorGroup().background());
  m_descLabel->setMinimumHeight(5 * fontMetrics().height());
  boxLayout2->addWidget(m_descLabel, 10);
  TQWhatsThis::add(m_descLabel, i18n("A description of the selected item is shown here."));

  TQHBox* box = new TQHBox(frame, "statusbox");
  boxLayout->addWidget(box);
  box->setSpacing(KDialog::spacingHint());

  m_statusBar = new KStatusBar(box, "statusbar");
  m_statusBar->insertItem(TQString(), PROGRESS_STATUS_ID, 1, false);
  m_statusBar->setItemAlignment(PROGRESS_STATUS_ID, AlignLeft | AlignVCenter);
  m_progress = new TQProgressBar(m_statusBar, "progress");
  m_progress->setTotalSteps(0);
  m_progress->setFixedHeight(fontMetrics().height()+2);
  m_statusBar->addWidget(m_progress, 0, true);

  KPushButton* closeButton = new KPushButton(KStdGuiItem::close(), box);
  connect(closeButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotClose()));
  closeButton->setFocus();

  connect(m_timer, TQT_SIGNAL(timeout()), TQT_SLOT(slotMoveProgress()));

  setMinimumWidth(TQMAX(minimumWidth(), NEW_STUFF_MIN_WIDTH));
  setMinimumHeight(TQMAX(minimumHeight(), NEW_STUFF_MIN_HEIGHT));
  resize(configDialogSize(TQString::fromLatin1("NewStuff Dialog Options")));

  KConfigGroup dialogConfig(KGlobal::config(), "NewStuff Dialog Options");
  TQValueList<int> splitList = dialogConfig.readIntListEntry("Splitter Sizes");
  if(!splitList.empty()) {
    m_split->setSizes(splitList);
  }

  setStatus(i18n("Downloading information..."));

  ProviderLoader* loader = new Tellico::NewStuff::ProviderLoader(this);
  connect(loader, TQT_SIGNAL(providersLoaded(TQPtrList<KNS::Provider>*)), TQT_SLOT(slotProviders(TQPtrList<KNS::Provider>*)));
  connect(loader, TQT_SIGNAL(percent(KIO::Job*, unsigned long)), TQT_SLOT(slotShowPercent(KIO::Job*, unsigned long)));
  connect(loader, TQT_SIGNAL(error()), TQT_SLOT(slotProviderError()));

  KConfigGroup config(KGlobal::config(), "KNewStuff");
  TQString prov = config.readEntry("ProvidersUrl");
  if(prov.isEmpty()) {
    if(m_type == EntryTemplate) {
      prov = TQString::fromLatin1("http://periapsis.org/tellico/newstuff/tellicotemplates-providers.php");
      TQString alt = TQString::fromLatin1("http://download.kde.org/khotnewstuff/tellicotemplates-providers.xml");
      loader->setAlternativeProvider(alt);
    } else {
      prov = TQString::fromLatin1("http://periapsis.org/tellico/newstuff/tellicoscripts-providers.php");
    }
  }
  if(m_type == EntryTemplate) {
    m_typeName = TQString::fromLatin1("tellico/entry-template");
  } else {
    m_typeName = TQString::fromLatin1("tellico/data-source");
  }
  loader->load(m_typeName, prov);

  KAcceleratorManager::manage(this);
}

Dialog::~Dialog() {
  delete m_cursorSaver;
  m_cursorSaver = 0;

  saveDialogSize(TQString::fromLatin1("NewStuff Dialog Options"));
  KConfigGroup config(KGlobal::config(), "NewStuff Dialog Options");
  config.writeEntry("Splitter Sizes", m_split->sizes());
}

void Dialog::slotProviderError() {
  if(m_listView->childCount() == 0) {
    myDebug() << "NewStuff::Dialog::slotCheckError() - no available items" << endl;
    setStatus(TQString());

    delete m_cursorSaver;
    m_cursorSaver = 0;
  }
}

void Dialog::slotProviders(TQPtrList<KNS::Provider>* list_) {
  for(KNS::Provider* prov = list_->first(); prov; prov = list_->next()) {
    KIO::TransferJob* job = KIO::get(prov->downloadUrl());
    m_jobs[job] = prov;
    connect(job, TQT_SIGNAL(data(KIO::Job*, const TQByteArray&)),
            TQT_SLOT(slotData(KIO::Job*, const TQByteArray&)));
    connect(job, TQT_SIGNAL(result(KIO::Job*)), TQT_SLOT(slotResult(KIO::Job*)));
    connect(job, TQT_SIGNAL(percent(KIO::Job*, unsigned long)),
            TQT_SLOT(slotShowPercent(KIO::Job*, unsigned long)));
  }
}

void Dialog::slotData(KIO::Job* job_, const TQByteArray& data_) {
  TQDataStream stream(m_data[job_], IO_WriteOnly | IO_Append);
  stream.writeRawBytes(data_.data(), data_.size());
}

void Dialog::slotResult(KIO::Job* job_) {
//  myDebug() << "NewStuff::Dialog::slotResult()" << endl;
  TQDomDocument dom;
  if(!dom.setContent(m_data[job_])) {
    KNS::Provider* prov = m_jobs[job_];
    KURL u = prov ? prov->downloadUrl() : KURL();
    myDebug() << "NewStuff::Dialog::slotResult() - can't load result: " << u.url() << endl;
    m_jobs.remove(job_);
    if(m_jobs.isEmpty()) {
      setStatus(i18n("Ready."));
      delete m_cursorSaver;
      m_cursorSaver = 0;
    }
    return;
  }

  TQDomElement knewstuff = dom.documentElement();

  for(TQDomNode pn = knewstuff.firstChild(); !pn.isNull(); pn = pn.nextSibling()) {
    TQDomElement stuff = pn.toElement();
    if(stuff.isNull()) {
      continue;
    }

    if(stuff.tagName() == Latin1Literal("stuff")) {
      KNS::Entry* entry = new KNS::Entry(stuff);
      if(!entry->type().isEmpty() && entry->type() != m_typeName) {
        myLog() << "NewStuff::Dialog::slotResult() - type mismatch, skipping " << ENTRYNAME(entry) << endl;
        continue;
      }

      addEntry(entry);
    }
  }
  m_jobs.remove(job_);
  if(m_jobs.isEmpty()) {
    setStatus(i18n("Ready."));
    delete m_cursorSaver;
    m_cursorSaver = 0;
  }
}

void Dialog::addEntry(KNS::Entry* entry_) {
  if(!entry_) {
    return;
  }

  Item* item = new Item(m_listView);
  item->setText(0, ENTRYNAME(entry_));
  item->setText(1, entry_->version());
  item->setText(2, TQString::number(entry_->rating()));
  item->setText(3, TQString::number(entry_->downloads()));
  item->setText(4, KGlobal::locale()->formatDate(entry_->releaseDate(), true /*short format */));
  item->setStatus(NewStuff::Manager::installStatus(entry_));
  m_entryMap.insert(item, entry_);

  if(!m_listView->selectedItem()) {
    m_listView->setSelected(item, true);
    slotSelected(item);
  }
}

void Dialog::slotSelected(TQListViewItem* item_) {
  if(!item_) {
    return;
  }

  KNS::Entry* entry = m_entryMap[item_];
  if(!entry) {
    return;
  }

  KURL preview = entry->preview(m_lang);
  if(!preview.isEmpty() && preview.isValid()) {
    delete m_tempPreviewImage;
    m_tempPreviewImage = new KTempFile();
    m_tempPreviewImage->setAutoDelete(true);
    KURL dest;
    dest.setPath(m_tempPreviewImage->name());
    KIO::FileCopyJob* job = KIO::file_copy(preview, dest, -1, true, false, false);
    connect(job, TQT_SIGNAL(result(KIO::Job*)), TQT_SLOT(slotPreviewResult(KIO::Job*)));
    connect(job, TQT_SIGNAL(percent(KIO::Job*, unsigned long)),
            TQT_SLOT(slotShowPercent(KIO::Job*, unsigned long)));
    m_lastPreviewItem = item_;
  }
  TQPixmap pix = m_type == EntryTemplate
              ? DesktopIcon(TQString::fromLatin1("looknfeel"), KIcon::SizeLarge)
              : UserIcon(TQString::fromLatin1("script"));
  m_iconLabel->setPixmap(pix);

  TQString license = entry->license();
  if(!license.isEmpty()) {
    license.prepend('(').append(')');
  }
  TQString name = TQString::fromLatin1("%1 %2").arg(ENTRYNAME(entry)).arg(license);
  TQFont font = m_nameLabel->font();
  font.setBold(true);
  font.setItalic(false);
  m_nameLabel->setFont(font);
  m_nameLabel->setText(name);

  m_infoLabel->setText(entry->author());

  TQString desc = entry->summary(m_lang);
  desc.replace(TQRegExp(TQString::fromLatin1("\\n")), TQString::fromLatin1("<br>"));
  m_descLabel->setText(desc);

  InstallStatus installed = static_cast<Item*>(item_)->status();
  m_install->setText(installed == OldVersion ? i18n("Update Stuff", "Update") : i18n("Install"));
  m_install->setEnabled(installed != Current);
}

void Dialog::slotInstall() {
  TQListViewItem* item = m_listView->currentItem();
  if(!item) {
    return;
  }

  KNS::Entry* entry = m_entryMap[item];
  if(!entry) {
    return;
  }

  delete m_cursorSaver;
  m_cursorSaver = new GUI::CursorSaver();
  setStatus(i18n("Installing item..."));
  m_progress->show();
  m_timer->start(100);
  connect(m_manager, TQT_SIGNAL(signalInstalled(KNS::Entry*)), TQT_SLOT(slotDoneInstall(KNS::Entry*)));
  m_manager->install(m_type, entry);
  delete m_cursorSaver;
  m_cursorSaver = 0;
}

void Dialog::slotDoneInstall(KNS::Entry* entry_) {
  TQMap<TQListViewItem*, KNS::Entry*>::Iterator it;
  for(it = m_entryMap.begin(); entry_ && it != m_entryMap.end(); ++it) {
    if(it.data() == entry_) {
      InstallStatus installed = Manager::installStatus(entry_);
      static_cast<Item*>(it.key())->setStatus(installed);
      m_install->setEnabled(installed != Current);
      break;
    }
  }
  delete m_cursorSaver;
  m_cursorSaver = 0;
  setStatus(i18n("Ready."));
  m_timer->stop();
  m_progress->hide();
}

void Dialog::slotMoveProgress() {
  m_progress->setProgress(m_progress->progress()+5);
}

void Dialog::setStatus(const TQString& text_) {
  m_statusBar->changeItem(TQChar(' ') + text_, PROGRESS_STATUS_ID);
}

void Dialog::slotShowPercent(KIO::Job*, unsigned long pct_) {
  if(pct_ >= 100) {
    m_progress->hide();
  } else {
    m_progress->show();
    m_progress->setProgress(static_cast<int>(pct_), 100);
  }
}

void Dialog::slotPreviewResult(KIO::Job* job_) {
  KIO::FileCopyJob* job = static_cast<KIO::FileCopyJob*>(job_);
  if(job->error()) {
    return;
  }
  TQString tmpFile = job->destURL().path(); // might be different than m_tempPreviewImage->name()
  TQPixmap pix(tmpFile);

  if(!pix.isNull()) {
    if(pix.width() > 64 || pix.height() > 64) {
      pix.convertFromImage(pix.convertToImage().smoothScale(64, 64, TQ_ScaleMin));
    }
    // only set label if it's still current
    if(m_listView->selectedItem() == m_lastPreviewItem) {
      m_iconLabel->setPixmap(pix);
    }
  }
  delete m_tempPreviewImage;
  m_tempPreviewImage = 0;
}

#include "dialog.moc"
