/*
 * This file is part of the KFTPGrabber project
 *
 * Copyright (C) 2003-2004 by the KFTPGrabber developers
 * Copyright (C) 2003-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 <qlabel.h>
#include <qlayout.h>
#include <qpainter.h>
#include <qtextedit.h>
#include <qtabwidget.h>
#include <qhbox.h>
#include <qvbox.h>
#include <qthread.h>

#include <kapplication.h>
#include <kmdimainfrm.h>
#include <kmdichildview.h>
#include <kmenubar.h>
#include <kpopupmenu.h>
#include <ktoolbar.h>
#include <kiconloader.h>
#include <kdialogbase.h>
#include <kfiledetailview.h>
#include <kmessagebox.h>
#include <klineeditdlg.h>
#include <kstatusbar.h>
#include <klocale.h>
#include <kio/job.h>
#include <kpassdlg.h>
#include <ksslpkcs12.h>
#include <kstandarddirs.h>

#include "misc.h"
#include "kftpapi.h"

// Widgets
#include "widgets/systemtray.h"
#include "bookmarks/sidebar.h"
#include "bookmarks/editor.h"
#include "bookmarks/listview.h"
#include "logview.h"
#include "queueview/queueview.h"
#include "queueview/threadview.h"
#include "widgets/quickconnect.h"
#include "kftpserverlineedit.h"
#include "browser/view.h"
#include "kftpzeroconflistview.h"
#include "trafficgraph.h"
#include "kftptabwidget.h"
#include "failedtransfers.h"

#include "sidebar.h"

// Other KFTPGrabber stuff
#include "misc/config.h"
#include "misc/customcommands/manager.h"

#include "mainwindow.h"
#include "mainactions.h"
#include "kftpbookmarks.h"
#include "kftpqueue.h"
#include "kftpsession.h"
#include "kftpqueueconverter.h"
#include "kftppluginmanager.h"
#include "engine/thread.h"

using namespace KFTPGrabberBase;

MainWindow::MainWindow()
  : KMainWindow()
{
  // Init the API
  KFTPAPI::getInstance()->m_mainWindow = this;

  // Set the shell's ui resource file
  setXMLFile("kftpgrabberui.rc");
  connect(KApplication::kApplication(), SIGNAL(shutDown()), this, SLOT(appShutdown()));

  // Restore size and position
  resize(KFTPCore::Config::size());
  move(KFTPCore::Config::position());
  setCaption("KFTPGrabber");

  KFTPCore::Config::self()->postInit();

  // Load plugins
  KFTPAPI::getInstance()->pluginManager()->loadPlugins();
  
  // Load custom commands
  KFTPCore::CustomCommands::Manager::self()->load();

  connect(KFTPQueue::Manager::self(), SIGNAL(queueUpdate()), this, SLOT(slotUpdateStatusBar()));
  connect(KFTPBookmarks::Manager::self(), SIGNAL(update()), this, SLOT(initBookmarkMenu()));
  connect(KFTPCore::Config::self(), SIGNAL(configChanged()), this, SLOT(slotConfigChanged()));

  m_trafficGraph = 0L;
  m_bookmarkMenu = 0L;
  m_zeroconfMenu = 0L;
  m_walletMenu = 0L;

  // Init the gui system
  initTrafficGraph();
  initMainView();
  initStatusBar();
  initBookmarkMenu();
  
  // Create the actions object
  m_actions = new MainActions(this);

  // Create the systray icon
  new KFTPWidgets::SystemTray(this);

  // Create base two sessions
  KFTPSession::Manager::self()->spawnLocalSession(KFTPSession::LeftSide);
  KFTPSession::Manager::self()->spawnLocalSession(KFTPSession::RightSide);

  // Load bookmarks
  QTimer::singleShot(500, this, SLOT(slotReadBookmarks()));

  // Check for the uirc file
  if (KGlobal::dirs()->findResource("appdata", xmlFile()) == QString::null) {
    KMessageBox::error(0, i18n("<qt>Unable to find %1 XML GUI descriptor file. Please check that you have installed the application correctly! If you have any questions please ask on %2.<br><br><b>Warning:</b> Current GUI will be incomplete!</qt>").arg(xmlFile()).arg("irc.freenode.net/#kftpgrabber"));
  }

  createGUI(0);

  // Auto-save toolbar/menubar/statusbar settings
  setAutoSaveSettings(QString::fromLatin1("MainWindow"), false);
}

void MainWindow::slotReadBookmarks()
{
  // Load bookmarks and custom site commands
  KFTPBookmarks::Manager::self()->load(getStoreDir("bookmarks.xml"));

  // Load the saved queue
  KFTPQueue::Manager::self()->getConverter()->importQueue(getStoreDir("queue"));

  // Update the bookmark menu
  initBookmarkMenu();
}

void MainWindow::appShutdown()
{
  KFTPQueue::Manager::self()->stopAllTransfers();
  KFTPSession::Manager::self()->disconnectAllSessions();
  
  // Save the queueview layout
  m_queueView->saveLayout();

  // Save the config data on shutdown
  KFTPCore::Config::self()->saveConfig();

  // Save current queue
  KFTPQueue::Manager::self()->getConverter()->exportQueue(getStoreDir("queue"));
}

bool MainWindow::queryClose()
{
  if(KApplication::kApplication()->sessionSaving()) {
    m_actions->m_closeApp = true;
  }

  if (!KFTPCore::Config::exitOnClose() && KFTPCore::Config::showSystrayIcon() && !m_actions->m_closeApp) {
    /*
      * This code was adopted from the Konversation project
      * copyright: (C) 2003 by Dario Abatianni, Peter Simonsson
      * email:     eisfuchs@tigress.com, psn@linux.se
      */

    // Compute size and position of the pixmap to be grabbed:
    QPoint g = KFTPWidgets::SystemTray::self()->mapToGlobal(KFTPWidgets::SystemTray::self()->pos());
    int desktopWidth  = kapp->desktop()->width();
    int desktopHeight = kapp->desktop()->height();
    int tw = KFTPWidgets::SystemTray::self()->width();
    int th = KFTPWidgets::SystemTray::self()->height();
    int w = desktopWidth / 4;
    int h = desktopHeight / 9;
    int x = g.x() + tw/2 - w/2; // Center the rectange in the systray icon
    int y = g.y() + th/2 - h/2;
    if ( x < 0 )                 x = 0; // Move the rectangle to stay in the desktop limits
    if ( y < 0 )                 y = 0;
    if ( x + w > desktopWidth )  x = desktopWidth - w;
    if ( y + h > desktopHeight ) y = desktopHeight - h;

    // Grab the desktop and draw a circle arround the icon:
    QPixmap shot = QPixmap::grabWindow( qt_xrootwin(),  x,  y,  w,  h );
    QPainter painter( &shot );
    const int MARGINS = 6;
    const int WIDTH   = 3;
    int ax = g.x() - x - MARGINS -1;
    int ay = g.y() - y - MARGINS -1;
    painter.setPen(  QPen( Qt::red,  WIDTH ) );
    painter.drawArc( ax,  ay,  tw + 2*MARGINS,  th + 2*MARGINS,  0,  16*360 );
    painter.end();

    // Associate source to image and show the dialog:
    QMimeSourceFactory::defaultFactory()->setPixmap( "systray_shot",  shot );
    KMessageBox::information( this,
    i18n( "<p>Closing the main window will keep KFTPGrabber running in the system tray. "
          "Use <b>Quit</b> from the <b>KFTPGrabber</b> menu to quit the application.</p>"
          "<p><center><img source=\"systray_shot\"></center></p>" ),
    i18n( "Docking in System Tray" ),  "HideMenuBarWarning" );
    hide();

    return false;
  }

  if (KFTPCore::Config::confirmExit() && KFTPQueue::Manager::self()->getNumRunning() > 0) {
    if (KMessageBox::questionYesNo(0, i18n("There is currently a transfer running.",
                                           "There are currently %n transfers running.",
                                           KFTPQueue::Manager::self()->getNumRunning()) + i18n("\nAre you sure you want to quit?"),
                                      i18n("Quit")) == KMessageBox::No)
    {
      return false;
    }
  }

  // Save XML bookmarks here, because the user may be prompted for an encryption password
  KFTPBookmarks::Manager::self()->save();

  return true;
}

MainWindow::~MainWindow()
{
  delete m_bookmarkMenu;
  delete m_zeroconfMenu;
  delete m_walletMenu;
}

void MainWindow::initTrafficGraph()
{
  // Setup traffic graph
  m_graphTimer = new QTimer(this);
  connect(m_graphTimer, SIGNAL(timeout()), this, SLOT(slotUpdateTrafficGraph()));
  m_graphTimer->start(1000);

  // Create and configure the traffic graph
  m_trafficGraph = new KFTPWidgets::TrafficGraph(0, "graph");
  m_trafficGraph->setShowLabels(true);

  m_trafficGraph->addBeam(QColor(255, 0, 0));
  m_trafficGraph->addBeam(QColor(0, 0, 255));

  m_trafficGraph->repaint();
}

void MainWindow::showBookmarkEditor()
{
  KFTPWidgets::Bookmarks::BookmarkEditor *bookmarkEditor = new KFTPWidgets::Bookmarks::BookmarkEditor(this, "bookmark editor");
  bookmarkEditor->exec();
  delete bookmarkEditor;

  // Update the bookmarks globaly
  KFTPBookmarks::Manager::self()->emitUpdate();
}

void MainWindow::initBookmarkMenu()
{
  // Bookmarks menu
  if (!m_bookmarkMenu)
    m_bookmarkMenu = new KActionMenu(i18n("Bookmarks"), 0, actionCollection(), "bookmarks");
  else
    m_bookmarkMenu->popupMenu()->clear();

  // Zeroconf menu
  if (!m_zeroconfMenu)
    m_zeroconfMenu = new KActionMenu(i18n("FTP Sites Near Me"));

  // Wallet menu
  if (!m_walletMenu)
    m_walletMenu = new KActionMenu(i18n("Sites In KWallet"), loadSmallIcon("wallet_open"));

  m_bookmarkMenu->popupMenu()->insertItem(loadSmallIcon("bookmark"), i18n("Edit Bookmarks..."), 1);
  m_bookmarkMenu->popupMenu()->connectItem(1, this, SLOT(showBookmarkEditor()));
  m_bookmarkMenu->insert(m_zeroconfMenu);

  if (KFTPCore::Config::showWalletSites())
    m_bookmarkMenu->insert(m_walletMenu);

  m_bookmarkMenu->popupMenu()->insertSeparator();

  // Populate the bookmarks and zeroconf menus
  KFTPBookmarks::Manager::self()->guiPopulateBookmarksMenu(m_bookmarkMenu);
  KFTPBookmarks::Manager::self()->guiPopulateZeroconfMenu(m_zeroconfMenu);

  if (KFTPCore::Config::showWalletSites())
    KFTPBookmarks::Manager::self()->guiPopulateWalletMenu(m_walletMenu);
}

void MainWindow::initStatusBar()
{
  statusBar()->insertItem(i18n("idle"), 1, 1);
  statusBar()->setItemAlignment(1, Qt::AlignLeft);

  statusBar()->insertItem(i18n("Download: %1/s").arg(KIO::convertSize(KFTPQueue::Manager::self()->getDownloadSpeed())), 2);
  statusBar()->insertItem(i18n("Upload: %1/s").arg(KIO::convertSize(KFTPQueue::Manager::self()->getUploadSpeed())), 3);
}

void MainWindow::initSidebars()
{
  // BEGIN bottom
  m_bottomSidebar = new KFTPWidgets::Sidebar(centralWidget(), KFTPWidgets::Sidebar::Bottom);

  // Create the queue
  m_queueView = new KFTPWidgets::QueueView(0, "queue");

  // Create the failed transfers view
  KFTPWidgets::FailedTransfers *failedTransfers = new KFTPWidgets::FailedTransfers(0, "failedtransfers");

  m_bottomSidebar->addSidebar(m_queueView, i18n("Queue"), "document");
  m_bottomSidebar->addSidebar(failedTransfers, i18n("Failed Transfers"), "cancel");
  // END bottom

  // BEGIN left
  m_leftSidebar = new KFTPWidgets::Sidebar(m_bottomSidebar->content(), KFTPWidgets::Sidebar::Left);
  m_leftSidebar->setVisible(KFTPCore::Config::showLeftSidebar());

  // Add the bookmarks sidebar
  m_bookmarkSidebar = new KFTPWidgets::Bookmarks::Sidebar(0, "bookmarks");

  // Add the zeroconf sidebar
  m_zeroconfSidebar = new KFTPZeroConfListView(0, "zeroconf");

  m_leftSidebar->addSidebar(m_bookmarkSidebar, i18n("Bookmarks"), "bookmark");
  m_leftSidebar->addSidebar(m_zeroconfSidebar, i18n("Sites Near Me"), "lan");
  // END left
}

void MainWindow::initMainView()
{
  setCentralWidget(new QWidget(this, "qt_central_widget"));

  QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget(), 0, 0, "tLayout");

  // Create the sidebars
  initSidebars();

  mainLayout->addWidget(m_bottomSidebar);

  QSplitter *splitter = new QSplitter(m_leftSidebar->content());

  // Session layouts
  QWidget *box_1 = new QWidget(splitter);
  QWidget *box_2 = new QWidget(splitter);

  QHBoxLayout *lay_1 = new QHBoxLayout(box_1);
  QHBoxLayout *lay_2 = new QHBoxLayout(box_2);

  KFTPTabWidget *leftTabs  = new KFTPTabWidget(box_1, "TabWidgetLeft");
  KFTPTabWidget *rightTabs = new KFTPTabWidget(box_2, "TabWidgetRight");

  leftTabs->setHoverCloseButton(true);
  rightTabs->setHoverCloseButton(true);

  lay_1->addWidget(leftTabs);
  lay_2->addWidget(rightTabs);

  // Create the session manager
  new KFTPSession::Manager(this, new QTabWidget(0, "logs"), leftTabs, rightTabs);

  // Add some sidebars that can't be added before the session manager is created
  m_bottomSidebar->addSidebar(new KFTPWidgets::ThreadView(0, "threads"), i18n("Threads"), "socket");
  m_bottomSidebar->addSidebar(KFTPSession::Manager::self()->getStatTabs(), i18n("Log"), "info");
  m_bottomSidebar->addSidebar(m_trafficGraph, i18n("Traffic"), "share");
}

void MainWindow::slotUpdateStatusBar()
{
  // Status bar
  statusBar()->changeItem(i18n("Download: %1/s").arg(KIO::convertSize(KFTPQueue::Manager::self()->getDownloadSpeed())), 2);
  statusBar()->changeItem(i18n("Upload: %1/s").arg(KIO::convertSize(KFTPQueue::Manager::self()->getUploadSpeed())), 3);
}

void MainWindow::slotUpdateTrafficGraph()
{
  // Update the traffic graph
  if (m_trafficGraph) {
    QValueList<double> trafficList;
    trafficList.append((double) KFTPQueue::Manager::self()->getDownloadSpeed() / 1024);
    trafficList.append((double) KFTPQueue::Manager::self()->getUploadSpeed() / 1024);

    m_trafficGraph->addSample(trafficList);
  }
}

void MainWindow::slotQuickConnect(const QString &title, const QString &host, int port)
{
  // Create/get the new dialog
  KFTPWidgets::QuickConnectDialog *quickConnect = new KFTPWidgets::QuickConnectDialog(this);
  
  quickConnect->setCaption(title);
  quickConnect->setHost(host);
  quickConnect->setPort(port);

  if (!host.isEmpty())
    quickConnect->setFocusToUser();

  if (quickConnect->exec() == KDialogBase::Accepted) {
    // Get the url and connect
    KFTPSession::Session *session = KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, quickConnect->getUrl());

    // Configure the client socket
    quickConnect->setupClient(session->getClient());
  }

  delete quickConnect;
}

void MainWindow::slotConfigChanged()
{
  m_leftSidebar->setVisible(KFTPCore::Config::showLeftSidebar());
}

#include "mainwindow.moc"
