/* SpeedHandler.cpp */

/* Copyright (C) 2011-2024 Michael Lugmair (Lucio Carreras)
 *
 * This file is part of sayonara player
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Pitcher.h"

#include "Components/Engine/EngineUtils.h"
#include "Components/Engine/PipelineExtensions/Changeable.h"
#include "Utils/Logger/Logger.h"
#include "Utils/Utils.h"

namespace
{
	class PitcherImpl :
		public PipelineExtensions::Pitcher
	{
		public:
			PitcherImpl(GstElement* leftNeighbor, GstElement* rightNeighbor) :
				m_leftNeighbor {leftNeighbor},
				m_rightNeighbor {rightNeighbor}
			{
				Engine::Utils::createElement(&m_pitch, "pitch");
				g_object_ref(m_pitch);
			}

			~PitcherImpl() override
			{
				g_object_unref(m_pitch);
			}

			void setSpeed(const float speed, const double pitch, const bool preservePitch) override
			{
				if(!isPitchElementValid())
				{
					return;
				}

				if(preservePitch)
				{
					Engine::Utils::setValues(m_pitch,
					                         "tempo", speed,
					                         "rate", 1.0,
					                         "pitch", pitch);
				}

				else
				{
					Engine::Utils::setValues(m_pitch,
					                         "tempo", 1.0,
					                         "rate", speed,
					                         "pitch", pitch);
				}
			}

			void setPitchActivated(const bool b) override
			{
				using namespace PipelineExtensions;

				if(b && isPitchElementValid())
				{
					const auto added = Changeable::addElement(m_pitch, m_leftNeighbor, m_rightNeighbor);
					if(!added)
					{
						spLog(Log::Warning, this) << QString("Could not add pitch between %1 and %2")
							.arg(Engine::Utils::getElementName(m_leftNeighbor))
							.arg(Engine::Utils::getElementName(m_rightNeighbor));
					}
				}

				else
				{
					Changeable::removeElement(m_pitch, m_leftNeighbor, m_rightNeighbor);
				}
			}

		private:
			[[nodiscard]] bool isPitchElementValid() const
			{
				return G_IS_OBJECT(m_pitch);
			}

			GstElement* m_pitch {nullptr};
			GstElement* m_leftNeighbor;
			GstElement* m_rightNeighbor;
	};

	class PitcherDummy :
		public PipelineExtensions::Pitcher
	{
		public:
			~PitcherDummy() override = default;

			void setPitchActivated(bool /*b*/) override {}

			void setSpeed(float /*speed*/, double /*pitch*/, bool /*preservePitch*/) override {}
	};
}

namespace PipelineExtensions
{
	Pitcher::~Pitcher() = default;

	std::shared_ptr<Pitcher> createPitcher(GstElement* leftNeighbor, GstElement* rightNeighbor)
	{
		if(Util::getEnvironment("SAYONARA_ENGINE_PITCH") == "OFF")
		{
			return createDummyPitcher();
		}

		return std::make_shared<PitcherImpl>(leftNeighbor, rightNeighbor);
	}

	std::shared_ptr<Pitcher> createDummyPitcher()
	{
		return std::make_shared<PitcherDummy>();
	}
}