/***************************************************************************
 *   Copyright (C) 2005 by Joris Guisson                                   *
 *   joris.guisson@gmail.com                                               *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/
#ifndef BTPEERMANAGER_H
#define BTPEERMANAGER_H


#include <interfaces/peersource.h>
#include <ktorrent_export.h>
#include <peer/peer.h>
#include <peer/superseeder.h>
#include <peer/peerconnector.h>
#include <mse/encryptedpacketsocket.h>

namespace KNetwork
{
	class KResolverResults;
}

namespace bt
{
	class PeerID;
	class Piece;
	class Torrent;
	class Authenticate;
	class ChunkCounter;
	class PieceDownloader;
	class ConnectionLimit;

	using KNetwork::KResolverResults;

	const Uint32 MAX_SIMULTANIOUS_AUTHS = 20;

	/// Base class for handling pieces
	class KTORRENT_EXPORT PieceHandler
	{
	public:
		virtual ~PieceHandler() {}

		virtual void pieceReceived(const Piece & p) = 0;
	};

	/**
	 * @author Joris Guisson
	 * @brief Manages all the Peers
	 *
	 * This class manages all Peer objects.
	 * It can also open connections to other peers.
	 */
	class KTORRENT_EXPORT PeerManager : public QObject
	{
		Q_OBJECT
	public:
		/**
		 * Constructor.
		 * @param tor The Torrent
		 */
		PeerManager(Torrent & tor);
		virtual ~PeerManager();

		/// Get the connection limits
		static ConnectionLimit & connectionLimits();

		/**
		 * Check for new connections, update down and upload speed of each Peer.
		 * Initiate new connections.
		 */
		void update();

		/**
		 * Pause the peer connections
		 */
		void pause();

		/**
		 * Unpause the peer connections
		 */
		void unpause();

		/**
		 * Get a list of all peers.
		 * @return A QList of Peer's
		 */
		QList<Peer::Ptr> getPeers() const;

		/**
		 * Find a Peer based on it's ID
		 * @param peer_id The ID
		 * @return A Peer or 0, if nothing could be found
		 */
		Peer::Ptr findPeer(Uint32 peer_id);

		/**
		 * Find a Peer based on it's PieceDownloader
		 * @param pd The PieceDownloader
		 * @return The matching Peer or 0 if none can be found
		 */
		Peer::Ptr findPeer(PieceDownloader* pd);

		void setWantedChunks(const BitSet & bs);

		/**
		 * Close all Peer connections.
		 */
		void closeAllConnections();

		/**
		 * Start listening to incoming requests.
		 * @param superseed Set to true to get superseeding
		 */
		void start(bool superseed);

		/**
		 * Stop listening to incoming requests.
		 */
		void stop();

		/**
		 * Kill all peers who appear to be stale
		 */
		void killStalePeers();

		/// Get the number of connected peers
		Uint32 getNumConnectedPeers() const;

		/// Get the number of connected seeders
		Uint32 getNumConnectedSeeders() const;

		/// Get the number of connected leechers
		Uint32 getNumConnectedLeechers() const;

		/// Get the number of pending peers we are attempting to connect to
		Uint32 getNumPending() const;

		/// Is the peer manager started
		bool isStarted() const;

		/// Get the Torrent
		const Torrent & getTorrent() const;

		/// Get the combined upload rate of all peers in bytes per sec
		Uint32 uploadRate() const;

		/**
		 * A new connection is ready for this PeerManager.
		 * @param sock The socket
		 * @param peer_id The Peer's ID
		 * @param support What extensions the peer supports
		 */
		void newConnection(mse::EncryptedPacketSocket::Ptr sock, const PeerID & peer_id, Uint32 support);

		/**
		 * Add a potential peer
		 * @param addr The peers' address
		 * @param local Is it a peer on the local network
		 * @return void
		 **/
		void addPotentialPeer(const net::Address & addr, bool local);

		/**
		 * Kills all connections to seeders.
		 * This is used when torrent download gets finished
		 * and we should drop all connections to seeders
		 */
		void killSeeders();

		/**
		 * Kills all peers that are not interested for a long time.
		 * This should be used when torrent is seeding ONLY.
		 */
		void killUninterested();

		/// Get a BitSet of all available chunks
		const BitSet & getAvailableChunksBitSet() const;

		/// Get the chunk counter.
		ChunkCounter & getChunkCounter();

		/// Are we connected to a Peer given it's PeerID ?
		bool connectedTo(const PeerID & peer_id);

		/**
		 * A peer has authenticated.
		 * @param auth The Authenticate object
		 * @param pcon The PeerConnector
		 * @param ok Whether or not the attempt was succesfull
		 * @param token The ConnectionLimit::Token
		 */
		void peerAuthenticated(Authenticate* auth, PeerConnector::WPtr pcon, bool ok, ConnectionLimit::Token::Ptr token);

		/**
		 * Save the IP's and port numbers of all peers.
		 */
		void savePeerList(const QString & file);

		/**
		 * Load the peer list again and add them to the potential peers
		 */
		void loadPeerList(const QString & file);

		class PeerVisitor
		{
		public:
			virtual ~PeerVisitor() {}

			/// Called for each Peer
			virtual void visit(const Peer::Ptr p) = 0;
		};

		/// Visit all peers
		void visit(PeerVisitor & visitor);

		/// Is PEX eanbled
		bool isPexEnabled() const;

		/// Enable or disable PEX
		void setPexEnabled(bool on);

		/// Set the group IDs of each peer
		void setGroupIDs(Uint32 up, Uint32 down);

		/// Have message received by a peer
		void have(Peer* p, Uint32 index);

		/// Bitset received by a peer
		void bitSetReceived(Peer* p, const BitSet & bs);

		/// Rerun the choker
		void rerunChoker();

		/// Does the choker need to run again
		bool chokerNeedsToRun() const;

		/// A PEX message was received
		void pex(const QByteArray & arr);

		/// A port packet was received
		void portPacketReceived(const QString & ip, Uint16 port);

		/// A Piece was received
		void pieceReceived(const Piece & p);

		/// Set the piece handler
		void setPieceHandler(PieceHandler* ph);

		/// Enable or disable super seeding
		void setSuperSeeding(bool on, const BitSet & chunks);

		/// Send a have message to all peers
		void sendHave(Uint32 index);

		/// Set if we are a partial seed or not
		void setPartialSeed(bool partial_seed);

		/// Are we a partial seed
		bool isPartialSeed() const;

	public slots:
		/**
		 * A PeerSource, has new potential peers.
		 * @param ps The PeerSource
		 */
		void peerSourceReady(PeerSource* ps);

	signals:
		void newPeer(Peer* p);
		void peerKilled(Peer* p);

	private:
		class Private;
		Private* d;
	};

}

#endif
