/*
 *  Copyright (C) 2005 Alo Sarv <madcat_@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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __CLIENTMANAGER_H__
#define __CLIENTMANAGER_H__

#include <hnbase/osdep.h>
#include <hnbase/lambda_placeholders.h>
#include <hncore/baseclient.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>

class ClientManager {
public:
	// the implementation data structures; you don't need to understand this.
	// Public api starts few pages below
	struct Indices : public boost::multi_index::indexed_by<
		boost::multi_index::hashed_unique<
			boost::multi_index::identity<BaseClient*>
		>, // sorted by module
		boost::multi_index::ordered_non_unique<
			boost::multi_index::const_mem_fun<
				BaseClient, ModuleBase*, &BaseClient::getModule
			>
		>, // connected clients + module
		boost::multi_index::ordered_non_unique<
			boost::multi_index::composite_key<
				BaseClient*,
				boost::multi_index::const_mem_fun<
					BaseClient, bool,
					&BaseClient::isConnected
				>,
				boost::multi_index::const_mem_fun<
					BaseClient, ModuleBase*,
					&BaseClient::getModule
				>
			>
		>, // sources
		boost::multi_index::ordered_non_unique<
			boost::multi_index::composite_key<
				BaseClient*,
				boost::multi_index::const_mem_fun<
					BaseClient, bool,
					&BaseClient::canDownload
				>,
				boost::multi_index::const_mem_fun<
					BaseClient, PartData*,
					&BaseClient::getSource
				>
			>
		>, // clients in our upload-queue
		boost::multi_index::ordered_non_unique<
			boost::multi_index::composite_key<
				BaseClient*,
				boost::multi_index::const_mem_fun<
					BaseClient, bool,
					&BaseClient::wantsUpload
				>,
				boost::multi_index::const_mem_fun<
					BaseClient, SharedFile*,
					&BaseClient::getReqFile
				>
			>
		>, // client's we'r uploading to
		boost::multi_index::ordered_non_unique<
			boost::multi_index::composite_key<
				BaseClient*,
				boost::multi_index::const_mem_fun<
					BaseClient, bool,
					&BaseClient::isUploading
				>,
				boost::multi_index::const_mem_fun<
					BaseClient, SharedFile*,
					&BaseClient::getReqFile
				>
			>
		>, // clients we'r downloading from
		boost::multi_index::ordered_non_unique<
			boost::multi_index::composite_key<
				BaseClient*,
				boost::multi_index::const_mem_fun<
					BaseClient, bool,
					&BaseClient::isDownloading
				>,
				boost::multi_index::const_mem_fun<
					BaseClient, PartData*,
					&BaseClient::getSource
				>
			>
		>
	> {};
	struct Container
		: public boost::multi_index_container<BaseClient*,Indices>
	{};
	typedef Container::nth_index<0>::type ClientIndex;
	typedef Container::nth_index<1>::type ModuleIndex;
	typedef Container::nth_index<2>::type ConnectedIndex;
	typedef Container::nth_index<3>::type SourceIndex;
	typedef Container::nth_index<4>::type QueueIndex;
	typedef Container::nth_index<5>::type UploadingIndex;
	typedef Container::nth_index<6>::type DownloadingIndex;
	typedef std::pair<
		ModuleIndex::const_iterator,
		ModuleIndex::const_iterator
	> ModuleClients;
	typedef std::pair<
		ConnectedIndex::const_iterator,
		ConnectedIndex::const_iterator
	> ConnectedClients;
	typedef std::pair<
		SourceIndex::const_iterator,
		SourceIndex::const_iterator
	> SourceClients;
	typedef std::pair<
		QueueIndex::const_iterator,
		QueueIndex::const_iterator
	> QueuedClients;
	typedef std::pair<
		UploadingIndex::const_iterator,
		UploadingIndex::const_iterator
	> UploadingClients;
	typedef std::pair<
		DownloadingIndex::const_iterator,
		DownloadingIndex::const_iterator
	> DownloadingClients;

	//! @returns The only instance of this class
	static ClientManager& instance();

	/**
	 * \name Views
	 * All of these return a range of clients matching the query, in form of
	 * std::pair<first, one-past-last>.
	 */
	//!@{
	//! @returns All connected clients
	ConnectedClients getConnected() const {
		return m_connectedIndex.equal_range(boost::make_tuple(true));
	}
	//! @returns All connected clients for specified module
	ConnectedClients getConnected(ModuleBase *mod) const {
		return m_connectedIndex.equal_range(
			boost::make_tuple(true, mod)
		);
	}
	//! @returns All clients for specified module
	ModuleClients find(ModuleBase *mod) const {
		return m_moduleIndex.equal_range(mod);
	}
	//! @returns All sources
	SourceClients getSources() const {
		return m_sourceIndex.equal_range(boost::make_tuple(true));
	}
	//! @returns All sources for specified file
	SourceClients getSources(PartData *file) const {
		return m_sourceIndex.equal_range(
			boost::make_tuple(true, file)
		);
	}
	//! @returns All queued clients
	QueuedClients getQueued() const {
		return m_queueIndex.equal_range(boost::make_tuple(true));
	}
	//! @returns All queued clients for specified file
	QueuedClients getQueued(SharedFile *file) const {
		return m_queueIndex.equal_range(boost::make_tuple(true, file));
	}
	//! @returns All currently uploading clients
	UploadingClients getUploading() const {
		return m_uploadingIndex.equal_range(boost::make_tuple(true));
	}
	//! @returns All clients currently uploading specified file
	UploadingClients getUploading(SharedFile *file) const {
		return m_uploadingIndex.equal_range(
			boost::make_tuple(true, file)
		);
	}
	//! @returns All downloading clients
	DownloadingClients getDownloading() const {
		return m_downloadingIndex.equal_range(boost::make_tuple(true));
	}
	//! @returns All clients currently downloading specified file
	DownloadingClients getDownloading(PartData *file) const {
		return m_downloadingIndex.equal_range(
			boost::make_tuple(true, file)
		);
	}
	//!@}
private:
	// singleton pattern requirements
	ClientManager();
	ClientManager(const ClientManager&);
	ClientManager& operator=(const ClientManager&);
	~ClientManager();

	friend class BaseClient;

	// references to various views of the container
	Container         m_clients;
	ClientIndex      &m_clientIndex;
	ModuleIndex      &m_moduleIndex;
	ConnectedIndex   &m_connectedIndex;
	SourceIndex      &m_sourceIndex;
	QueueIndex       &m_queueIndex;
	UploadingIndex   &m_uploadingIndex;
	DownloadingIndex &m_downloadingIndex;
};

#endif