/*
 * 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>
 *
 * 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 "kftpsession.h"
#include "kftpapi.h"
#include "browser/detailsview.h"
#include "browser/treeview.h"
#include "browser/view.h"
#include "kftpbookmarks.h"
#include "misc.h"
#include "widgets/systemtray.h"
#include "mainactions.h"

#include "misc/kftpconfig.h"
#include "misc/filter.h"

#include <tqdir.h>
#include <tqobjectlist.h>

#include <tdemessagebox.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <kpassdlg.h>
#include <kstaticdeleter.h>

using namespace KFTPGrabberBase;
using namespace KFTPEngine;
using namespace KFTPCore::Filter;

namespace KFTPSession {

//////////////////////////////////////////////////////////////////
////////////////////////    Connection     ///////////////////////
//////////////////////////////////////////////////////////////////

Connection::Connection(Session *session, bool primary)
  : TQObject(session),
    m_primary(primary),
    m_busy(false),
    m_aborting(false),
    m_scanning(false)
{
  // Create the actual connection client
  m_client = new KFTPEngine::Thread();
  
  connect(m_client->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), this, SLOT(slotEngineEvent(KFTPEngine::Event*)));

  // If this is not a core session connection, connect
  if (!primary) {
    // Connect to the server
    KURL url = session->getClient()->socket()->getCurrentUrl();
    
    KFTPBookmarks::Manager::self()->setupClient(session->getSite(), m_client);
    m_client->connect(url);
  }
}

Connection::~Connection()
{
  delete m_client;
}

bool Connection::isConnected()
{
  return !static_cast<Session*>(parent())->isRemote() || m_client->socket()->isConnected();
}

void Connection::acquire(KFTPQueue::Transfer *transfer)
{
  if (m_busy || !static_cast<Session*>(parent())->isRemote())
    return;

  m_curTransfer = transfer;
  m_busy = true;

  connect(transfer, SIGNAL(transferComplete(long)), this, SLOT(slotTransferCompleted()));
  connect(transfer, SIGNAL(transferAbort(long)), this, SLOT(slotTransferCompleted()));

  emit connectionAcquired();
}

void Connection::remove()
{
  // Disconnect all signals from the transfer
  if (m_curTransfer)
    m_curTransfer->TQObject::disconnect(this);

  m_curTransfer = 0L;
  m_busy = false;

  emit connectionRemoved();
  emit static_cast<Session*>(parent())->freeConnectionAvailable();
}

void Connection::abort()
{
  if (m_aborting || !m_client->socket()->isBusy())
    return;

  // Emit the signal before aborting
  emit aborting();

  // Abort transfer
  m_aborting = true;
  m_client->abort();
  m_aborting = false;
}

void Connection::scanDirectory(KFTPQueue::Transfer *parent)
{
  // Lock the connection and the transfer
  acquire(parent);
  parent->lock();
  
  m_scanning = true;
  
  if (isConnected())
    m_client->scan(parent->getSourceUrl());
}

void Connection::addScannedDirectory(KFTPEngine::DirectoryTree *tree, KFTPQueue::Transfer *parent)
{
  // Directories
  DirectoryTree::DirIterator dirEnd = tree->directories()->end();
  for (DirectoryTree::DirIterator i = tree->directories()->begin(); i != dirEnd; i++) {
    KURL sourceUrlBase = parent->getSourceUrl();
    KURL destUrlBase = parent->getDestUrl();
    
    sourceUrlBase.addPath((*i)->info().filename());
    destUrlBase.addPath((*i)->info().filename());
    
    // Check if we should skip this entry
    const ActionChain *actionChain = Filters::self()->process(sourceUrlBase, 0, true);
     
    if (actionChain && actionChain->getAction(Action::Skip))
      continue;
    
    // Add directory transfer
    KFTPQueue::TransferDir *transfer = new KFTPQueue::TransferDir(parent);
    transfer->setSourceUrl(sourceUrlBase);
    transfer->setDestUrl(destUrlBase);
    transfer->setTransferType(parent->getTransferType());
    transfer->setId(KFTPQueue::Manager::self()->nextTransferId());
    
    emit KFTPQueue::Manager::self()->newTransfer(transfer);
    
    addScannedDirectory(*i, transfer);
    
    if (KFTPCore::Config::skipEmptyDirs() && !transfer->hasChildren())
      KFTPQueue::Manager::self()->removeTransfer(transfer, false);
  }
  
  // Files
  DirectoryTree::FileIterator fileEnd = tree->files()->end();
  for (DirectoryTree::FileIterator i = tree->files()->begin(); i != fileEnd; i++) {
    KURL sourceUrlBase = parent->getSourceUrl();
    KURL destUrlBase = parent->getDestUrl();
    
    sourceUrlBase.addPath((*i).filename());
    destUrlBase.addPath((*i).filename());
    
    // Check if we should skip this entry
    const ActionChain *actionChain = Filters::self()->process(sourceUrlBase, (*i).size(), false);
     
    if (actionChain && actionChain->getAction(Action::Skip))
      continue;
    
    // Add file transfer
    KFTPQueue::TransferFile *transfer = new KFTPQueue::TransferFile(parent);
    transfer->addSize((*i).size());
    transfer->setSourceUrl(sourceUrlBase);
    transfer->setDestUrl(destUrlBase);
    transfer->setTransferType(parent->getTransferType());
    transfer->setId(KFTPQueue::Manager::self()->nextTransferId());
    
    emit KFTPQueue::Manager::self()->newTransfer(transfer);
  }
}

void Connection::slotEngineEvent(KFTPEngine::Event *event)
{
  switch (event->type()) {
    case Event::EventDisconnect: {
      emit connectionLost(this);
      break;
    }
    case Event::EventConnect: {
      emit connectionEstablished();
      
      if (m_scanning) {
        // Connected successfully, let's scan
        m_client->scan(m_curTransfer->getSourceUrl());
      }
      break;
    }
    case Event::EventError: {
      ErrorCode error = event->getParameter(0).asErrorCode();
      
      if (m_scanning && (error == ConnectFailed || error == LoginFailed || error == OperationFailed)) {
        // Scanning should be aborted, since there was an error
        m_scanning = false;
        m_curTransfer->unlock();
        remove();
        
        emit static_cast<Session*>(parent())->dirScanDone();
      }
      break;
    }
    case Event::EventScanComplete: {
      if (m_scanning) {
        // We have the listing
        DirectoryTree *tree = static_cast<DirectoryTree*>(event->getParameter(0).asData());
        addScannedDirectory(tree, m_curTransfer);
        delete tree;
        
        m_scanning = false;
        m_curTransfer->unlock();
        remove();
        
        emit static_cast<Session*>(parent())->dirScanDone();
      }
      break;
    }
    default: break;
  }
}

void Connection::slotTransferCompleted()
{
  // Remove the lock
  remove();
}

void Connection::reconnect()
{
  if (!m_client->socket()->isConnected()) {
    KFTPBookmarks::Manager::self()->setupClient(static_cast<Session*>(parent())->getSite(), m_client);
    m_client->connect(m_client->socket()->getCurrentUrl());
  }
}

////////////////////////////////////////////////////////
////////////////////    Session     ////////////////////
////////////////////////////////////////////////////////

Session::Session(Side side)
  : TQObject(),
    m_side(side),
    m_remote(false),
    m_aborting(false),
    m_registred(false),
    m_site(0)
{
  // Register this session
  Manager::self()->registerSession(this);
}

Session::~Session()
{
}

KFTPEngine::Thread *Session::getClient()
{
  // Return the first (core) connection's client
  return m_connections.at(0)->getClient();
}

bool Session::isConnected()
{
  // If there are no connections, just check if the session is remote
  if (m_connections.count() == 0)
    return !m_remote;
  
  return m_connections.at(0)->isConnected();
}

void Session::slotClientEngineEvent(KFTPEngine::Event *event)
{
  switch (event->type()) {
    case Event::EventConnect: {
      // ***************************************************************************
      // ****************************** EventConnect *******************************
      // ***************************************************************************
      m_remote = true;
      m_aborting = false;
      m_lastUrl = getClient()->socket()->getCurrentUrl();
      
      TQString siteName;
      if (m_site)
        siteName = m_site->getAttribute("name");
      else
        siteName = m_lastUrl.host();
  
      Manager::self()->getTabs(m_side)->changeTab(m_fileView, siteName);
      Manager::self()->getStatTabs()->changeTab(m_log, i18n("Log (%1)").arg(siteName));
      Manager::self()->getStatTabs()->showPage(m_log);
      Manager::self()->doEmitUpdate();
      
      KURL homeUrl = getClient()->socket()->getCurrentUrl();
      
      if (m_site && !m_site->getProperty("defremotepath").isEmpty())
        homeUrl.setPath(m_site->getProperty("defremotepath"));
      else
        homeUrl.setPath(getClient()->socket()->getDefaultDirectory());
      
      m_fileView->setHomeUrl(homeUrl);
      m_fileView->goHome();
      
      Session *opposite = Manager::self()->getActive(oppositeSide(m_side));
      
      if (m_site && !opposite->isRemote()) {
        TQString localPath = m_site->getProperty("deflocalpath");
        
        if (!localPath.isEmpty())
          opposite->getFileView()->openUrl(KURL(localPath));
      }
      break;
    }
    case Event::EventDisconnect: {
      // ***************************************************************************
      // **************************** EventDisconnect ******************************
      // ***************************************************************************
      m_remote = false;
      m_aborting = false;
    
      Manager::self()->getTabs(m_side)->changeTab(m_fileView, i18n("Local Session"));
      Manager::self()->getStatTabs()->changeTab(m_log, "[" + i18n("Log") + "]");
      Manager::self()->doEmitUpdate();
      
      m_fileView->setHomeUrl(KURL(KFTPCore::Config::defLocalDir()));
      m_fileView->goHome();
      break;
    }
    case Event::EventCommand: m_log->ftpLog(1, event->getParameter(0).asString());  break;
    case Event::EventMultiline: m_log->ftpLog(2, event->getParameter(0).asString()); break;
    case Event::EventResponse: m_log->ftpLog(0, event->getParameter(0).asString()); break;
    case Event::EventMessage: m_log->ftpLog(3, event->getParameter(0).asString()); break;
    case Event::EventRetrySuccess: {
      // ***************************************************************************
      // ************************** EventRetrySuccess ******************************
      // ***************************************************************************
      if (KFTPCore::Config::showRetrySuccessBalloon()) {
        KFTPWidgets::SystemTray::self()->showBalloon(i18n("Connection with %1 has been successfully established.").arg(getClient()->socket()->getCurrentUrl().host()));
      }
      break;
    }
    case Event::EventReloadNeeded: {
      // We should only do refreshes if the queue is not being processed
      if (KFTPQueue::Manager::self()->getNumRunning() == 0)
        m_fileView->reload();
      break;
    }
    case Event::EventPubkeyPassword: {
      // A public-key authentication password was requested
      TQString pass;
      int ret = KPasswordDialog::getPassword(pass, i18n("Please provide your private key decryption password."));
      
      if (ret == KPasswordDialog::Accepted) {
        PubkeyWakeupEvent *event = new PubkeyWakeupEvent();
        event->password = pass;
        
        getClient()->wakeup(event);
      } else {
        getClient()->abort();
      }
      break;
    }
    default: break;
  }
}

void Session::scanDirectory(KFTPQueue::Transfer *parent, Connection *connection)
{
  // Go trough all files in path and add them as transfers that have parent as their parent
  // transfer
  KURL path = parent->getSourceUrl();

  if (path.isLocalFile()) {
    connect(new DirectoryScanner(parent), SIGNAL(completed()), this, SIGNAL(dirScanDone()));
  } else if (m_remote) {
    if (!connection) {
      if (!isFreeConnection()) {
        emit dirScanDone();
        return;
      }
      
      // Assign a new connection (it might be unconnected!)
      connection = assignConnection();
    }
    
    connection->scanDirectory(parent);
  }
}

void Session::abort()
{
  if (m_aborting)
    return;

  m_aborting = true;

  emit aborting();

  // Abort all connections
  Connection *conn;
  for (conn = m_connections.first(); conn; conn = m_connections.next()) {
    conn->abort();
  }

  m_aborting = false;
}

void Session::reconnect(const KURL &url)
{
  // Set the reconnect url
  m_reconnectUrl = url;
  
  if (m_remote && getClient()->socket()->isConnected()) {
    abort();
    
    connect(getClient()->eventHandler(), SIGNAL(disconnected()), this, SLOT(slotStartReconnect()));
    getClient()->disconnect();
  } else {
    // The session is already disconnected, just call the slot
    slotStartReconnect();
  }
}

void Session::slotStartReconnect()
{
  disconnect(getClient()->eventHandler(), SIGNAL(disconnected()), this, SLOT(slotStartReconnect()));
  
  // Reconnect only if this is a remote url
  if (!m_reconnectUrl.isLocalFile()) {
    KFTPBookmarks::Manager::self()->setupClient(m_site, getClient());
    getClient()->connect(m_reconnectUrl);
  }
  
  // Invalidate the url
  m_reconnectUrl = KURL();
}

int Session::getMaxThreadCount()
{
  // First get the global thread count
  int count = KFTPCore::Config::threadCount();
  
  if (!KFTPCore::Config::threadUsePrimary())
    count++;

  // Try to see if threads are disabled for this site
  if (count > 1 && isRemote()) {
    if (m_site && m_site->getIntProperty("disableThreads"))
      return 1;
  }

  return count;
}

bool Session::isFreeConnection()
{
  unsigned int max = getMaxThreadCount();
  unsigned int free = 0;

  if ((m_connections.count() < max && max > 1) || !isRemote())
    return true;

  Connection *conn;
  for (conn = m_connections.first(); conn; conn = m_connections.next()) {
    if (!conn->isBusy() && (!conn->isPrimary() || KFTPCore::Config::threadUsePrimary() || max == 1))
      free++;
  }

  return free > 0;
}

Connection *Session::assignConnection()
{
  unsigned int max = getMaxThreadCount();

  if (m_connections.count() == 0) {
    // We need a new core connection
    Connection *conn = new Connection(this, true);
    m_connections.append(conn);

    Manager::self()->doEmitUpdate();

    return conn;
  } else {
    // Find a free connection
    Connection *conn;
    for (conn = m_connections.first(); conn; conn = m_connections.next()) {
      if (!conn->isBusy() && (!conn->isPrimary() || KFTPCore::Config::threadUsePrimary() || max == 1))
        return conn;
    }

    // No free connection has been found, but we may be able to create
    // another (if we are within limits)
    if (m_connections.count() < max) {
      conn = new Connection(this);
      m_connections.append(conn);

      Manager::self()->doEmitUpdate();

      return conn;
    }
  }

  return 0;
}

void Session::disconnectAllConnections()
{
  // Abort any possible transfers first
  abort();
  
  // Now disconnect all connections
  Connection *conn;

  for (conn = m_connections.first(); conn; conn = m_connections.next()) {
    if (conn->getClient()->socket()->isConnected()) {
      conn->getClient()->disconnect();
    }
  }
}

////////////////////////////////////////////////////////
/////////////////////// Manager ////////////////////////
////////////////////////////////////////////////////////

Manager *Manager::m_self = 0;

Manager *Manager::self()
{
  return m_self;
}

Manager::Manager(TQObject *parent, TQTabWidget *stat, KFTPTabWidget *left, KFTPTabWidget *right)
  : TQObject(parent),
    m_statTabs(stat),
    m_leftTabs(left),
    m_rightTabs(right),
    m_active(0),
    m_leftActive(0),
    m_rightActive(0)
{
  Manager::m_self = this;

  // Connect some signals
  connect(left, SIGNAL(currentChanged(TQWidget*)), this, SLOT(slotActiveChanged(TQWidget*)));
  connect(right, SIGNAL(currentChanged(TQWidget*)), this, SLOT(slotActiveChanged(TQWidget*)));

  connect(left, SIGNAL(closeRequest(TQWidget*)), this, SLOT(slotSessionCloseRequest(TQWidget*)));
  connect(right, SIGNAL(closeRequest(TQWidget*)), this, SLOT(slotSessionCloseRequest(TQWidget*)));
}

void Manager::registerSession(Session *session)
{
  m_active = session;
  
  // Create some new stuff and assign it to the session
  session->assignConnection();
  session->m_fileView = new KFTPWidgets::Browser::View(0L, "", session->getClient(), session);
  session->m_log = new KFTPWidgets::LogView();
  
  // Install event filters
  session->getFileView()->getDetailsView()->installEventFilter(this);
  session->getFileView()->getTreeView()->installEventFilter(this);
  session->getFileView()->m_toolBarFirst->installEventFilter(this);
  session->getFileView()->m_toolBarSecond->installEventFilter(this);

  connect(session->getFileView()->getDetailsView(), SIGNAL(clicked(TQListViewItem*)), this, SLOT(slotSwitchFocus()));
  connect(session->getFileView()->getTreeView(), SIGNAL(clicked(TQListViewItem*)), this, SLOT(slotSwitchFocus()));
  connect(session->getFileView()->m_toolBarFirst, SIGNAL(clicked(int)), this, SLOT(slotSwitchFocus()));
  connect(session->getFileView()->m_toolBarSecond, SIGNAL(clicked(int)), this, SLOT(slotSwitchFocus()));

  // Connect some signals
  connect(session->getClient()->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), session, SLOT(slotClientEngineEvent(KFTPEngine::Event*)));

  // Assign GUI positions
  m_statTabs->addTab(session->m_log, "[" + i18n("Log") + "]");
  getTabs(session->m_side)->addTab(session->m_fileView, KFTPGrabberBase::loadSmallIcon("computer"), i18n("Session"));

  // Actually add the session
  m_sessionList.append(session);
  session->m_registred = true;
}

KFTPWidgets::Browser::View *Manager::getActiveView()
{
  if (m_active)
    return m_active->getFileView();
  
  return 0;
}

Session *Manager::getActiveSession()
{
  return m_active;
}

bool Manager::eventFilter(TQObject *object, TQEvent *event)
{
  if (event->type() == TQEvent::FocusIn || event->type() == TQEvent::MouseButtonPress) {
    switchFocusToObject(object);
  }
  
  return false;
}

void Manager::slotSwitchFocus()
{
  switchFocusToObject(TQObject::sender());
}

void Manager::switchFocusToObject(const TQObject *object)
{
  if (!object)
    return;
  
  for (;;) {
    if (object->isA("KFTPWidgets::Browser::View"))
      break;
    
    if (!(object = object->parent()))
      break;
  }
  
  if (object) {
    // We have the proper object
    Session *session = find(static_cast<const KFTPWidgets::Browser::View*>(object));
    
    if (session && session != m_active) {
      m_active = session;
      
      // Open the current session's log tab
      if (session->isRemote())
        m_statTabs->showPage(session->getLog());
    }
  }
}

void Manager::unregisterSession(Session *session)
{
  // Destroy all objects related to the session and remove it
  getTabs(session->m_side)->removePage(session->m_fileView);
  m_statTabs->removePage(session->m_log);

  if (session->getClient()->socket()->isConnected()) {
    session->abort();
    session->getClient()->disconnect();
  }

  // Delete objects
  session->m_fileView->deleteLater();
  session->m_log->deleteLater();

  // Actually remove the session
  m_sessionList.remove(session);
  delete session;
  
  // Emit update
  emit update();
}

void Manager::doEmitUpdate()
{
  emit update();
}

void Manager::disconnectAllSessions()
{
  Session *i;

  for (i = m_sessionList.first(); i; i = m_sessionList.next())
    i->disconnectAllConnections();
}

Session *Manager::find(KFTPEngine::Thread *client)
{
  Session *i;

  for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
    if (i->getClient() == client)
      return i;
  }

  return 0L;
}

Session *Manager::find(KFTPWidgets::Browser::View *fileView)
{
  Session *i;

  for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
    if (i->m_fileView == fileView)
      return i;
  }

  return 0L;
}

Session *Manager::find(KFTPWidgets::LogView *log)
{
  Session *i;

  for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
    if (i->m_log == log)
      return i;
  }

  return 0L;
}

Session *Manager::find(const KURL &url, bool mustUnlock)
{
  if (url.isLocalFile())
    return find(true);

  Session *i;

  for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
    KURL tmp = i->getClient()->socket()->getCurrentUrl();
    tmp.setPath(url.path());

    if (tmp == url && i->isRemote() && i->isConnected() && (!mustUnlock || i->isFreeConnection()))
      return i;
  }
  
  return 0L;
}

Session *Manager::find(bool local)
{
  Session *i;

  for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
    if (i->m_remote != local)
      return i;
  }

  return 0L;
}

Session *Manager::findLast(const KURL &url, Side side)
{
  if (url.isLocalFile())
    return find(true);

  Session *i;

  for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
    KURL tmp = i->m_lastUrl;
    tmp.setPath(url.path());

    if (tmp == url && !i->isRemote() && (i->getSide() || side == IgnoreSide))
      return i;
  }
  
  return 0L;
}

Session *Manager::spawnLocalSession(Side side, bool forceNew)
{
  // Creates a new local session
  Session *session = 0L;

  if (forceNew || (session = find(true)) == 0L || (session->m_side != side && side != IgnoreSide)) {
    side = side == IgnoreSide ? LeftSide : side;

    session = new Session(side);
    session->m_remote = false;
    getTabs(side)->changeTab(session->m_fileView, i18n("Local Session"));
    getStatTabs()->changeTab(session->m_log, "[" + i18n("Log") + "]");
  }

  setActive(session);
  return session;
}

Session *Manager::spawnRemoteSession(Side side, const KURL &remoteUrl, KFTPBookmarks::Site *site, bool mustUnlock)
{
  // Creates a new remote session and connects it to the correct server
  Session *session;

  if (remoteUrl.isLocalFile())
    return spawnLocalSession(side);

  if ((session = find(remoteUrl, mustUnlock)) == 0L || (session->m_side != side && side != IgnoreSide)) {
    // Try to find the session that was last connected to this URL
    if ((session = findLast(remoteUrl, side)) == 0L) {
      // Attempt to reuse a local session if one exists one the right side
      session = getActive(RightSide);
      
      if (session->isRemote()) {
        side = side == IgnoreSide ? RightSide : side;
        session = new Session(side);
      }
    }
  
    // Try to find the site by url if it is not set
    if (!site)
      site = KFTPBookmarks::Manager::self()->findSite(remoteUrl);

    // Set properties
    session->m_remote = true;
    session->m_site = site;
    m_active = session;

    KFTPBookmarks::Manager::self()->setupClient(site, session->getClient());
    session->getClient()->connect(remoteUrl);
  }

  return session;
}

void Manager::setActive(Session *session)
{
  // Make a session active on its own side ;)
  Session *oldActive = getActive(session->m_side);

  oldActive ? oldActive->m_active = false : 0;
  session->m_active = true;

  switch (session->m_side) {
    case LeftSide: m_leftActive = session; break;
    case RightSide: m_rightActive = session; break;
    case IgnoreSide: tqDebug("Invalid side specified!"); return;
  }

  // Refresh the GUI
  getTabs(session->m_side)->showPage(session->m_fileView);
}

Session *Manager::getActive(Side side)
{
  switch (side) {
    case LeftSide: return m_leftActive;
    case RightSide: return m_rightActive;
    case IgnoreSide: tqDebug("Invalid side specified!"); break;
  }

  return NULL;
}

KFTPTabWidget *Manager::getTabs(Side side)
{
  switch (side) {
    case LeftSide: return m_leftTabs;
    case RightSide: return m_rightTabs;
    case IgnoreSide: tqDebug("Invalid side specified!"); break;
  }

  return NULL;
}

void Manager::slotActiveChanged(TQWidget *page)
{
  Session *session = find(static_cast<KFTPWidgets::Browser::View*>(page));
  setActive(session);
}

void Manager::slotSessionCloseRequest(TQWidget *page)
{
  Session *session = find(static_cast<KFTPWidgets::Browser::View*>(page));

  if (getTabs(session->m_side)->count() == 1) {
    KMessageBox::error(0L, i18n("At least one session must remain open on each side."));
    return;
  }

  if ((session->m_remote && session->getClient()->socket()->isBusy()) || !session->isFreeConnection()) {
    KMessageBox::error(0L, i18n("Please finish all transfers before closing the session."));
    return;
  } else {
    // Remove the session
    if (session->getClient()->socket()->isConnected()) {
      if (KMessageBox::questionYesNo(0L, i18n("This session is currently connected. Are you sure you wish to disconnect?"), i18n("Close Session")) == KMessageBox::No)
        return;
    }

    unregisterSession(session);
  }
}

}

#include "kftpsession.moc"
