////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2023 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "WindowBase.hpp"
#include "WindowImpl.hpp"
#include "Err.hpp"


namespace {
	// A nested named namespace is used here to allow unity builds of SFML.
	namespace WindowsBaseImpl {
		const sf::WindowBase* fullscreenWindow = NULL;
	}
}


namespace sf {
	////////////////////////////////////////////////////////////
	WindowBase::WindowBase() :
		m_impl(NULL),
		m_size(0, 0) {

	}


	////////////////////////////////////////////////////////////
	WindowBase::WindowBase(VideoMode mode, const String& title, Uint32 style) :
		m_impl(NULL),
		m_size(0, 0) {
		WindowBase::create(mode, title, style);
	}


	////////////////////////////////////////////////////////////
	WindowBase::WindowBase(WindowHandle handle) :
		m_impl(NULL),
		m_size(0, 0) {
		WindowBase::create(handle);
	}


	////////////////////////////////////////////////////////////
	WindowBase::~WindowBase() {
		close();
	}


	////////////////////////////////////////////////////////////
	void WindowBase::create(VideoMode mode, const String& title, Uint32 style) {
		// Destroy the previous window implementation
		close();

		// Fullscreen style requires some tests
		if (style & Style::Fullscreen) {
			// Make sure there's not already a fullscreen window (only one is allowed)
			if (getFullscreenWindow()) {
				err() << "Creating two fullscreen windows is not allowed, switching to windowed mode" << std::endl;
				style &= ~static_cast<Uint32>(Style::Fullscreen);
			}
			else {
				// Make sure that the chosen video mode is compatible
				if (!mode.isValid()) {
					err() << "The requested video mode is not available, switching to a valid mode" << std::endl;
					mode = VideoMode::getFullscreenModes()[0];
				}

				// Update the fullscreen window
				setFullscreenWindow(this);
			}
		}

		// Check validity of style according to the underlying platform
#if defined(SFML_SYSTEM_IOS) || defined(SFML_SYSTEM_ANDROID)
		if (style & Style::Fullscreen)
			style &= ~static_cast<Uint32>(Style::Titlebar);
		else
			style |= Style::Titlebar;
#else
		if ((style & Style::Close) || (style & Style::Resize))
			style |= Style::Titlebar;
#endif

		// Recreate the window implementation
		
		m_impl = priv::WindowImpl::create(mode, title, style);
		// Perform common initializations
		initialize();
	}


	////////////////////////////////////////////////////////////
	void WindowBase::create(WindowHandle handle) {
		// Destroy the previous window implementation
		close();

		// Recreate the window implementation
		m_impl = priv::WindowImpl::create(handle);

		// Perform common initializations
		initialize();
	}


	////////////////////////////////////////////////////////////
	void WindowBase::close() {
		// Delete the window implementation
		delete m_impl;
		m_impl = NULL;

		// Update the fullscreen window
		if (this == getFullscreenWindow())
			setFullscreenWindow(NULL);
	}


	////////////////////////////////////////////////////////////
	bool WindowBase::isOpen() const {
		return m_impl != NULL;
	}


	////////////////////////////////////////////////////////////
	bool WindowBase::pollEvent(Event& event) {
		if (m_impl && m_impl->popEvent(event, false)) {
			return filterEvent(event);
		}
		else {
			return false;
		}
	}


	////////////////////////////////////////////////////////////
	bool WindowBase::waitEvent(Event& event) {
		if (m_impl && m_impl->popEvent(event, true)) {
			return filterEvent(event);
		}
		else {
			return false;
		}
	}


	////////////////////////////////////////////////////////////
	Vector2i WindowBase::getPosition() const {
		return m_impl ? m_impl->getPosition() : Vector2i();
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setPosition(const Vector2i& position) {
		if (m_impl)
			m_impl->setPosition(position);
	}


	////////////////////////////////////////////////////////////
	Vector2u WindowBase::getSize() const {
		return m_size;
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setSize(const Vector2u& size) {
		if (m_impl) {
			m_impl->setSize(size);

			// Cache the new size
			m_size.x = size.x;
			m_size.y = size.y;

			// Notify the derived class
			onResize();
		}
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setTitle(const String& title) {
		if (m_impl)
			m_impl->setTitle(title);
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setIcon(unsigned int width, unsigned int height, const Uint8* pixels) {
		if (m_impl)
			m_impl->setIcon(width, height, pixels);
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setVisible(bool visible) {
		if (m_impl)
			m_impl->setVisible(visible);
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setMouseCursorVisible(bool visible) {
		if (m_impl)
			m_impl->setMouseCursorVisible(visible);
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setMouseCursorGrabbed(bool grabbed) {
		if (m_impl)
			m_impl->setMouseCursorGrabbed(grabbed);
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setMouseCursor(const Cursor& cursor) {
		if (m_impl)
			m_impl->setMouseCursor(cursor.getImpl());
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setKeyRepeatEnabled(bool enabled) {
		if (m_impl)
			m_impl->setKeyRepeatEnabled(enabled);
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setJoystickThreshold(float threshold) {
		if (m_impl)
			m_impl->setJoystickThreshold(threshold);
	}


	////////////////////////////////////////////////////////////
	void WindowBase::requestFocus() {
		if (m_impl)
			m_impl->requestFocus();
	}


	////////////////////////////////////////////////////////////
	bool WindowBase::hasFocus() const {
		return m_impl && m_impl->hasFocus();
	}


	////////////////////////////////////////////////////////////
	WindowHandle WindowBase::getSystemHandle() const {
		return m_impl ? m_impl->getSystemHandle() : 0;
	}


	//////////////////////////////////////////////////////////////
	//bool WindowBase::createVulkanSurface(const VkInstance& instance, VkSurfaceKHR& surface, const VkAllocationCallbacks* allocator) {
	//	return m_impl ? m_impl->createVulkanSurface(instance, surface, allocator) : false;
	//}


	////////////////////////////////////////////////////////////
	void WindowBase::onCreate() {
		// Nothing by default
	}


	////////////////////////////////////////////////////////////
	void WindowBase::onResize() {
		// Nothing by default
	}


	////////////////////////////////////////////////////////////
	bool WindowBase::filterEvent(const Event& event) {
		// Notify resize events to the derived class
		if (event.type == Event::Resized) {
			// Cache the new size
			m_size.x = event.size.width;
			m_size.y = event.size.height;

			// Notify the derived class
			onResize();
		}

		return true;
	}


	////////////////////////////////////////////////////////////
	void WindowBase::initialize() {
		// Setup default behaviors (to get a consistent behavior across different implementations)
		setMouseCursorVisible(true);
		setKeyRepeatEnabled(true);

		// Get and cache the initial size of the window
		m_size = m_impl->getSize();

		// Notify the derived class
		onCreate();
	}


	////////////////////////////////////////////////////////////
	const WindowBase* WindowBase::getFullscreenWindow() {
		return WindowsBaseImpl::fullscreenWindow;
	}


	////////////////////////////////////////////////////////////
	void WindowBase::setFullscreenWindow(const WindowBase* window) {
		WindowsBaseImpl::fullscreenWindow = window;
	}

} // namespace sf