/*

Copyright (c) 2014-2018, 2020-2021, Arvid Norberg
Copyright (c) 2016, 2020, Alden Torres
All rights reserved.

You may use, distribute and modify this code under the terms of the BSD license,
see LICENSE file.
*/

#ifndef TORRENT_PEER_CLASS_HPP_INCLUDED
#define TORRENT_PEER_CLASS_HPP_INCLUDED

#include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/aux_/bandwidth_limit.hpp"
#include "libtorrent/units.hpp"
#include "libtorrent/aux_/deque.hpp"

#include <vector>
#include <array>
#include <string>
#include <cstdint>
#include <memory>

namespace libtorrent {

	using peer_class_t = aux::strong_typedef<std::uint32_t, struct peer_class_tag>;

	// holds settings for a peer class. Used in set_peer_class() and
	// get_peer_class() calls.
	struct TORRENT_EXPORT peer_class_info
	{
		// ``ignore_unchoke_slots`` determines whether peers should always
		// unchoke a peer, regardless of the choking algorithm, or if it should
		// honor the unchoke slot limits. It's used for local peers by default.
		// If *any* of the peer classes a peer belongs to has this set to true,
		// that peer will be unchoked at all times.
		bool ignore_unchoke_slots;

		// adjusts the connection limit (global and per torrent) that applies to
		// this peer class. By default, local peers are allowed to exceed the
		// normal connection limit for instance. This is specified as a percent
		// factor. 100 makes the peer class apply normally to the limit. 200
		// means as long as there are fewer connections than twice the limit, we
		// accept this peer. This factor applies both to the global connection
		// limit and the per-torrent limit. Note that if not used carefully one
		// peer class can potentially completely starve out all other over time.
		int connection_limit_factor;

		// not used by libtorrent. It's intended as a potentially user-facing
		// identifier of this peer class.
		std::string label;

		// transfer rates limits for the whole peer class. They are specified in
		// bytes per second and apply to the sum of all peers that are members of
		// this class.
		int upload_limit;
		int download_limit;

		// relative priorities used by the bandwidth allocator in the rate
		// limiter. If no rate limits are in use, the priority is not used
		// either. Priorities start at 1 (0 is not a valid priority) and may not
		// exceed 255.
		int upload_priority;
		int download_priority;
	};

	struct TORRENT_EXTRA_EXPORT peer_class
	{
		friend struct peer_class_pool;

		explicit peer_class(std::string l)
			: ignore_unchoke_slots(false)
			, connection_limit_factor(100)
			, label(std::move(l))
			, in_use(true)
			, references(1)
		{
			priority[0] = 1;
			priority[1] = 1;
		}

		void clear()
		{
			in_use = false;
			label.clear();
		}

		void set_info(peer_class_info const* pci);
		void get_info(peer_class_info* pci) const;

		void set_upload_limit(int limit);
		void set_download_limit(int limit);

		// the bandwidth channels, upload and download
		// keeps track of the current quotas
		aux::bandwidth_channel channel[2];

		bool ignore_unchoke_slots;
		int connection_limit_factor;

		// priority for bandwidth allocation
		// in rate limiter. One for upload and one
		// for download
		std::array<int, 2> priority;

		// the name of this peer class
		std::string label;

	private:
		// this is set to false when this slot is not in use for a peer_class
		bool in_use;

		int references;
	};

	struct TORRENT_EXTRA_EXPORT peer_class_pool
	{
		peer_class_t new_peer_class(std::string label);
		void decref(peer_class_t c);
		void incref(peer_class_t c);
		peer_class* at(peer_class_t c);
		peer_class const* at(peer_class_t c) const;

	private:

		// state for peer classes (a peer can belong to multiple classes)
		// this can control
		aux::deque<peer_class, peer_class_t> m_peer_classes;

		// indices in m_peer_classes that are no longer used
		std::vector<peer_class_t> m_free_list;
	};
}

#endif // TORRENT_PEER_CLASS_HPP_INCLUDED
