/* PlaylistLoader.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 "PlaylistLoader.h"
#include "PlaylistDBWrapper.h"

#include "Database/Connector.h"
#include "Database/Playlist.h"
#include "Utils/Algorithm.h"
#include "Utils/MetaData/MetaDataList.h"
#include "Utils/Playlist/CustomPlaylist.h"
#include "Utils/Settings/Settings.h"
#include "Utils/Tagging/TagReader.h"

namespace
{
[[nodiscard]] QList<Id> extractIdsFromPlaylists(const QList<CustomPlaylist>& customPlaylists)
{
	auto ids = QList<Id>{};
	Util::Algorithm::transform(customPlaylists, ids, [](const auto& playlist) {
		return playlist.id();
	});
	return ids;
}

[[nodiscard]] QList<Id> mergeIdLists(QList<Id> ids1, const QList<Id>& ids2)
{
	for(const auto& id: ids2)
	{
		if(!ids1.contains(id))
		{
			ids1 << id;
		}
	}

	return ids1;
}

class LoaderImpl : public Playlist::Loader
{
	public:
		explicit LoaderImpl(const Tagging::TagReaderPtr& tagReader) :
			m_tagReader{tagReader},
			m_lastTrackIndex{GetSetting(Set::PL_LastTrack)}
		{
			constexpr const auto IdAsc = PlaylistSortOrder::IDAsc;

			const auto loadTemporaryPlaylists = GetSetting(Set::PL_LoadTemporaryPlaylists);
			const auto loadSavedPlaylists = GetSetting(Set::PL_LoadSavedPlaylists);
			const auto loadLastTrackBeforeStop = GetSetting(Set::PL_RememberTrackAfterStop);
			const auto recentPlaylists = GetSetting(Set::PL_RecentPlaylists);
			const auto loadRecentSession = GetSetting(Set::PL_LoadRecentPlaylists);

			if(m_lastTrackIndex == -1 && loadLastTrackBeforeStop)
			{
				m_lastTrackIndex = GetSetting(Set::PL_LastTrackBeforeStop);
			}

			if(loadRecentSession)
			{
				const auto tempPlaylists =
					loadPlaylists(Playlist::StoreType::OnlyTemporary, IdAsc, false, m_tagReader);

				const auto temporaryIds = extractIdsFromPlaylists(tempPlaylists);
				m_playlists <<
					Playlist::loadPlaylistsByIds(mergeIdLists(recentPlaylists, temporaryIds), true, m_tagReader);
			}

			else
			{
				if(loadSavedPlaylists && loadTemporaryPlaylists)
				{
					m_playlists = loadPlaylists(Playlist::StoreType::TemporaryAndPermanent, IdAsc, true, m_tagReader);
				}

				else if(loadSavedPlaylists)
				{
					m_playlists = loadPlaylists(Playlist::StoreType::OnlyPermanent, IdAsc, true, m_tagReader);
				}

				else if(loadTemporaryPlaylists)
				{
					m_playlists = loadPlaylists(Playlist::StoreType::OnlyTemporary, IdAsc, true, m_tagReader);
				}

				else
				{
					m_playlists.clear();
				}
			}

			if(m_playlists.isEmpty())
			{
				return;
			}

			if(m_lastPlaylistId >= 0)
			{
				const auto hasPlaylistId = Util::Algorithm::contains(m_playlists, [&](const auto& playlist) {
					return (m_lastPlaylistId == playlist.id());
				});

				if(!hasPlaylistId)
				{
					auto* playlistConnector = DB::Connector::instance()->playlistConnector();
					const auto playlist = playlistConnector->getPlaylistById(m_lastPlaylistId, true);
					m_playlists.prepend(playlist);
				}
			}
		}

		~LoaderImpl() = default;

		[[nodiscard]] int getLastPlaylistIndex() const override
		{
			return Util::Algorithm::indexOf(m_playlists, [&](const auto& playlist) {
				return (playlist.id() == m_lastPlaylistId);
			});
		}

		[[nodiscard]] int getLastTrackIndex() const override
		{
			if(!GetSetting(Set::PL_LoadLastTrack))
			{
				return -1;
			}

			const auto lastPlaylistIndex = getLastPlaylistIndex();
			if(!Util::between(lastPlaylistIndex, m_playlists.size()))
			{
				return -1;
			}

			const auto trackCount = m_playlists[lastPlaylistIndex].tracks().count();
			if((trackCount > 0) && (m_lastTrackIndex >= trackCount))
			{
				return 0;
			}

			if(!Util::between(m_lastTrackIndex, trackCount))
			{
				return -1;
			}

			return m_lastTrackIndex;
		}

		[[nodiscard]] const QList<CustomPlaylist>& playlists() const override { return m_playlists; }

	private:
		Tagging::TagReaderPtr m_tagReader;
		QList<CustomPlaylist> m_playlists;
		int m_lastPlaylistId{GetSetting(Set::PL_LastPlaylist)};
		int m_lastTrackIndex{-1};
};
} // namespace

std::shared_ptr<Playlist::Loader> Playlist::Loader::create(const Tagging::TagReaderPtr& tagReader)
{
	return std::make_shared<LoaderImpl>(tagReader);
}
