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

#ifndef FENNEC_PLATFORM_INTERFACE_PLATFORM_H
#define FENNEC_PLATFORM_INTERFACE_PLATFORM_H

#include <fennec/string/cstring.h>

#include <fennec/platform/window_manager.h>

#include <fennec/rtti/enable.h>
#include <fennec/rtti/singleton.h>
#include <fennec/rtti/detail/_this_t.h>

/*
 * This class should resemble the target platform as a whole. By itself, it will represent the OS, i.e. Linux, Windows,
 * etc.
 *
 * We want everything to be type agnostic from the top level down
 *
 * The general structure is:
 * Platform -> Display Protocol -> GFX Context    -> GFX API
 *                              -> Window         -> GFX Surface
 *                              -> Input Protocol
 *
 * For example, let's say we are on Linux, using Wayland and OpenGL with EGL
 * EGL will know that we are using OpenGL, but won't know we are using Wayland
 * OpenGL won't know or care that Linux, EGL, or Wayland is being used
 * Wayland won't know or care that OpenGL or EGL is being used
 * Linux won't know or care that OpenGL, Wayland, or EGL are in use
 *
 * Why do we want everything to be type agnostic?
 * Let's say someone wants to add a DirectX extension for Windows. They shouldn't have to touch the
 * Windows platform class or Win32 display manager class at all to write the implementation. This
 * allows the extension to remain entirely independent of the engine which allows you to just drop
 * it into your project, and it will work as expected. This should also keep version compatibility
 * issues to the absolute minimum.
 *
 * We can allow this by notifying the platform of all available drivers at startup and give them priorities.
 * Then, the platform will load the highest priority first, only falling back when a driver fails to load or is
 * deemed incompatible for whatever reason.
 *
 *
 */

namespace fennec
{

///
/// \brief Main platform class
class platform : public singleton<platform*> {

// Definitions =========================================================================================================
public:

	/// \name Definitions
	/// @{

	using shared_object = struct shared_object; //!< handle for shared object code
	using symbol        = void*;                //!< handle for a symbol loaded from a shared object

	/// @}


// Constructors & Destructors ==========================================================================================
public:

	/// \name Constructors & Destructors
	/// @{

	///
	/// \brief constructor
	platform();

	///
	/// \brief Copy Constructor
	/// \details Deleted, no semantics for copying a platform
	platform(const platform&) = delete;

	///
	/// \brief destructor
	virtual ~platform() = default;


	/// @}


// Initialization ======================================================================================================
public:

	/// \name Initialization
	/// @{

	virtual void initialize(); //!< Initialize Drivers and Contexts
	virtual void shutdown();   //!< Close Drivers and Contexts

	/// @}


// Shared Objects ======================================================================================================
public:

	/// \name Shared Objects
	/// @{

	///
	/// \brief shared object linker
	/// \param file the name of the shared object to link
	/// \returns a reference to the shared object
	virtual shared_object* load_object(const cstring& file)  = 0;

	///
	/// \brief shared object release
	/// \param obj the shared object to release
	virtual void unload_object(shared_object* obj) = 0;

	///
	/// \brief shared object symbol locator
	/// \param obj the shared object to search
	/// \param name the name of the symbol to search for
	/// \returns a reference to the symbol
	virtual symbol find_symbol(shared_object* obj, const cstring& name) = 0;

	/// @}


// Window Management ===================================================================================================
public:

	/// \name Window Management
	/// @{

	///
	/// \brief Returns the window manager for the platform
	/// \returns Reference to the window manager.
	virtual window_manager& get_window_manager() const {
		return *wmanager;
	}

	/// @}

protected:
	unique_ptr<window_manager> wmanager; //!< the window manager

#ifndef FENNEC_DOXYGEN
	FENNEC_RTTI_CLASS_ENABLE() {
	}
#endif
};

}

#endif // FENNEC_PLATFORM_INTERFACE_PLATFORM_H
