/*
 * 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 "Math/Math.hpp"
#include "Base/PlatformEnums.hpp"
#include "Base/ICloseable.hpp"
#include <string>
#include <stdexcept>

namespace OpenVulkano
{
	enum WindowMode
	{
		WINDOWED = 0,
		BORDERLESS = 1,
		FULLSCREEN = 2,
		BORDERLESS_FULLSCREEN = 3
	};

	class IWindowHandler;
	class IVulkanWindow;
	class IOpenGlWindow;

	struct WindowConfiguration
	{
		Math::Vector2ui size{1280, 720};
		Math::Vector2i position{0, 0};
		std::string title = "Window Title";
		WindowMode windowMode = WINDOWED;
	};

	class IWindow : public ICloseable
	{
	public:
		virtual ~IWindow() = default;

		virtual void Init(RenderAPI::RenderApi renderApi) = 0;

		virtual bool WindowHasBeenDestroyed() const = 0;
		virtual void SetWindowHasBeenDestroyed() = 0;

		virtual bool HasTitle() = 0;
		virtual const std::string& GetTitle() = 0;
		virtual void SetTitle(const std::string& title) = 0;

		virtual WindowMode GetWindowMode() = 0;
		virtual void SetWindowMode(WindowMode) = 0;
		inline void SetFullscreen() { SetWindowMode(FULLSCREEN); }
		inline void SetWindowed() { SetWindowMode(WINDOWED); }

		virtual const WindowConfiguration& GetWindowConfiguration() = 0;
		inline uint32_t GetWidth() { return GetSize().x; }
		inline uint32_t GetHeight() { return GetSize().y; }
		virtual Math::Vector2ui GetSize() = 0;
		virtual void SetSize(uint32_t width, uint32_t height) = 0;
		inline void SetSize(Math::Vector2ui size) { SetSize(size.x, size.y); }
		virtual void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) = 0;

		virtual Math::Vector2i GetPosition() = 0;
		virtual void SetPosition(int posX, int posY) = 0;
		inline void SetPosition(Math::Vector2i pos) { SetPosition(pos.x, pos.y); };

		virtual void SetMouseVisibility(bool hideMouse) {};

		virtual void Show() = 0;
		virtual void Hide() = 0;
		virtual void Show(bool show) = 0;

		virtual IWindowHandler* GetWindowHandler() = 0;
		virtual void SetWindowHandler(IWindowHandler* handler) = 0;

		/**
		 * \brief Gets the vulkan window implementation of the window.
		 * \return The IVulkanWindow reference of the window. nullptr if the current Window dose not implement IVulkanWindow
		 */
		virtual IVulkanWindow* GetVulkanWindow() = 0;
		virtual IOpenGlWindow* GetOpenGlWindow() = 0;

		[[nodiscard]] virtual uint32_t GetWindowId() const = 0;

		virtual void* GetNativeWindowHandle() = 0;
		
		virtual float GetContentScale() const { return 1; }
		virtual float GetInterfaceOrientation() const { return 0; }
	protected:
		static uint32_t CreateWindowId()
		{
			static uint32_t id = 0;
			return id++;
		}
	};

	class IWindowHandler
	{
	public:
		virtual ~IWindowHandler() = default;

		virtual void OnWindowMinimize(IWindow* window) = 0;
		virtual void OnWindowRestore(IWindow* window) = 0;
		virtual void OnWindowFocusLost(IWindow* window) = 0;
		virtual void OnWindowFocusGained(IWindow* window) = 0;
		virtual void OnWindowMove(IWindow* window, int posX, int posY) = 0;
		virtual void OnWindowResize(IWindow* window, uint32_t newWidth, uint32_t newHeight) = 0;
		virtual void OnWindowClose(IWindow* window) = 0;
	};

	class WindowInitFailedException : public std::runtime_error
	{
	public:
		WindowInitFailedException(char const* const message) : runtime_error(message) {}
	};
}
