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

#include "GestureProcessor.hpp"
#include "Touch.hpp"
#include "Gesture.hpp"
#include "Base/Logger.hpp"

namespace OpenVulkano::Input
{
	bool Gesture::ResolveConflicts()
	{
		if (m_processor) return m_processor->ResolveConflicts(this);
		return false;
	}

	void Gesture::RemoveFromProcessor()
	{
		if (m_processor) m_processor->RemoveGesture(this);
	}

	GestureProcessor::GestureProcessor()
			: m_gesturePan(1), m_gesturePanTwoFingers(2)
	{}

	GestureProcessor::~GestureProcessor()
	{
		if (!m_gestures.empty()) Close();
	}

	void GestureProcessor::Init()
	{
		AddGesture(&m_gesturePan);
		AddGesture(&m_gesturePanTwoFingers);
		AddGesture(&m_gesturePinch);
		AddGesture(&m_gestureTap);
		m_conflictResolutionDelegate = [this](const Gesture* existing, const Gesture* newGesture) {
			if(existing == &m_gesturePan && newGesture != &m_gestureTap)
			{
				Logger::INPUT->debug("Replacing pan with multitouch gesture");
				return ConflictResult::NewGesture;
			}
			return ConflictResult::BothGestures;
		};
	}

	void GestureProcessor::Close()
	{
		for(const auto& gesture : m_gestures)
		{
			gesture->m_processor = nullptr;
		}
		m_gestures.clear();
		m_conflictResolutionDelegate = nullptr;
	}

	void GestureProcessor::AddGesture(Gesture* gesture)
	{
		gesture->m_processor = this;
		m_gestures.push_back(gesture);
	}

	void GestureProcessor::RemoveGesture(OpenVulkano::Input::Gesture* gesture)
	{
		for (auto it = m_gestures.begin(); it != m_gestures.end(); it++)
		{
			if (*it == gesture)
			{
				(*it)->m_processor = nullptr;
				m_gestures.erase(it);
				break;
			}
		}
	}

	//region forward events
	void GestureProcessor::Reset()
	{
		for (const auto& gesture : m_gestures)
		{
			gesture->Reset();
		}
	}

	void GestureProcessor::TouchDown(const OpenVulkano::Input::Touch& touch)
	{
		for (const auto& gesture : m_gestures)
		{
			gesture->TouchDown(touch);
		}
	}

	void GestureProcessor::TouchUp(const OpenVulkano::Input::Touch& touch)
	{
		for (const auto& gesture : m_gestures)
		{
			gesture->TouchUp(touch);
		}
	}

	void GestureProcessor::TouchMoved(const OpenVulkano::Input::Touch& touch)
	{
		for (const auto& gesture : m_gestures)
		{
			gesture->TouchMoved(touch);
		}
	}
	//endregion

	bool GestureProcessor::ResolveConflicts(Gesture* gesture)
	{
		if (!m_conflictResolutionDelegate) return true;
		bool canActivate = true;
		for (const auto& g : m_gestures)
		{
			if (g != gesture && g->IsActive())
			{
				ConflictResult conflictResult = m_conflictResolutionDelegate(g, gesture);

				switch (conflictResult)
				{
					case ConflictResult::NeitherGesture:
						g->Reset();
						canActivate = false;
						break;
					case ConflictResult::ExistingGesture:
						canActivate = false;
						break;
					case ConflictResult::NewGesture:
						g->Reset();
						break;
					case ConflictResult::BothGestures:
						break;
				}
			}
		}
		return canActivate;
	}
}