// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program 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.
//
//  This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/containers/generic.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_CONTAINERS_GENERIC_H
#define FENNEC_CONTAINERS_GENERIC_H

#include <fennec/memory/allocator.h>

#include <fennec/rtti/type.h>

namespace fennec
{

///
/// \brief A struct capable of holding a single object of any type
/// \details
/// | Property    | Value       |
/// |:-----------:|:-----------:|
/// | stable      |      ✅     |
/// | dynamic     |      ⛔     |
/// | homogeneous |      ⛔     |
/// | distinct    |      ⛔     |
/// | ordered     |      ⛔     |
/// | linear      |      ✅     |
/// | space       | \emph{O(1)} |
/// | access      | \emph{O(1)} |
/// | find        |      ⛔     |
/// | insertion   | \emph{O(1)} |
/// | deletion    | \emph{O(1)} |
struct generic {

// Definitions =========================================================================================================
private:

	// based on GCC
	enum op_ : uint8_t {
		op_clone,
		op_destroy,
		op_type,
	};

	using manager_t = void* (*)(uint8_t, void*);


// Constructors & Destructor ===========================================================================================
public:

	/// \name Constructors & Destructor
	/// @{

	///
	/// \brief Default Constructor
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	generic()
		: _handle(nullptr)
		, _manage(nullptr) {
	}

	///
	/// \brief Copy Constructor
	/// \param gen The generic object to copy
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	generic(const generic& gen)
		: _handle(nullptr)
		, _manage(gen._manage) {
		if (_manage) {
			_handle = _manage(op_clone, gen._handle);
		}
	}

	///
	/// \brief Move Constructor
	/// \param gen The generic object to move
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	generic(generic&& gen)
		: _handle(gen._handle)
		, _manage(gen._manage) {
		gen._handle = nullptr;
		gen._manage = nullptr;
	}

	///
	/// \brief Value Constructor
	/// \tparam T The type of the value
	/// \param x The value
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename T>
	generic(T&& x)
		: _handle(new T(fennec::forward<T>(x)))
		, _manage(_manage_impl<T>) {
	}

	///
	/// \brief Emplace Constructor
	/// \tparam T The type to construct
	/// \tparam ArgsT The argument types
	/// \param args The argument values
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename T, typename...ArgsT>
	generic(type_identity<T>, ArgsT&&...args)
		: _handle(new T(fennec::forward<ArgsT>(args)...))
		, _manage(_manage_impl<T>) {
	}

	///
	/// \brief Destructor
	~generic() {
		reset();
	}

	/// @}


// Properties ==========================================================================================================
public:

	/// \name Properties
	/// @{

	///
	/// \brief runtime type acquisition
	/// \returns a runtime type struct referencing the held type
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	type type() const {
		return *static_cast<fennec::type*>(_manage(op_type, nullptr));
	}

	///
	/// \returns \emph{true} if there is a held value, \emph{false} otherwise
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	bool has_value() const {
		return _handle != nullptr;
	}

	/// @}


// Assignment ==========================================================================================================
public:

	/// \name Assignment Operators
	/// @{

	///
	/// \brief copy assignment
	/// \param gen the generic to copy
	/// \returns a reference to \emph{this} after copying the contents of \emph{gen}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	generic& operator=(const generic& gen) {
		if (this == &gen) { // self-assignment case
			return *this;
		}

		reset();
		_manage = gen._manage;
		_handle = _manage(op_clone, gen._handle);
		return *this;
	}

	///
	/// \brief move assignment
	/// \param gen the generic to move
	/// \returns a reference to \emph{this} after swapping contents with \emph{gen}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	generic& operator=(generic&& gen) noexcept {
		swap(gen);
		return *this;
	}

	///
	/// \brief value assignment
	/// \tparam T the type of the value
	/// \param x the value to assign
	/// \returns a reference to \emph{this} after having assigned \emph{x}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename T>
	generic& operator=(T&& x) {
		reset();
		_handle = new T(fennec::forward<T>(x));
		_manage = _manage_impl<T>();
		return *this;
	}

	/// @}


// Modifiers ===========================================================================================================
public:

	/// \name Modifiers
	/// @{

	///
	/// \brief emplace value
	///
	/// \details constructs a new value of type \emph{T} using \emph{args\ldots}
	/// \tparam T the type to construct
	/// \tparam ArgsT the argument types
	/// \param args the argument values
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename T, typename...ArgsT>
	void emplace(ArgsT&&...args) {
		reset();
		_handle = new T(fennec::forward<ArgsT>(args)...);
		_manage = _manage_impl<T>;
	}

	///
	/// \brief reset value
	/// \details clears the held value using the appropriate destructor
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	void reset() {
		if (_manage) {
			_handle = _manage(op_destroy, _handle);
			_manage = nullptr;
		}
	}

	///
	/// \brief C++ 11 Swap Specification
	/// \param gen the generic to swap with
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	void swap(generic& gen) noexcept {
		fennec::swap(_handle, gen._handle);
		fennec::swap(_manage, gen._manage);
	}

	/// @}


// Casting =============================================================================================================
public:

	/// \name Casting
	/// @{

	///
	/// \brief cast value
	///
	/// \details equivalent to \emph{reinterpret_cast}
	/// \tparam T The type to cast to
	/// \returns The contents of generic after having cast to \emph{T}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename T, typename U = remove_cvref_t<T>>
	T cast() {
		return static_cast<T>(*static_cast<U*>(_handle));
	}

	///
	/// \details equivalent to \emph{reinterpret_cast}
	/// \tparam T The type to cast to
	/// \returns The contents of generic after having cast to \emph{T}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename T, typename U = remove_cvref_t<T>>
	T cast() const {
		return static_cast<T>(*static_cast<U*>(_handle));
	}

	/// @}


// Private Member Variables ============================================================================================
private:
	void*     _handle;
	manager_t _manage;


// Private Helpers =====================================================================================================
private:
	template<typename T>
	static void* _manage_impl(uint8_t op, void* hnd) {
		static fennec::type t = type::get<T>();
		T* ptr = hnd;

		switch (op) {
			case op_clone:
				return new T(*ptr);
			case op_destroy:
				delete ptr;
				return nullptr;
			case op_type:
				return &t;
			default:
				return nullptr;
		}
	}
};

}

#endif // FENNEC_CONTAINERS_GENERIC_H