/*
 * 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 "GestureTap.hpp"

using namespace std::chrono_literals;

namespace OpenVulkano::Input
{
	namespace
	{
		constexpr auto MAX_DURATION = 1s;
		constexpr float MAX_DISTANCE = 12.0f;
		constexpr float MAX_DISTANCE2 = MAX_DISTANCE * MAX_DISTANCE;
	}

	int GestureTap::GetTouchCount() const
	{
		return std::count_if(m_touchMap.begin(), m_touchMap.end(), [](const std::pair<TouchId, bool>& t) { return t.second; });
	}

	void GestureTap::Cancel()
	{
		SetActive(false);
		m_tapInfo.position = {};
	}

	void GestureTap::Reset()
	{
		Cancel();
		m_touchMap.clear();
		m_startTime = {};
	}

	void GestureTap::TouchMoved(const Touch& touch)
	{
		if (IsActive())
		{
			if ((std::chrono::steady_clock::now() - m_startTime > MAX_DURATION) ||
				(Math::Utils::distance2(touch.GetPosition(), m_initialPoint) > MAX_DISTANCE2) ||
				((m_distance += Math::Utils::distance2(touch.GetPosition(), touch.GetLastPosition()) > 3 * MAX_DISTANCE2)))
			{
				Cancel();
			}
		}
		else
		{
			m_tapInfo.position = touch.GetPosition();
		}
	}

	void GestureTap::TouchDown(const Touch& touch)
	{
		m_touchMap[touch.GetId()] = true;
		if (1 == m_touchMap.size()) // single tap only
		{
			m_startTime = std::chrono::steady_clock::now();
			m_initialPoint = touch.GetPosition();
			m_distance = 0.0f;
			m_tapInfo.position = m_initialPoint;
			SetActive(true);
		}
		else
		{
			Cancel();
		}
	}

	void GestureTap::TouchUp(const Touch& touch)
	{
		m_touchMap[touch.GetId()] = false;
		if (IsActive())
		{
			m_tapInfo.duration = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - m_startTime);
			m_tapInfo.position = touch.GetPosition();
			if (m_tapInfo.duration <= MAX_DURATION)
			{
				OnTap.NotifyAll(this, m_tapInfo);
			}
			Cancel();
		}
		if (!GetTouchCount()) Reset(); // empty map if no touch
	}
}
