/***************************************************************************
                          connectionmanager.cpp  -  description
                             -------------------
    begin                : ons dec 18 2002
    copyright            : (C) 2003 by Björn Sahlström
    email                : kbjorn@users.sourceforge.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.                                   *
 *                                                                         *
 ***************************************************************************/

//////////////////////////////////////////////////////////////////////
// Qt specific include files
#include <qmap.h>
#include <qintdict.h>
#include <qregexp.h>
//////////////////////////////////////////////////////////////////////
// KDE specific include files
#include <kio/scheduler.h>
#include <kio/global.h>
#include <kio/slaveconfig.h>
#include <kapplication.h>
#include <kconfig.h>
#include <kconfigbase.h>
#include <kmessagebox.h>
#include <kdebug.h>
//////////////////////////////////////////////////////////////////////
// System specific include files
//////////////////////////////////////////////////////////////////////
// Application specific include files
#include "connectionmanager.h"
#include "kbearmainwiniface.h"
#include "kbearcore.h"
#include "childconnection.h"
#include "childsingleconnection.h"
#include "toplevelconnection.h"
#include "siteconnection.h"
#include "sitechildsingleconnection.h"
#include "sitesingleconnection.h"
#include "connectioninterface.h"
#include "kbearfilecopyjob.h"
#include "kbearcopyjob.h"
#include "kbeardeletejob.h"
#include "transfer.h"

using namespace KBear;

#include "connectionmanager.moc"

//-----------------------------------------------
// class ConnectionManagerPrivate
//-----------------------------------------------
class ConnectionManager::ConnectionManagerPrivate {
	public:
		ConnectionManagerPrivate()
			:	m_connections( 31 )
		{
			m_connections.setAutoDelete( true );
		}
	public:
		// this list maps the connections to the id stored in the SiteInfo
		QIntDict<ConnectionInterface> m_connections;
};
//-----------------------------------------------
// class ConnectionManager
//-----------------------------------------------
ConnectionManager* ConnectionManager::s_instance = 0L;
//-----------------------------------------------
ConnectionManager::ConnectionManager()
	:	QObject( KBearMainWindowInterface::getInstance()->core() ),
	d(  new ConnectionManagerPrivate )
{
	connect( KIO::SlaveConfig::self(), SIGNAL( configNeeded( const QString&, const QString& ) ),
						this, SLOT( slotConfigNeeded( const QString&, const QString& ) ) );
}
//-----------------------------------------------
ConnectionManager::~ConnectionManager() {
	cleanUp();
	delete d;
}
//-----------------------------------------------
void ConnectionManager::cleanUp() {
	QIntDictIterator<ConnectionInterface> it( d->m_connections );
	for( ; it.current(); ++it ) {
		it.current()->closeConnection( true );
		it.current()->deleteLater();
		d->m_connections.remove( it.currentKey() );
	}
}
//-----------------------------------------------
void ConnectionManager::slotConfigNeeded( const QString& prot, const QString& host ) {
	// we always try to resume
	KIO::SlaveConfig::self()->setConfigData( prot, host, QString::fromLatin1("AutoResume"), QString::fromLatin1("true") );
}
//-----------------------------------------------
void ConnectionManager::attachJob( int ID, KIO::SimpleJob* job ) {
	ConnectionInterface* connection = 0L;
	if( ID >= 0 ) {// local filesys
		connection = getConnectionByID( ID );
	}

	if( ID < 0 || ! connection ) {
		KIO::Scheduler::scheduleJob( job );
	}
	else {
		connection->addJob( job );
	}

}
//-----------------------------------------------
ConnectionManager* ConnectionManager::getInstance() {

	if( s_instance == 0L )
		s_instance = new ConnectionManager;
	return s_instance;
}
//-----------------------------------------------
void ConnectionManager::closeConnection( int ID ) {
	if( ID < 0 )
		return;
	ConnectionInterface* connection = getConnectionByID( ID );
	if( connection )
		connection->closeConnection();
}
//-----------------------------------------------
void ConnectionManager::slotConnectionClosed( int ID ) {
	ConnectionInterface* connection = getConnectionByID( ID );
	if( ! connection ) {
		kdError() << "BUG !!!! slotConnectionClosed called without any connection"<< endl;
		return;
	}

	removeConnection( connection );
}
//-----------------------------------------------
void ConnectionManager::closeConnection( const SiteInfo& info ) {
	closeConnection( info.ID() );
}
//-----------------------------------------------
bool ConnectionManager::suspend( int ID ) {
	kdDebug()<<k_funcinfo<<" ID="<<ID<<endl;
	if( ID <  0 )
		return false;
	ConnectionInterface* connection = getConnectionByID( ID );
	if( !  connection )
		return false;

	while( ! connection->getSlave()->suspended() )
		connection->getSlave()->suspend();

	return true;
}
//-----------------------------------------------
bool ConnectionManager::resume( int ID ) {
	kdDebug()<<k_funcinfo<<" ID="<<ID<<endl;
	if( ID <  0 )
		return false;
	ConnectionInterface* connection = getConnectionByID( ID );
	if( !  connection )
		return false;

	if( connection->getSlave()->suspended() ) {
		connection->getSlave()->resume();
		return true;
	}

	return false;
}
//-----------------------------------------------
bool ConnectionManager::openConnection( int ID ) {
	if( ID <  0 )
		return false;
	ConnectionInterface* connection = getConnectionByID( ID );
	if( !  connection )
		return false;

	if( ! connection->openConnection() ) {
		kdError() << "Could not open Connection with ID="<<ID<< endl;
		connection->closeConnection();
		return false;
	}

	return true;
}
//-----------------------------------------------
int ConnectionManager::getNewID() {
	int ID = 0;
	while( d->m_connections.find( ID ) ) // loop until no such key
		++ID;

	return ID;
}
//-----------------------------------------------
bool ConnectionManager::createNewSite( SiteInfo& info ) {
	// first thing to do is to check if the sit exists and if so should it use a single connection ?
	// if that is the case we don't create a new toplevel connection,
	// instead we attach this new one to the already opened
	bool attach = false;
	ConnectionInterface* connection = 0L;
	if( info.singleConnection() ) {
		connection = getConnectionByLabel( info.label() );
		if( connection ) {
			// The site existed
			if( connection->getInfo().singleConnection() )
				attach = true;
		}
	}

	int ID = getNewID();
	info.setID( ID );

	if( info.anonym() ) {
		KConfigGroupSaver cs( kapp->config(), kapp->config()->group() );
		kapp->config()->setGroup( QString::fromLatin1( "General" ) );
		if( info.pass().isEmpty() )
			info.setPass( kapp->config()->readEntry( QString::fromLatin1( "Default Email" ), QString::fromLatin1( "anonymous@" ) ) );
		if( info.user().isEmpty() )
			info.setUser( QString::fromLatin1("anonymous") );
	}

	if( info.singleConnection() ) {
		TopLevelConnectionInterface* topConnection = dynamic_cast<TopLevelConnectionInterface*>( connection );
		if( attach && topConnection ) {
			connection = new SiteChildSingleConnection( topConnection, info );
			topConnection->addConnection( connection );
		}
		else {
			info.setLabel( getConnectionLabel( info.label() ) );
			connection = new SiteSingleConnection( info );
			connect( connection, SIGNAL( infoMessage( int, const QString& ) ),
								this, SIGNAL( infoMessage( int, const QString& ) ) );
		}
	}
	else {
		info.setLabel( getConnectionLabel( info.label() ) );
		connection = new SiteConnection( info );
		connect( connection, SIGNAL( infoMessage( int, const QString& ) ),
							this, SIGNAL( infoMessage( int, const QString& ) ) );
	}

	if( ! connection )
		return false;

	connect( connection, SIGNAL( connected( int ) ), this, SIGNAL( connected( int ) ) );
	connect( connection, SIGNAL( notConnected( int ) ), this, SIGNAL( notConnected( int ) ) );
	connect( connection, SIGNAL( closed( int ) ), this, SLOT( slotConnectionClosed( int ) ) );
	connect( connection, SIGNAL( error( int, int, const QString& ) ), this, SIGNAL( error( int, int, const QString& ) ) );

	kdDebug()<<"Adding SiteConnection with ID="<<ID<<endl;
	d->m_connections.insert( ID, connection );

	return true;
}
//-----------------------------------------------
bool ConnectionManager::createNewConnection( SiteInfo& info ) {
	int originalID = info.ID();
	int ID = getNewID();

	TopLevelConnectionInterface* parentConnection = dynamic_cast<TopLevelConnectionInterface*>( getConnectionByID( originalID ) );

	if( info.anonym() ) {
		KConfigGroupSaver cs( kapp->config(), kapp->config()->group() );
		kapp->config()->setGroup( QString::fromLatin1( "General" ) );
		if( info.pass().isEmpty() )
			info.setPass( kapp->config()->readEntry( QString::fromLatin1( "Default Email" ), QString::fromLatin1( "anonymous@" ) ) );
		if( info.user().isEmpty() )
			info.setUser( QString::fromLatin1("anonymous") );
	}

	info.setID( ID );

	ConnectionInterface* connection = 0L;
	if( parentConnection ) {
		if( parentConnection->getInfo().singleConnection() ) {
			connection = new ChildSingleConnection( parentConnection, info );
			kdDebug()<<k_funcinfo<<" Adding ChildSingleConnection with ID="<<ID<<endl;
		}
		else {
			connection = new ChildConnection( parentConnection, info );
			kdDebug()<<k_funcinfo<<" Adding ChildConnection with ID="<<ID<<endl;
		}
		parentConnection->addConnection( connection );
	}
	else {
		info.setLabel( getConnectionLabel( info.label() ) );
		connection = new TopLevelConnection( info );
		connect( connection, SIGNAL( infoMessage( int, const QString& ) ),
							this, SIGNAL( infoMessage( int, const QString& ) ) );
		kdDebug()<<k_funcinfo<<" Adding TopLevelConnection with ID="<<ID<<endl;
	}

	if( !  connection )
		return false;

	connect( connection, SIGNAL( connected( int ) ), this, SIGNAL( connected( int ) ) );
	connect( connection, SIGNAL( notConnected( int ) ), this, SIGNAL( notConnected( int ) ) );
	connect( connection, SIGNAL( closed( int ) ), this, SLOT( slotConnectionClosed( int ) ) );
	connect( connection, SIGNAL( error( int, int, const QString& ) ), this, SIGNAL( error( int, int, const QString& ) ) );

	d->m_connections.insert( ID, connection );

	return true;
}
//-----------------------------------------------
void ConnectionManager::removeConnection( ConnectionInterface* c ) {
	// first check site conections
	QIntDictIterator<ConnectionInterface> it( d->m_connections );
	for( ; it.current(); ++it ) {
		if( it.current() == c ) {
			int ID = it.currentKey();
			kdDebug()<<k_funcinfo<<" Removing Connection with ID="<<ID<<endl;

			if( dynamic_cast<SiteConnection*>( it.current() ) )
				emit siteClosed( ID );
			else
				emit connectionClosed( ID );

			d->m_connections.remove( ID );
//			c->deleteLater();
			return;
		}
	}
	// if we get here something is definitely wrong
	kdError() << "BUG!!!!!!!! Could not find and remove Connection ="<<c<< endl;
}
//-----------------------------------------------
QString ConnectionManager::getConnectionLabel( const QString& label ) {
	QString result;
	int num = 1;
	QString addStr = QString::null;

	do {
		addStr = ( ( num>1 ) ? (QString::fromLatin1(" (")+QString::number(num)+QString::fromLatin1(")") ): QString::null ) ;
		result = label + addStr;
		++num;
	}while( ! checkNameIntegrity( result ) );

	return result;
}
//-----------------------------------------------
ConnectionInterface* ConnectionManager::getConnectionByID( int ID ){
	if( ID < 0 )
		return 0L;

	ConnectionInterface* connection = 0L;
	if( ! d->m_connections.isEmpty() )
		connection = d->m_connections.find( ID );
	return connection;
}
//-----------------------------------------------
SiteInfo ConnectionManager::getSiteInfo( int ID ){
	SiteInfo info;
	ConnectionInterface* connection = getConnectionByID( ID );
	if( connection )
		info = connection->getInfo();
	return info;
}
//-----------------------------------------------
ConnectionInterface* ConnectionManager::getConnectionByLabel( const QString& label ){
	TopLevelConnectionInterface* connection = 0L;
	bool found = false;
	QIntDictIterator<ConnectionInterface> it( d->m_connections );
	QRegExp reg( label + QString::fromLatin1(" ([0-9]{1,4})") );
	for( ; it.current(); ++it ) {
		connection = dynamic_cast<TopLevelConnectionInterface*>( it.current() );

		if( connection && ( connection->getInfo().label() == label || reg.exactMatch( connection->getInfo().label() ) ) ) {
			found = true;
			break;
		}
	}
	if( found )
		return connection;

	return 0L;
}
//-----------------------------------------------
ConnectionInterface* ConnectionManager::getConnectionBySiteInfo( const SiteInfo& info ){
	return getConnectionByID( info.ID() );
}
//-----------------------------------------------
bool ConnectionManager::checkNameIntegrity( const QString& label ) {
	return ( getConnectionByLabel( label ) == 0L );
}
//-----------------------------------------------
