/**
 * @file fsfloaterstreamtitle.cpp
 * @brief Class for the stream title and history floater
 *
 * $LicenseInfo:firstyear=2022&license=viewerlgpl$
 * Phoenix Firestorm Viewer Source Code
 * Copyright (c) 2022 Ansariel Hiller @ Second Life
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
 * http://www.firestormviewer.org
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "fsfloaterstreamtitle.h"

#include "fsscrolllistctrl.h"
#include "llaudioengine.h"
#include "llbutton.h"
#include "llfloaterreg.h"
#include "lltextbox.h"
#include "llviewercontrol.h"

static constexpr size_t MAX_HISTORY_ENTRIES{ 10 };
static constexpr F32 SCROLL_STEP_DELAY{ 0.25f };
static constexpr F32 SCROLL_END_DELAY{ 4.f };

FSStreamTitleManager::~FSStreamTitleManager()
{
	if (mMetadataUpdateConnection.connected())
	{
		mMetadataUpdateConnection.disconnect();
	}
}

void FSStreamTitleManager::initSingleton()
{
	if (!gAudiop || !gAudiop->getStreamingAudioImpl())
	{
		return;
	}

	mMetadataUpdateConnection = gAudiop->getStreamingAudioImpl()->setMetadataUpdateCallback(std::bind(&FSStreamTitleManager::processMetadataUpdate, this, std::placeholders::_1));
	processMetadataUpdate(gAudiop->getStreamingAudioImpl()->getCurrentMetadata());
}

void FSStreamTitleManager::processMetadataUpdate(const LLSD& metadata) noexcept
{
	std::string chat{};

	if (metadata.has("ARTIST"))
	{
		chat = metadata["ARTIST"].asString();
	}
	if (metadata.has("TITLE"))
	{
		if (chat.length() > 0)
		{
			chat.append(" - ");
		}
		chat.append(metadata["TITLE"].asString());
	}

	if (chat != mCurrentStreamTitle)
	{
		mCurrentStreamTitle = std::move(chat);

		if (!mCurrentStreamTitle.empty() && (mStreamTitleHistory.empty() || mStreamTitleHistory.back() != mCurrentStreamTitle))
		{
			mStreamTitleHistory.emplace_back(mCurrentStreamTitle);

			if (mStreamTitleHistory.size() > MAX_HISTORY_ENTRIES)
			{
				mStreamTitleHistory.erase(mStreamTitleHistory.begin());
			}

			mHistoryUpdateSignal(mStreamTitleHistory);
		}

		mUpdateSignal(mCurrentStreamTitle);
	}
}


//////////////////////////////////////////////////////////////////////////
/// FSFloaterStreamTitleHistory
//////////////////////////////////////////////////////////////////////////

FSFloaterStreamTitleHistory::FSFloaterStreamTitleHistory(const LLSD& key)
	: LLFloater(key)
{
}

FSFloaterStreamTitleHistory::~FSFloaterStreamTitleHistory()
{
	if (mUpdateConnection.connected())
	{
		mUpdateConnection.disconnect();
	}
}

BOOL FSFloaterStreamTitleHistory::postBuild()
{
	mHistoryCtrl = findChild<FSScrollListCtrl>("history");

	FSStreamTitleManager& instance = FSStreamTitleManager::instance();
	mUpdateConnection = instance.setHistoryUpdateCallback([this](const FSStreamTitleManager::history_vec_t& history) { updateHistory(history); });
	updateHistory(instance.getStreamTitleHistory());

	return TRUE;
}

void FSFloaterStreamTitleHistory::updateHistory(const FSStreamTitleManager::history_vec_t& history)
{
	mHistoryCtrl->clearRows();
	
	for (const auto& entry : history)
	{
		LLSD data;
		data["columns"][0]["name"] = "title";
		data["columns"][0]["value"] = entry;

		mHistoryCtrl->addElement(data, ADD_TOP);
	}
}

void FSFloaterStreamTitleHistory::setOwnerOrigin(LLView* owner) noexcept
{
	mOwner = owner->getHandle();
}

void FSFloaterStreamTitleHistory::draw()
{
	LLFloater::draw();
	
	if (mOwner.get())
	{
		static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
		drawConeToOwner(mContextConeOpacity, max_opacity, mOwner.get());
	}
}


//////////////////////////////////////////////////////////////////////////
/// FSFloaterStreamTitle
//////////////////////////////////////////////////////////////////////////

FSFloaterStreamTitle::FSFloaterStreamTitle(const LLSD& key)
	: LLFloater(key), LLEventTimer(SCROLL_STEP_DELAY)
{
	mEventTimer.stop();
}

FSFloaterStreamTitle::~FSFloaterStreamTitle()
{
	if (mUpdateConnection.connected())
	{
		mUpdateConnection.disconnect();
	}
}

BOOL FSFloaterStreamTitle::postBuild()
{
	mTitletext = findChild<LLTextBox>("streamtitle");
	mHistoryBtn = findChild<LLButton>("btn_history");

	FSStreamTitleManager& instance = FSStreamTitleManager::instance();
	mUpdateConnection = instance.setUpdateCallback([this](std::string_view streamtitle) { updateStreamTitle(streamtitle); });
	updateStreamTitle(instance.getCurrentStreamTitle());

	mHistoryBtn->setCommitCallback(std::bind(&FSFloaterStreamTitle::openHistory, this));

	setVisibleCallback(boost::bind(&FSFloaterStreamTitle::closeHistory, this));

	return TRUE;
}

void FSFloaterStreamTitle::openHistory() noexcept
{
	LLFloater* root_floater = gFloaterView->getParentFloater(this);
	FSFloaterStreamTitleHistory* history_floater = LLFloaterReg::showTypedInstance<FSFloaterStreamTitleHistory>("fs_streamtitlehistory");

	if (root_floater)
	{
		root_floater->addDependentFloater(history_floater);
		history_floater->setOwnerOrigin(root_floater);
	}

	mHistory = history_floater->getHandle();
}

void FSFloaterStreamTitle::closeHistory() noexcept
{
	if (mHistory.get())
	{
		mHistory.get()->closeFloater();
	}
}

void FSFloaterStreamTitle::updateStreamTitle(std::string_view streamtitle) noexcept
{
	if (streamtitle.empty())
	{
		mTitletext->setText(getString("NoStream"));
	}
	else
	{
		mTitletext->setText(static_cast<std::string>(streamtitle));
	}

	mCurrentTitle = mTitletext->getText();
	mCurrentDrawnTitle = mCurrentTitle;

	mTitletext->setToolTip(mCurrentTitle);

	checkTitleWidth();
}

void FSFloaterStreamTitle::reshape(S32 width, S32 height, BOOL called_from_parent)
{
	LLFloater::reshape(width, height, called_from_parent);
	checkTitleWidth();
}

void FSFloaterStreamTitle::checkTitleWidth() noexcept
{
	if (!mTitletext)
	{
		return;
	}

	S32 text_width = mTitletext->getFont()->getWidth(mCurrentTitle);
	S32 textbox_width = mTitletext->getRect().getWidth();

	if (text_width > textbox_width)
	{
		mResetTitle = false;
		mEventTimer.start();
	}
	else
	{
		mEventTimer.stop();
		mTitletext->setText(mCurrentTitle);
		mCurrentDrawnTitle = mCurrentTitle;
	}
}

BOOL FSFloaterStreamTitle::tick()
{
	if (mResetTitle)
	{
		mPeriod = SCROLL_END_DELAY;
		mEventTimer.reset();
		mCurrentDrawnTitle = mCurrentTitle;
		mResetTitle = false;
	}
	else
	{
		mPeriod = SCROLL_STEP_DELAY;
		mEventTimer.reset();
		mCurrentDrawnTitle.erase(mCurrentDrawnTitle.begin());
	}

	mTitletext->setText(mCurrentDrawnTitle);

	S32 text_width = mTitletext->getFont()->getWidth(mCurrentDrawnTitle);
	S32 textbox_width = mTitletext->getRect().getWidth();

	if (textbox_width > text_width)
	{
		mPeriod = SCROLL_END_DELAY;
		mEventTimer.reset();
		mResetTitle = true;
	}

	return FALSE;
}
