/*
 * 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 "Touch.hpp"
#include "GestureProcessor.hpp"
#include "Input/InputDevice.hpp"
#include "Base/Event.hpp"
#include "GestureInfos.hpp"
#include <mutex>
#include <vector>

namespace OpenVulkano::Input
{
	class InputDeviceTouch final : public InputDevice
	{
		GestureProcessor m_gestureProcessor;
		std::vector<Touch> m_touches;
		TouchId m_nextId = 0;
		bool m_multiTouch = false;
		uint8_t m_pressedButtons = 0, m_lastPressedButtons = 0, m_toBePressedButtons = 0;
		float m_axes[InputKey::Touch::Axis::AXIS_LAST + 1] = { 0 };
		Math::Vector2f m_lastTap{}, m_nextTap{};
		PinchInfo m_lastPinchInfo, m_nextPinchInfo;
		PanInfo m_lastPanInfo, m_nextPanInfo, m_last2FPanInfo, m_next2FPanInfo;

		decltype(m_touches)::iterator FindTouch(TouchId id);

		[[nodiscard]] bool GetLastButton(InputKey::Touch::Button button) const
		{
			return m_lastPressedButtons & (1 << button);
		}

	public:
		InputDeviceTouch() = default;

		void Init();

		void Tick() final;

		TouchId AddTouch(const Math::Vector2f& position);
		void TouchDown(TouchId id);
		void TouchUp(TouchId id);
		void TouchMoved(TouchId id, const Math::Vector2f& position);
		void RemoveTouch(TouchId id);

		void UpdatePinchStarted(const Gesture* pinch, const PinchInfo &info);
		void UpdatePinchMoved(const Gesture* pinch, const PinchInfo &info);
		void UpdatePanStarted(const Gesture* pan, const PanInfo &info);
		void UpdatePanMoved(const Gesture* pan, const PanInfo &info);
		void UpdatePanTwoFingersStarted(const Gesture* pan, const PanInfo &info);
		void UpdatePanTwoFingersMoved(const Gesture* pan, const PanInfo &info);
		void UpdateTap(const Gesture* tap, const TapInfo &info);

		[[nodiscard]] float ReadAxis(int16_t key) const override {return GetAxis( static_cast<InputKey::Touch::Axis>(key)); }
		[[nodiscard]] bool ReadButton(int16_t key) const override { return GetButton(static_cast<InputKey::Touch::Button>(key)); }
		[[nodiscard]] bool ReadButtonUp(int16_t key) const override { return GetButtonUp(static_cast<InputKey::Touch::Button>(key)); }
		[[nodiscard]] bool ReadButtonDown(int16_t key) const override { return GetButtonDown(static_cast<InputKey::Touch::Button>(key)); }

		[[nodiscard]] const Math::Vector2f& GetTapPosition() const { return m_nextTap; }

		[[nodiscard]] float GetAxis(const InputKey::Touch::Axis axis) const { return m_axes[axis]; }

		[[nodiscard]] bool GetButton(const InputKey::Touch::Button button) const
		{
			return m_pressedButtons & (1 << button);
		}

		[[nodiscard]] bool GetButtonUp(const InputKey::Touch::Button button) const
		{
			return !GetButton(button) && GetLastButton(button);
		}

		[[nodiscard]] bool GetButtonDown(const InputKey::Touch::Button button) const
		{
			return GetButton(button) && !GetLastButton(button);
		}

		[[nodiscard]] bool HasTouch() const { return !m_touches.empty(); }
		[[nodiscard]] bool IsMultiTouch() const { return m_multiTouch; }

		Event<Touch&> OnTouchAdded, OnTouchDown, OnTouchUp, OnTouchMoved, OnTouchRemoved;
	};
}
