/*
 * This file is part of the KFTPGrabber project
 *
 * Copyright (C) 2004 by the KFTPGrabber developers
 * Copyright (C) 2004 Jernej Kos <kostko@jweb-network.net>
 * Copyright (C) 2004 Markus Brueffer <markus@brueffer.de>
 *
 * 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
 * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
 * NON-INFRINGEMENT.  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.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * file(s) with this exception, you may extend this exception to your
 * version of the file(s), but you are not obligated to do so.  If you
 * do not wish to do so, delete this exception statement from your
 * version.  If you delete this exception statement from all source
 * files in the program, then also delete it here.
 */

#include "kftpapi.h"
#include "listview.h"
#include "kftpbookmarks.h"
#include "kftpsession.h"
#include "misc.h"

#include <qdragobject.h>

#include <kaction.h>
#include <kinputdialog.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kdebug.h>
#include <kio/passdlg.h>

using namespace KFTPGrabberBase;

namespace KFTPWidgets {

namespace Bookmarks {

ListViewItem::ListViewItem(QListView *parent, const QString &t1)
  : KListViewItem(parent, t1)
{
}

ListViewItem::ListViewItem(QListViewItem *parent, const QString &t1)
  : KListViewItem(parent, t1)
{
}

int ListViewItem::compare(QListViewItem *i, int col, bool) const
{
  if (m_type != static_cast<ListViewItem*>(i)->m_type) {
    if (m_type == 0)
      return -1; // category
    else
      return 1; // server
  }

  return KListViewItem::compare(i, col, false);
}

ListView::ListView(KFTPBookmarks::Manager *bookmarks, QWidget *parent, const char *name)
 : KFTPWidgets::ListView(parent, name),
   m_autoUpdate(false),
   m_connectBookmark(false),
   m_editMenuItem(false),
   m_bookmarks(bookmarks),
   m_activeSite(0),
   m_activeCategory(0),
   m_activeItem(0)
{
  // Set some stuff
  setMinimumWidth(150);
  setFullWidth(true);
  addColumn(i18n("Bookmarks"));
  setRootIsDecorated(true);
  setEmptyListText(i18n("No bookmarks."));
  setItemsRenameable(false);

  // Drag & drop
  setDragEnabled(true);
  setAcceptDrops(true);
  setDropVisualizer(false);

  // Init auto open timer
  m_openTimer = new QTimer(this);

  connect(this, SIGNAL(dropped(QDropEvent*, QListViewItem*)), this, SLOT(slotDropped(QDropEvent*, QListViewItem*)));
  connect(this, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotClicked(QListViewItem*)));
  connect(this, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(slotDoubleClicked(QListViewItem*)));
  connect(this, SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int)), this, SLOT(slotContextMenu(QListViewItem*, const QPoint&, int)));

  /* Init the actions */
  m_newAction = new KAction(i18n("&New..."), "filenew", KShortcut(), this, SLOT(slotNewAction()), actionCollection(), "bookmark_new");
  m_renameAction = new KAction(i18n("&Rename"), KShortcut(), this, SLOT(slotRenameAction()), actionCollection(), "bookmark_rename");
  m_deleteAction = new KAction(i18n("&Delete"), "editdelete", KShortcut(), this, SLOT(slotDeleteAction()), actionCollection(), "bookmark_delete");
  m_subCatAction = new KAction(i18n("&Create Subcategory..."), "folder_new", KShortcut(), this, SLOT(slotSubCatAction()), actionCollection(), "bookmark_subcat");
  m_copyAction = new KAction(i18n("&Duplicate"), KShortcut(), this, SLOT(slotDuplicateAction()), actionCollection(), "bookmark_duplicate");
}

KActionCollection *ListView::actionCollection()
{
  return KFTPAPI::getInstance()->mainWindow()->actionCollection();
}

void ListView::setAutoUpdate(bool value)
{
  m_autoUpdate = value;

  if (value)
    connect(m_bookmarks, SIGNAL(update()), this, SLOT(slotAutoUpdate()));

  slotAutoUpdate();
}

void ListView::setConnectBookmark(bool value)
{
  m_connectBookmark = value;

  if (value)
    connect(this, SIGNAL(executed(QListViewItem*)), this, SLOT(slotBookmarkExecuted(QListViewItem*)));
}

void ListView::setEditMenuItem(bool value)
{
  m_editMenuItem = value;
}

void ListView::slotBookmarkExecuted(QListViewItem *item)
{
  if (!item || !m_connectBookmark || static_cast<ListViewItem*>(item)->m_type == BT_CATEGORY)
    return;

  KFTPBookmarks::Site *site = static_cast<ListViewItem*>(item)->m_site;
  KURL siteUrl = site->getUrl();
  
  // Handle empty usernames and passwords for non-anonymous sites
  if (!siteUrl.hasUser() || !siteUrl.hasPass() && siteUrl.user() != "anonymous") {
    KIO::PasswordDialog *dlg = new KIO::PasswordDialog(i18n("Please provide your username and password for connecting to this site."), siteUrl.user(), true);
    dlg->addCommentLine(i18n("Site:"), QString("%1:%2").arg(siteUrl.host()).arg(siteUrl.port()));
    
    if (dlg->exec() == KDialogBase::Accepted) {
      siteUrl.setUser(dlg->username());
      siteUrl.setPass(dlg->password());
      
      if (dlg->keepPassword()) {
        // Save password to the bookmarked site
        site->setProperty("username", dlg->username());
        site->setProperty("password", encodePassword(dlg->password()));
      }
      
      delete dlg;
    } else {
      // Abort connection attempt
      delete dlg;
      return;
    }
  }

  // Just spawn a new session
  KFTPSession::Session *session = KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, siteUrl, site);
  m_bookmarks->setupClient(site, session->getClient());
}

void ListView::slotAutoUpdate()
{
  if (m_autoUpdate) {
    // Update the bookmark list
    fillBookmarkData();
  }
}

QDragObject *ListView::dragObject()
{
  // Determine hotspot and pixmap
  QPoint hotspot;
  QPixmap pixmap = *selectedItem()->pixmap(0);
  hotspot.setX(pixmap.width() / 2);
  hotspot.setY(pixmap.height() / 2);

  QString id = static_cast<ListViewItem*>(selectedItem())->m_site->id();
  m_dragObject = new QStoredDrag("application/x-qlistviewitem", this);
  QByteArray data;
  QDataStream arg(data, IO_WriteOnly);
  arg << id;

  // Start the drag
  static_cast<QStoredDrag*>(m_dragObject)->setEncodedData(data);
  m_dragObject->setPixmap(pixmap, hotspot);

  return m_dragObject;
}

bool ListView::acceptDrag(QDropEvent *e) const
{
  // If it is a local drag, accept it
  return e->source() == static_cast<QWidget*>(const_cast<ListView*>(this));
}

void ListView::contentsDragEnterEvent(QDragEnterEvent *e)
{
  if (!acceptDrag(e)) {
    e->ignore();
    return;
  }
  e->acceptAction();

  m_currentBeforeDropItem = selectedItem();
  QListViewItem *item = itemAt(contentsToViewport(e->pos()));

  if (item) {
    m_dropItem = item;
    m_openTimer->start(750);
  } else {
    m_dropItem = 0L;
  }
}

void ListView::contentsDragMoveEvent(QDragMoveEvent *e)
{
  if (!acceptDrag(e)) {
    e->ignore();
    return;
  }
  e->acceptAction();

  QListViewItem *item = itemAt(contentsToViewport(e->pos()));
  if (item) {
    setSelected(item, true);
    if (item != m_dropItem) {
      m_openTimer->stop();
      m_dropItem = item;
      m_openTimer->start(750);
    }
  } else {
    if (selectedItem()) {
      setSelected(selectedItem(), false);
    }

    m_openTimer->stop();
    m_dropItem = 0L;
  }
}

void ListView::contentsDragLeaveEvent(QDragLeaveEvent*)
{
  if (m_currentBeforeDropItem) {
    setSelected(m_currentBeforeDropItem, true);
    ensureItemVisible(m_currentBeforeDropItem);
  } else {
    setSelected(m_dropItem, false);
  }

  m_openTimer->stop();
  m_dropItem = 0L;
}

void ListView::slotOpenTimer()
{
  m_openTimer->stop();
  if (m_dropItem && m_dropItem->isExpandable()) {
    // Expand the category
    m_dropItem->setOpen(true);
  }
}

void ListView::slotDropped(QDropEvent *e, QListViewItem*)
{
  KFTPBookmarks::Site *parentSite = 0L;

  if (selectedItem()) {
    QListViewItem *newParent = 0L;

    // Get the new path
    if (static_cast<ListViewItem*>(selectedItem())->m_type == 0)
      newParent = selectedItem();
    else
      newParent = selectedItem()->parent();

    if (newParent)
      parentSite = static_cast<ListViewItem*>(newParent)->m_site;
    else
      parentSite = m_bookmarks->findCategory("root");
  } else {
    parentSite = m_bookmarks->findCategory("root");
  }

  QString id;

  // Decode the data
  QDataStream arg(e->encodedData("application/x-qlistviewitem"), IO_ReadOnly);
  arg >> id;

  // Move the site
  KFTPBookmarks::Site *originalSite = m_bookmarks->findSite(id);
  parentSite->reparentSite(originalSite);

  emit bookmarkMoved();

  // Notify the bookmark manager
  m_bookmarks->emitUpdate();
}

void ListView::fillBookmarkData()
{
  // Fill the tree with data
  m_bookmarks->guiPopulateBookmarksTree(this);
}

void ListView::slotClicked(QListViewItem *item)
{
  m_activeItem = static_cast<ListViewItem*>(item);

  if (m_activeItem) {
    if (m_activeItem->m_type == BT_CATEGORY) {
      // Category
      m_activeCategory = m_activeItem->m_site;
      m_activeSite = 0L;
    } else {
      // Server
      m_activeSite = m_activeItem->m_site;
      m_activeCategory = m_activeSite->getParentSite();
    }
  }

  emit bookmarkClicked(item);
}

void ListView::slotDoubleClicked(QListViewItem *item)
{
  if (!item)
    return;

  setOpen(item, !item->isOpen());
}

KFTPBookmarks::Manager *ListView::getBookmarks()
{
  return m_bookmarks;
}

void ListView::slotRenameAction()
{
  // Rename a subcategory
  ListViewItem *item = static_cast<ListViewItem*>(selectedItems().at(0));
  KFTPBookmarks::Site *site = item->m_site;

  bool ok;
  QString newName = KInputDialog::getText(i18n("Category Name"), i18n("Rename category:"), item->text(0), &ok, this);

  if (ok) {
    // Ok, let's rename it
    site->setAttribute("name", newName);
    item->setText(0, newName);
  }
}

void ListView::slotSubCatAction()
{
  // Create a subcategory
  ListViewItem *item = static_cast<ListViewItem*>(selectedItems().at(0));
  KFTPBookmarks::Site *site = item ? item->m_site : m_bookmarks->findCategory("root");

  bool ok;
  QString newName = KInputDialog::getText(i18n("New Category Name"), i18n("New category:"), "", &ok, this);

  if (ok) {
    // Let's create the sub category
    site->addCategory(newName);

    emit categoryRenamed();
    m_bookmarks->emitUpdate();
  }
}

void ListView::slotNewAction()
{
  if (!m_activeCategory) {
    // Set the starting category to document root
    m_activeCategory = m_bookmarks->findCategory("root");
  }

  // Create the new node
  KFTPBookmarks::Site *site = m_activeCategory->addSite();
  site->setAttribute("name", i18n("New server"));
  site->setProperty("port", 21);

  // Create the ListViewItem
  ListViewItem *serv = 0L;
  if (!m_activeItem || (!m_activeItem->parent() && m_activeItem->m_type == BT_SERVER)) {
    serv = new ListViewItem(this, i18n("New Server"));
  } else if (m_activeItem->m_type == BT_CATEGORY) {
    serv = new ListViewItem(m_activeItem, i18n("New Server"));
    m_activeItem->setOpen(true);
  } else {
    serv = new ListViewItem(m_activeItem->parent(), i18n("New Server"));
  }

  serv->m_type = BT_SERVER;
  serv->m_site = site;

  serv->setPixmap(0, loadSmallPixmap("ftp"));

  // Set the new ListViewItem as active
  setSelected(serv, true);
  m_activeItem = serv;

  // Display its properties
  m_activeSite = site;

  emit bookmarkNew(serv, site);
}

void ListView::slotDuplicateAction()
{
  // Copy a bookmark
  ListViewItem *item = static_cast<ListViewItem*>(selectedItems().at(0));

  // Clone the node
  KFTPBookmarks::Site *copy = item->m_site->duplicate();

  // Create the ListViewItem
  ListViewItem *serv = 0L;
  if (item->parent())
    serv = new ListViewItem(item->parent(), copy->getAttribute("name"));
  else
    serv = new ListViewItem(this, copy->getAttribute("name"));

  serv->m_type = BT_SERVER;
  serv->m_site = copy;

  serv->setPixmap(0, loadSmallPixmap("ftp"));

  // Set the new ListViewItem as active
  setSelected(serv, true);
  m_activeItem = serv;

  // Display its properties
  m_activeSite = copy;

  emit bookmarkDuplicated(serv, copy);

  // Update the bookmarks
  m_bookmarks->emitUpdate();
}

void ListView::slotDeleteAction()
{
  // Delete a server or a category
  ListViewItem *item = static_cast<ListViewItem*>(selectedItems().at(0));

  // What do we have here ?
  if (item->m_type == BT_CATEGORY) {
    // Category
    if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to remove this category?")) == KMessageBox::Yes) {
      m_bookmarks->delSite(item->m_site);
    } else {
      return;
    }
  } else {
    // Server
    if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to remove this server?")) == KMessageBox::Yes) {
      m_bookmarks->delSite(item->m_site);
    } else {
      return;
    }
  }

  if ( !m_autoUpdate )
    delete item;

  m_activeItem = 0L;
  emit bookmarkDeleted();
}

void ListView::slotContextMenu(QListViewItem *item, const QPoint &pos, int)
{
  slotClicked(item);

  KPopupMenu *context = new KPopupMenu(this);

  if (item) {
    context->insertTitle(item->text(0));

    // Server or category
    if (static_cast<ListViewItem*>(item)->m_type == BT_CATEGORY) {
      m_newAction->plug(context);
      m_deleteAction->plug(context);
      m_subCatAction->plug(context);
      m_renameAction->plug(context);
    } else {
      if (m_editMenuItem)
        actionCollection()->action("bookmark_edit2")->plug(context);

      m_newAction->plug(context);
      m_copyAction->plug(context);
      m_deleteAction->plug(context);
    }
  } else {
    // Nothing selected
    m_newAction->plug(context);
    m_subCatAction->plug(context);
  }

  context->exec(pos);

  delete context;
}

}

}

#include "listview.moc"
