/*
 * Copyright (c) 2012 Mateusz Piękos
 * Licensed under the simplified BSD license.
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 */


#include <Swiften/Whiteboard/WhiteboardSessionManager.h>

#include <Swiften/Base/foreach.h>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/bind.hpp>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Whiteboard/WhiteboardResponder.h>
#include <Swiften/Presence/PresenceOracle.h>
#include "Swiften/Disco/EntityCapsProvider.h"

namespace Swift {
	WhiteboardSessionManager::WhiteboardSessionManager(IQRouter* router, StanzaChannel* stanzaChannel, PresenceOracle* presenceOracle, EntityCapsProvider* capsProvider) : router_(router), stanzaChannel_(stanzaChannel), presenceOracle_(presenceOracle), capsProvider_(capsProvider) {
		responder = new WhiteboardResponder(this, router);
		responder->start();
		stanzaChannel_->onPresenceReceived.connect(boost::bind(&WhiteboardSessionManager::handlePresenceReceived, this, _1));
		stanzaChannel_->onAvailableChanged.connect(boost::bind(&WhiteboardSessionManager::handleAvailableChanged, this, _1));
	}

	WhiteboardSessionManager::~WhiteboardSessionManager() {
		responder->stop();
		delete responder;
	}

	WhiteboardSession::ref WhiteboardSessionManager::getSession(const JID& to) {
		if (sessions_.find(to) == sessions_.end()) {
			return boost::shared_ptr<WhiteboardSession>();
		}
		return sessions_[to];
	}

	OutgoingWhiteboardSession::ref WhiteboardSessionManager::createOutgoingSession(const JID& to) {
		JID fullJID = to;
		if (fullJID.isBare()) {
			fullJID = getFullJID(fullJID);
		}
		OutgoingWhiteboardSession::ref session = boost::make_shared<OutgoingWhiteboardSession>(fullJID, router_);
		sessions_[fullJID] = session;
		session->onSessionTerminated.connect(boost::bind(&WhiteboardSessionManager::deleteSessionEntry, this, _1));
		session->onRequestRejected.connect(boost::bind(&WhiteboardSessionManager::deleteSessionEntry, this, _1));
		return session;
	}

	WhiteboardSession::ref WhiteboardSessionManager::requestSession(const JID& to) {
		WhiteboardSession::ref session = getSession(to);
		if (!session) {
			OutgoingWhiteboardSession::ref outgoingSession = createOutgoingSession(to);
			outgoingSession->startSession();
			return outgoingSession;
		} else {
			return session;
		}
	}

	void WhiteboardSessionManager::handleIncomingSession(IncomingWhiteboardSession::ref session) {
		sessions_[session->getTo()] = session;
		session->onSessionTerminated.connect(boost::bind(&WhiteboardSessionManager::deleteSessionEntry, this, _1));
		onSessionRequest(session);
	}

	JID WhiteboardSessionManager::getFullJID(const JID& bareJID) {
		JID fullReceipientJID;
		int priority = INT_MIN;
	
		//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
		std::vector<Presence::ref> presences = presenceOracle_->getAllPresence(bareJID);

		//iterate over them
		foreach(Presence::ref pres, presences) {
			if (pres->getPriority() > priority) {
				// look up caps from the jid
				DiscoInfo::ref info = capsProvider_->getCaps(pres->getFrom());
				if (info && info->hasFeature(DiscoInfo::WhiteboardFeature)) {
					priority = pres->getPriority();
					fullReceipientJID = pres->getFrom();
				}
			}
		}
	
		return fullReceipientJID;
	}

	void WhiteboardSessionManager::deleteSessionEntry(const JID& contact) {
		sessions_.erase(contact);
	}

	void WhiteboardSessionManager::handlePresenceReceived(Presence::ref presence) {
		if (!presence->isAvailable()) {
			WhiteboardSession::ref session = getSession(presence->getFrom());
			if (session) {
				session->cancel();
			}
		}
	}

	void WhiteboardSessionManager::handleAvailableChanged(bool available) {
		if (!available) {
			std::map<JID, WhiteboardSession::ref> sessionsCopy = sessions_;
			std::map<JID, WhiteboardSession::ref>::iterator it;
			for (it = sessionsCopy.begin(); it != sessionsCopy.end(); ++it) {
				it->second->cancel();
			}
		}
	}
}