/*
 * Copyright (c) 2010-2015 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#include <Swiften/Client/Client.h>

#include <Swiften/Queries/Responders/SoftwareVersionResponder.h>
#include <Swiften/Roster/XMPPRosterImpl.h>
#include <Swiften/Roster/XMPPRosterController.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Presence/StanzaChannelPresenceSender.h>
#include <Swiften/Presence/DirectedPresenceSender.h>
#include <Swiften/MUC/MUCRegistry.h>
#include <Swiften/MUC/MUCManager.h>
#include <Swiften/PubSub/PubSubManagerImpl.h>
#include <Swiften/Client/MemoryStorages.h>
#include <Swiften/VCards/VCardManager.h>
#include <Swiften/VCards/VCardManager.h>
#include <Swiften/Avatars/AvatarManagerImpl.h>
#include <Swiften/Disco/CapsManager.h>
#include <Swiften/Disco/EntityCapsManager.h>
#include <Swiften/Disco/ClientDiscoManager.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Presence/SubscriptionManager.h>
#include <Swiften/TLS/BlindCertificateTrustChecker.h>
#include <Swiften/Client/NickManagerImpl.h>
#include <Swiften/Client/ClientSession.h>
#include <Swiften/Jingle/JingleSessionManager.h>
#include <Swiften/Network/NetworkFactories.h>
#include <Swiften/FileTransfer/FileTransferManagerImpl.h>
#include <Swiften/Whiteboard/WhiteboardSessionManager.h>
#include <Swiften/Client/ClientBlockListManager.h>
#ifndef SWIFT_EXPERIMENTAL_FT
#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>
#endif

namespace Swift {

Client::Client(const JID& jid, const SafeString& password, NetworkFactories* networkFactories, Storages* storages) : CoreClient(jid, password, networkFactories), storages(storages) {
	memoryStorages = new MemoryStorages(networkFactories->getCryptoProvider());

	softwareVersionResponder = new SoftwareVersionResponder(getIQRouter());
	softwareVersionResponder->start();

	roster = new XMPPRosterImpl();
	rosterController = new XMPPRosterController(getIQRouter(), roster, getStorages()->getRosterStorage());

	subscriptionManager = new SubscriptionManager(getStanzaChannel());

	presenceOracle = new PresenceOracle(getStanzaChannel());
	presenceOracle->onPresenceChange.connect(boost::ref(onPresenceChange));

	stanzaChannelPresenceSender = new StanzaChannelPresenceSender(getStanzaChannel());
	directedPresenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender);
	discoManager = new ClientDiscoManager(getIQRouter(), directedPresenceSender, networkFactories->getCryptoProvider());

	mucRegistry = new MUCRegistry();
	mucManager = new MUCManager(getStanzaChannel(), getIQRouter(), directedPresenceSender, mucRegistry);

	vcardManager = new VCardManager(jid, getIQRouter(), getStorages()->getVCardStorage());
	avatarManager = new AvatarManagerImpl(vcardManager, getStanzaChannel(), getStorages()->getAvatarStorage(), networkFactories->getCryptoProvider(), mucRegistry);
	capsManager = new CapsManager(getStorages()->getCapsStorage(), getStanzaChannel(), getIQRouter(), networkFactories->getCryptoProvider());
	entityCapsManager = new EntityCapsManager(capsManager, getStanzaChannel());

	nickManager = new NickManagerImpl(jid.toBare(), vcardManager);
	nickResolver = new NickResolver(jid.toBare(), roster, vcardManager, mucRegistry);

	blindCertificateTrustChecker = new BlindCertificateTrustChecker();
	
	jingleSessionManager = new JingleSessionManager(getIQRouter());
	blockListManager = new ClientBlockListManager(getIQRouter());
	fileTransferManager = NULL;

	whiteboardSessionManager = NULL;
#ifdef SWIFT_EXPERIMENTAL_WB
	whiteboardSessionManager = new WhiteboardSessionManager(getIQRouter(), getStanzaChannel(), presenceOracle, getEntityCapsProvider());
#endif

	pubsubManager = new PubSubManagerImpl(getStanzaChannel(), getIQRouter());
}

Client::~Client() {
	delete pubsubManager;
	delete whiteboardSessionManager;

	delete fileTransferManager;
	delete blockListManager;
	delete jingleSessionManager;
	
	delete blindCertificateTrustChecker;

	delete nickResolver;
	delete nickManager;

	delete entityCapsManager;
	delete capsManager;
	delete avatarManager;
	delete vcardManager;

	delete mucManager;
	delete mucRegistry;

	delete discoManager;
	delete directedPresenceSender;
	delete stanzaChannelPresenceSender;

	delete presenceOracle;
	delete subscriptionManager;
	delete rosterController;
	delete roster;

	softwareVersionResponder->stop();
	delete softwareVersionResponder;

	delete memoryStorages;
}

XMPPRoster* Client::getRoster() const {
	return roster;
}

void Client::setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os) {
	softwareVersionResponder->setVersion(name, version, os);
}

void Client::handleConnected() {
	delete fileTransferManager;
#ifdef SWIFT_EXPERIMENTAL_FT
	fileTransferManager = new FileTransferManagerImpl(
			getJID(), 
			jingleSessionManager, 
			getIQRouter(), 
			getEntityCapsProvider(), 
			presenceOracle, 
			getNetworkFactories()->getConnectionFactory(), 
			getNetworkFactories()->getConnectionServerFactory(), 
			getNetworkFactories()->getTimerFactory(), 
			getNetworkFactories()->getDomainNameResolver(),
			getNetworkFactories()->getNetworkEnvironment(),
			getNetworkFactories()->getNATTraverser(),
			getNetworkFactories()->getCryptoProvider());
#else
	fileTransferManager = new DummyFileTransferManager();
#endif
	discoManager->handleConnected();
}

void Client::requestRoster() {
	// FIXME: We should set this once when the session is finished, but there
	// is currently no callback for this
	if (getSession()) {
		rosterController->setUseVersioning(getSession()->getRosterVersioningSupported());
	}
	rosterController->requestRoster();
}

Presence::ref Client::getLastPresence(const JID& jid) const {
	return presenceOracle->getLastPresence(jid);
}

Presence::ref Client::getHighestPriorityPresence(const JID& bareJID) const {
	return presenceOracle->getHighestPriorityPresence(bareJID);
}

Storages* Client::getStorages() const {
	if (storages) {
		return storages;
	}
	return memoryStorages;
}

PresenceSender* Client::getPresenceSender() const {
	return discoManager->getPresenceSender();
}

EntityCapsProvider* Client::getEntityCapsProvider() const {
	return entityCapsManager;
}


void Client::setAlwaysTrustCertificates() {
	setCertificateTrustChecker(blindCertificateTrustChecker);
}

NickManager* Client::getNickManager() const {
	return nickManager;
}

FileTransferManager* Client::getFileTransferManager() const {
	return fileTransferManager;
}

WhiteboardSessionManager* Client::getWhiteboardSessionManager() const {
	return whiteboardSessionManager;
}

}