/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#pragma once

#include "Base/UI/BaseWindow.hpp"
#include "Base/UI/IVulkanWindow.hpp"
#include "Base/UI/IOpenGlWindow.hpp"
#include "Base/IPlatform.hpp"
#include "InputProviderGLFW.hpp"

struct GLFWmonitor;
struct GLFWwindow;

namespace OpenVulkano::GLFW
{
	class WindowGLFW final : virtual public BaseWindow, virtual public IVulkanWindow, virtual public IOpenGlWindow
	{
	private:
		InputProviderGLFW& inputProvider;
		GLFWwindow* window = nullptr;
		IWindowHandler* handler = nullptr;
		Math::Vector2ui currentSize = {};

	public:
		WindowGLFW(InputProviderGLFW& inputProvider);

		~WindowGLFW() noexcept override;

	protected:
		[[nodiscard]] GLFWmonitor* GetCurrentMonitor() const;

		[[nodiscard]] GLFWmonitor* GetTargetMonitor() const;

		void Create();

		void RegisterCallbacks() const;

		static GLFWmonitor* GetPrimaryMonitor();

		static std::vector<GLFWmonitor*> GetMonitors();

	public: // IWindow implementation
		void Init(RenderAPI::RenderApi renderApi) override;

		void Close() override;

		void Present() const override;

		void Show() override;

		void Hide() override;

		void SetMouseVisibility(bool hideMouse) override;

		void SetTitle(const std::string& title) override;

		void SetSize(uint32_t width, uint32_t height) override;

		void SetPosition(int posX, int posY) override;

		void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) override;

		void MakeCurrentThread() override;

		void SetWindowMode(WindowMode windowMode) override;

		void SetWindowHandler(IWindowHandler* handler) override;

		IVulkanWindow* GetVulkanWindow() override { return this; };

		IOpenGlWindow* GetOpenGlWindow() override { return this; };

		// Status getter
		Math::Vector2ui GetSize() override;

		Math::Vector2i GetPosition() override;

		IWindowHandler* GetWindowHandler() override { return handler; }

		//IVulkanWindow stuff
		vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const vk::AllocationCallbacks* pAllocator) override;

		std::vector<std::string> GetRequiredInstanceExtensions() override;

		void* GetNativeWindowHandle() override { return window; }

	public: // Window events
		void OnResize(uint32_t newWidth, uint32_t newHeight);

		void OnMinimize();

		void OnRestore();

		void OnFocusLost();

		void OnFocusGained();

		void OnMove(int posX, int posY);

		void OnClose();

	protected:
		virtual void OnKeyEvent(int key, int scanCode, int action, int mods);

	private: // Callbacks
		static WindowGLFW* GetWindow(GLFWwindow* window);

		static void KeyboardCallback(GLFWwindow* window, int key, int scanCode, int action, int mods);

		static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);

		static void MouseMoveCallback(GLFWwindow* window, double posX, double posY);

		static void MouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset);

		static void MouseEnterExitCallback(GLFWwindow* window, int entered);

		static void ResizeCallback(GLFWwindow* window, int width, int height);

		static void FocusCallback(GLFWwindow* window, int focused);

		static void MinimizeCallback(GLFWwindow* window, int minimized);

		static void RefreshCallback(GLFWwindow* window);

		static void WindowMoveCallback(GLFWwindow* window, int posX, int posY);

		static void CloseCallback(GLFWwindow* window);

		static void DropCallback(GLFWwindow* window, int count, const char** paths);

	public:
		static std::vector<std::string> GetVulkanRequiredInstanceExtensions();
	};
}
