/*
 * This file is part of OpenTTD.
 * OpenTTD 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, version 2.
 * OpenTTD 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 OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
 */

/** @file provider_manager.h Definition of the ProviderManager. */

#ifndef PROVIDER_MANAGER_H
#define PROVIDER_MANAGER_H

/**
 * The ProviderManager manages a single Provider-type.
 *
 * It allows for automatic registration and unregistration of providers.
 *
 * @tparam T Service provider type.
 */
template <typename TProviderType>
class ProviderManager {
public:
	/* Avoid copying this object; it is a singleton object. */
	ProviderManager(ProviderManager const &) = delete;

	ProviderManager &operator=(ProviderManager const &) = delete;

	static void Register(const TProviderType &instance)
	{
		/* Insert according to comparator. */
		auto &providers = GetProviders();
		auto it = std::ranges::lower_bound(providers, &instance, typename TProviderType::ProviderSorter());
		providers.insert(it, &instance);
	}

	static void Unregister(const TProviderType &instance)
	{
		auto &providers = GetProviders();
		providers.erase(std::find(std::begin(providers), std::end(providers), &instance));
	}

	/**
	 * Get the currently known providers.
	 * @return The known providers.
	 */
	static std::vector<const TProviderType *> &GetProviders()
	{
		static std::vector<const TProviderType *> providers{};
		return providers;
	}
};

template <typename T>
class BaseProvider {
public:
	/**
	 * Create the provider.
	 * @param name The name of the provider.
	 * @param description A description of the provider.
	 */
	constexpr BaseProvider(std::string_view name, std::string_view description) : name(name), description(description) {}
	/** Ensure the destructor of the sub classes are called as well. */
	virtual ~BaseProvider() = default;

	/**
	 * Get the name of this provider.
	 * @return Our name.
	 */
	inline std::string_view GetName() const { return this->name; }

	/**
	 * Get a description of this provider.
	 * @return The description.
	 */
	inline std::string_view GetDescription() const { return this->description; }

	/**
	 * Sorter for BaseProvider.
	 *
	 * It will sort based on the name. If the name is the
	 * same, it will sort based on the pointer value.
	 */
	struct ProviderSorter {
		bool operator()(const T *a, const T *b) const
		{
			if (a->GetName() == b->GetName()) return a < b;
			return a->GetName() < b->GetName();
		}
	};

protected:
	const std::string_view name; ///< The name of the provider.
	const std::string_view description; ///< A description of the provider.
};

template <typename T>
class PriorityBaseProvider : public BaseProvider<T> {
public:
	/**
	 * Create the provider.
	 * @param name The name of the provider.
	 * @param description A description of the provider.
	 * @param priority The priority number to sort all providers by.
	 */
	constexpr PriorityBaseProvider(std::string_view name, std::string_view description, int priority) : BaseProvider<T>(name, description), priority(priority) {}

	/**
	 * Get the priority of this provider.
	 * @return The priority.
	 */
	inline int GetPriority() const { return this->priority; }

	/**
	 * Sorter for PriorityBaseProvider.
	 *
	 * It will sort based on the priority, smaller first. If the priority is the
	 * same, it will sort based on the pointer value.
	 */
	struct ProviderSorter {
		bool operator()(const T *a, const T *b) const
		{
			if (a->GetPriority() == b->GetPriority()) return a < b;
			return a->GetPriority() < b->GetPriority();
		}
	};

protected:
	const int priority; ///< The priority of this provider.
};

#endif /* PROVIDER_MANAGER_H */
