/*

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

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#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 <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
		int priority[2];

		// 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
