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

namespace std
{
#if __cpp_impl_coroutine
	namespace detail
	{
		template <class, class...>
		struct coroutine_traits_base
		{
		};

		template <class R, class... Args>
			requires requires { typename R::promise_type; }
		struct coroutine_traits_base<R, Args...>
		{
			using promise_type = R::promise_type;
		};
	}

	template <class R, class... Args>
	struct coroutine_traits : detail::coroutine_traits_base<R, Args...>
	{
	};

	template <class Promise = void>
	struct coroutine_handle;

	struct noop_coroutine_promise;
	template <>
	struct coroutine_handle<noop_coroutine_promise>;
	using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
	noop_coroutine_handle noop_coroutine() noexcept;

	template <class T>
	struct hash;
	template <class P>
	struct hash<coroutine_handle<P>>;

	template <>
	struct coroutine_handle<void>
	{
	private:
		void *ptr;

	public:
		constexpr coroutine_handle() noexcept : ptr(nullptr) {}
		constexpr coroutine_handle(nullptr_t __nul) noexcept : ptr(__nul) {}

		coroutine_handle &operator=(nullptr_t) noexcept
		{
			ptr = nullptr;
			return *this;
		}

		constexpr void *address() const noexcept { return ptr; }

		static constexpr coroutine_handle from_address(void *addr)
		{
			coroutine_handle temp;
			temp.ptr = addr;
			return temp;
		}

		constexpr explicit operator bool() const noexcept { return bool(ptr); }
		bool done() const { return __builtin_coro_done(ptr); }
		void operator()() const { resume(); }
		void resume() const { __builtin_coro_resume(ptr); }
		void destroy() const { __builtin_coro_destroy(ptr); }
	};

	constexpr bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept
	{
		return x.address() == y.address();
	}

	// constexpr strong_ordering operator<=>(coroutine_handle<> x, coroutine_handle<> y) noexcept;

	template <class Promise>
	struct coroutine_handle
	{
	private:
		void *ptr;

	public:
		constexpr coroutine_handle() noexcept : ptr(nullptr) {}
		constexpr coroutine_handle(nullptr_t) noexcept : ptr(nullptr) {}

		static coroutine_handle from_promise(Promise &promise)
		{
			coroutine_handle temp;
			temp.ptr = __builtin_coro_promise((char *)&promise, __alignof(Promise), true);
			return temp;
		}

		coroutine_handle &operator=(nullptr_t) noexcept
		{
			ptr = nullptr;
			return *this;
		}

		constexpr void *address() const noexcept { return ptr; }

		static constexpr coroutine_handle from_address(void *addr)
		{
			coroutine_handle temp;
			temp.ptr = addr;
			return temp;
		}

		constexpr operator coroutine_handle<>() const noexcept
		{
			return coroutine_handle<>::from_address(address());
		}

		constexpr explicit operator bool() const noexcept { return bool(ptr); }
		bool done() const { return __builtin_coro_done(ptr); }
		void operator()() const { resume(); }
		void resume() const { __builtin_coro_resume(ptr); }
		void destroy() const { __builtin_coro_destroy(ptr); }
		Promise &promise() const { return *static_cast<Promise *>(ptr); }
	};

	struct noop_coroutine_promise
	{
	};

	template <>
	struct coroutine_handle<noop_coroutine_promise>
	{
	private:
		static struct __chframe
		{
			static void __stub_resume() {}
			void (*__a)() = __stub_resume;
			void (*__b)() = __stub_resume;
			struct noop_coroutine_promise __c;
		} __frame;
		void *ptr = &__frame;

		explicit coroutine_handle() noexcept = default;
		friend coroutine_handle noop_coroutine() noexcept;

	public:
		constexpr operator coroutine_handle<>() const noexcept { return coroutine_handle<>::from_address(address()); }
		constexpr explicit operator bool() const noexcept { return true; }
		constexpr bool done() const noexcept { return false; }
		constexpr void operator()() const noexcept {}
		constexpr void resume() const noexcept {}
		constexpr void destroy() const noexcept {}
		noop_coroutine_promise &promise() const noexcept { return __frame.__c; }
		constexpr void *address() const noexcept { return ptr; }
	};

	inline noop_coroutine_handle::__chframe noop_coroutine_handle::__frame{};
	noop_coroutine_handle noop_coroutine() noexcept { return noop_coroutine_handle(); }

	struct suspend_never
	{
		constexpr bool await_ready() const noexcept { return true; }
		constexpr void await_suspend(coroutine_handle<>) const noexcept {}
		constexpr void await_resume() const noexcept {}
	};

	struct suspend_always
	{
		constexpr bool await_ready() const noexcept { return false; }
		constexpr void await_suspend(coroutine_handle<>) const noexcept {}
		constexpr void await_resume() const noexcept {}
	};
#else
#error "kernel requires -fcoroutines"
#endif
}
