/*
	This file is part of Fennix Kernel.

	Fennix Kernel 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 3 of
	the License, or (at your option) any later version.

	Fennix Kernel 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 Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include <types.h>
#include <algorithm>
#include <typeinfo>

namespace std
{
	template <typename T = void>
	struct equal_to
	{
		bool operator()(const T &lhs, const T &rhs) const
		{
			return lhs == rhs;
		}
	};

	template <typename Key>
	struct hash
	{
		size_t operator()(const Key &key) const
		{
#if defined(a64)
			static_assert(sizeof(uintptr_t) == sizeof(uint64_t));
			const uint64_t FNV_OFFSET_BASIS = 14695981039346656037ull;
			const uint64_t FNV_PRIME = 1099511628211ull;
#elif defined(a32)
			static_assert(sizeof(uintptr_t) == sizeof(uint32_t));
			const uint32_t FNV_OFFSET_BASIS = 2166136261u;
			const uint32_t FNV_PRIME = 16777619u;
#else
#error "Unsupported architecture"
#endif

			const uint8_t *data = reinterpret_cast<const uint8_t *>(&key);
			const size_t size = sizeof(Key);
			uintptr_t hash = FNV_OFFSET_BASIS;

			for (size_t i = 0; i < size; ++i)
			{
				hash ^= static_cast<uintptr_t>(data[i]);
				hash *= FNV_PRIME;
			}

			return static_cast<size_t>(hash);
		}
	};

	template <class T>
	class reference_wrapper;

	template <class>
	class function; /* undefined */

	template <class R, class... Args>
	class function<R(Args...)>
	{
	private:
		class impl_base
		{
		public:
			virtual ~impl_base() = default;
			virtual R invoke(Args...) const = 0;
#ifdef __GXX_RTTI
			virtual const std::type_info &target_type() const noexcept = 0;
#endif
			virtual impl_base *clone() const = 0;
		};

		template <class F>
		class impl : public impl_base
		{
		public:
			F _f;

			template <class G>
			impl(G &&f)
				: _f(std::forward<G>(f))
			{
			}

			R invoke(Args... args) const override
			{
				return _f(std::forward<Args>(args)...);
			}

#ifdef __GXX_RTTI
			const std::type_info &target_type() const noexcept override
			{
				return typeid(F);
			}
#endif

			impl_base *clone() const override
			{
				return new impl<F>(_f);
			}
		};

		impl_base *_ptr;

	public:
		using result_type = R;

		function() noexcept
			: _ptr(nullptr)
		{
		}

		function(std::nullptr_t) noexcept
			: _ptr(nullptr)
		{
		}

		function(const function &other)
			: _ptr(other._ptr)
		{
		}

		function(function &&other) noexcept
			: _ptr(other._ptr)
		{
			other._ptr = nullptr;
		}

		template <class F>
		function(F &&f)
			: _ptr(new impl<F>(std::forward<F>(f)))
		{
		}

		~function()
		{
			delete _ptr;
		}

		function &operator=(const function &other)
		{
			if (this != &other)
			{
				delete _ptr;
				_ptr = other._ptr ? other._ptr->clone() : nullptr;
			}
			return *this;
		}

		function &operator=(function &&other)
		{
			if (this != &other)
			{
				delete _ptr;
				_ptr = other._ptr;
				other._ptr = nullptr;
			}
			return *this;
		}

		function &operator=(std::nullptr_t) noexcept
		{
			delete _ptr;
			_ptr = nullptr;
			return *this;
		}

		template <class F>
		function &operator=(F &&f)
		{
			delete _ptr;
			_ptr = new impl<F>(std::forward<F>(f));
			return *this;
		}

		template <class F>
		function &operator=(std::reference_wrapper<F> f) noexcept
		{
			delete _ptr;
			_ptr = new impl<std::reference_wrapper<F>>(f);
			return *this;
		}

		void swap(function &other) noexcept
		{
			std::swap(_ptr, other._ptr);
		}

		explicit operator bool() const noexcept
		{
			return _ptr != nullptr;
		}

		R operator()(Args... args) const
		{
			return _ptr->invoke(std::forward<Args>(args)...);
		}

#ifdef __GXX_RTTI
		const std::type_info &target_type() const noexcept
		{
			return _ptr ? _ptr->target_type() : typeid(void);
		}

		template <class T>
		T *target() noexcept
		{
			return _ptr && _ptr->target_type() == typeid(T) ? &static_cast<impl<T> *>(_ptr)->_f : nullptr;
		}

		template <class T>
		const T *target() const noexcept
		{
			return _ptr && _ptr->target_type() == typeid(T) ? &static_cast<impl<T> *>(_ptr)->_f : nullptr;
		}
#endif
	};

	template <class R, class... Args>
	void swap(std::function<R(Args...)> &lhs, std::function<R(Args...)> &rhs) noexcept
	{
		lhs.swap(rhs);
	}

	template <class R, class... ArgTypes>

	bool operator==(const std::function<R(ArgTypes...)> &f, std::nullptr_t) noexcept
	{
		return !f;
	}

	template <class T = void>
	struct less
	{
		constexpr bool operator()(const T &lhs, const T &rhs) const
		{
			return lhs < rhs;
		}
	};
}