#include "mxbackgroundaudiomanager.h"

#include "legomain.h"
#include "misc.h"
#include "mxaudiopresenter.h"
#include "mxdssound.h"
#include "mxmisc.h"
#include "mxnotificationmanager.h"
#include "mxpresenter.h"
#include "mxstreamer.h"
#include "mxticklemanager.h"
#include "mxutilities.h"
#include "mxwavepresenter.h"

DECOMP_SIZE_ASSERT(MxBackgroundAudioManager, 0x150)

// FUNCTION: LEGO1 0x1007ea90
// FUNCTION: BETA10 0x100e8530
MxBackgroundAudioManager::MxBackgroundAudioManager()
{
	NotificationManager()->Register(this);
	m_activePresenter = NULL;
	m_pendingPresenter = NULL;
	m_tickleState = MxPresenter::e_idle;
	m_speed = 0;
	m_targetVolume = 0;
	m_volumeSuppressionAmount = 0;
	m_enabled = FALSE;
}

// FUNCTION: LEGO1 0x1007ec20
MxBackgroundAudioManager::~MxBackgroundAudioManager()
{
	TickleManager()->UnregisterClient(this);
	NotificationManager()->Unregister(this);
	DestroyMusic();
}

// FUNCTION: LEGO1 0x1007ece0
MxResult MxBackgroundAudioManager::Create(MxAtomId& p_script, MxU32 p_frequencyMS)
{
	MxResult result = OpenMusic(p_script);

	if (result == SUCCESS) {
		TickleManager()->RegisterClient(this, p_frequencyMS);
		m_enabled = TRUE;
	}

	return result;
}

// FUNCTION: LEGO1 0x1007ed20
MxResult MxBackgroundAudioManager::OpenMusic(MxAtomId& p_script)
{
	if (m_script.GetInternal()) {
		DestroyMusic();
	}

	MxResult result = FAILURE;

	if (Streamer()->Open(p_script.GetInternal(), 0)) {
		m_script = p_script;
		result = SUCCESS;
	}

	return result;
}

// FUNCTION: LEGO1 0x1007ed70
void MxBackgroundAudioManager::DestroyMusic()
{
	if (m_script.GetInternal()) {
		MxDSAction ds;
		ds.SetAtomId(m_script);
		ds.SetUnknown24(-2);
		DeleteObject(ds);
		Streamer()->Close(m_script.GetInternal());
		m_enabled = FALSE;
	}
}

// FUNCTION: LEGO1 0x1007ee40
MxResult MxBackgroundAudioManager::Tickle()
{
	switch (m_tickleState) {
	case MxPresenter::e_starting:
		FadeToTargetVolume();
		break;
	case MxPresenter::e_streaming:
		MakePendingPresenterActive();
		break;
	case MxPresenter::e_repeating:
		FadeInPendingPresenter();
	}

	return SUCCESS;
}

// FUNCTION: LEGO1 0x1007ee70
void MxBackgroundAudioManager::MakePendingPresenterActive()
{
	if (m_activePresenter && m_activePresenter->GetAction()) {
		DeleteObject(*m_pendingPresenter->GetAction());
	}

	if (m_pendingPresenter) {
		m_activePresenter = m_pendingPresenter;
		m_action1 = m_action2;
		m_pendingPresenter = NULL;
		m_action2.SetObjectId(-1);
		m_action2.SetAtomId(MxAtomId());
		m_tickleState = MxPresenter::e_idle;
	}
}

// FUNCTION: LEGO1 0x1007ef40
void MxBackgroundAudioManager::FadeInPendingPresenter()
{
	MxS32 compare, volume;

	if (m_activePresenter == NULL) {
		if (m_pendingPresenter) {
			if (m_volumeSuppressionAmount != 0) {
				compare = 30;
			}
			else {
				compare = m_targetVolume;
			}

			volume = m_pendingPresenter->GetVolume();
			if (volume < compare) {
				if (m_speed + m_pendingPresenter->GetVolume() <= compare) {
					compare = m_speed + m_pendingPresenter->GetVolume();
				}

				m_pendingPresenter->SetVolume(compare);
			}
			else {
				m_pendingPresenter->SetVolume(compare);
				m_activePresenter = m_pendingPresenter;
				m_action1 = m_action2;
				m_pendingPresenter = NULL;
				m_action2.SetObjectId(-1);
				m_action2.SetAtomId(MxAtomId());
				m_tickleState = MxPresenter::e_idle;
			}
		}
	}
	else if (m_activePresenter->GetAction() != NULL) {
		if (m_activePresenter->GetVolume() == 0) {
			DeleteObject(*m_activePresenter->GetAction());
		}
		else {
			if (m_activePresenter->GetVolume() - m_speed > 0) {
				volume = m_activePresenter->GetVolume() - m_speed;
			}
			else {
				volume = 0;
			}

			m_activePresenter->SetVolume(volume);
		}
	}
}

// FUNCTION: LEGO1 0x1007f0e0
// FUNCTION: BETA10 0x100e8d8d
void MxBackgroundAudioManager::FadeToTargetVolume()
{
	MxS32 volume, compare;

	if (m_activePresenter != NULL) {
		volume = m_activePresenter->GetVolume();

		if (m_volumeSuppressionAmount != 0) {
			compare = 30;
		}
		else {
			compare = m_targetVolume;
		}

		if (volume < compare) {
			m_activePresenter->SetVolume(volume + m_speed < compare ? volume + m_speed : compare);
		}
		else if (compare < volume) {
			m_activePresenter->SetVolume(volume - m_speed > compare ? volume - m_speed : compare);
		}
		else {
			m_activePresenter->SetVolume(volume);
			m_tickleState = MxPresenter::e_idle;
		}
	}
	else {
		m_tickleState = MxPresenter::e_idle;
	}
}

// FUNCTION: LEGO1 0x1007f170
// FUNCTION: BETA10 0x100e8eb6
MxLong MxBackgroundAudioManager::Notify(MxParam& p_param)
{
	MxNotificationParam& param = (MxNotificationParam&) p_param;

	switch (param.GetNotification()) {
	case c_notificationStartAction:
		StartAction(p_param);
		return 1;
	case c_notificationEndAction:
		StopAction(p_param);
		return 1;
	}
	return 0;
}

// FUNCTION: LEGO1 0x1007f1b0
void MxBackgroundAudioManager::StartAction(MxParam& p_param)
{
	// TODO: the sender is most likely a MxAudioPresenter?
	m_pendingPresenter = (MxAudioPresenter*) ((MxNotificationParam&) p_param).GetSender();
	m_action2.SetAtomId(m_pendingPresenter->GetAction()->GetAtomId());
	m_action2.SetObjectId(m_pendingPresenter->GetAction()->GetObjectId());
	m_targetVolume = ((MxDSSound*) (m_pendingPresenter->GetAction()))->GetVolume();
	m_pendingPresenter->SetVolume(0);
}

// FUNCTION: LEGO1 0x1007f200
void MxBackgroundAudioManager::StopAction(MxParam& p_param)
{
	if (((MxNotificationParam&) p_param).GetSender() == m_activePresenter) {
		m_activePresenter = NULL;
		m_action1.SetAtomId(MxAtomId());
		m_action1.SetObjectId(-1);
	}
	else if (((MxNotificationParam&) p_param).GetSender() == m_pendingPresenter) {
		m_pendingPresenter = NULL;
		m_action2.SetAtomId(MxAtomId());
		m_action2.SetObjectId(-1);
	}

	Lego()->HandleEndAction(p_param);
}

// FUNCTION: LEGO1 0x1007f2f0
// FUNCTION: BETA10 0x100e90fc
MxResult MxBackgroundAudioManager::PlayMusic(
	MxDSAction& p_action,
	MxS32 p_speed,
	MxPresenter::TickleState p_tickleState
)
{
	assert(p_speed > 0);

	if (!m_enabled) {
		return SUCCESS;
	}

	if (m_action2.GetObjectId() == -1 && m_action1.GetObjectId() != p_action.GetObjectId()) {
		MxDSAction action;
		action.SetAtomId(GetCurrentAction().GetAtomId());
		action.SetObjectId(GetCurrentAction().GetObjectId());
		action.SetUnknown24(GetCurrentAction().GetUnknown24());

		m_action2.SetAtomId(p_action.GetAtomId());
		m_action2.SetObjectId(p_action.GetObjectId());
		m_action2.SetNotificationObject(this);
		m_action2.SetOrigin(this);

		MxResult result = Start(&m_action2);

		GetCurrentAction().SetAtomId(action.GetAtomId());
		GetCurrentAction().SetObjectId(action.GetObjectId());
		GetCurrentAction().SetUnknown24(action.GetUnknown24());

		if (result == SUCCESS) {
			m_tickleState = p_tickleState;
			m_speed = p_speed;
		}

		return result;
	}

	return FAILURE;
}

// FUNCTION: BETA10 0x100e92ec
void MxBackgroundAudioManager::Update(MxS32 p_targetVolume, MxS32 p_speed, MxPresenter::TickleState p_tickleState)
{
	assert(p_targetVolume >= 0 && p_targetVolume <= 100);
	assert(p_speed > 0);

	m_tickleState = p_tickleState;
	m_speed = p_speed;
	m_targetVolume = p_targetVolume;
}

// FUNCTION: LEGO1 0x1007f470
// FUNCTION: BETA10 0x100e9388
void MxBackgroundAudioManager::Stop()
{
	if (m_action2.GetObjectId() != -1) {
		DeleteObject(m_action2);
	}

	m_pendingPresenter = NULL;
	m_action2.SetAtomId(MxAtomId());
	m_action2.SetObjectId(-1);

	if (m_action1.GetObjectId() != -1) {
		DeleteObject(m_action1);
	}

	m_activePresenter = NULL;
	m_action1.SetAtomId(MxAtomId());
	m_volumeSuppressionAmount = 0;
	m_action1.SetObjectId(-1);
	m_tickleState = MxPresenter::e_idle;
}

// FUNCTION: LEGO1 0x1007f570
// FUNCTION: BETA10 0x100e94e6
void MxBackgroundAudioManager::LowerVolume()
{
	if (m_volumeSuppressionAmount == 0) {
		if (m_tickleState == 0) {
			m_tickleState = MxPresenter::e_starting;
		}
		m_speed = 20;
	}
	m_volumeSuppressionAmount++;
}

// FUNCTION: LEGO1 0x1007f5b0
// FUNCTION: BETA10 0x100e9543
void MxBackgroundAudioManager::RaiseVolume()
{
	if (m_volumeSuppressionAmount != 0) {
		m_volumeSuppressionAmount--;
		if (m_volumeSuppressionAmount == 0) {
			if (m_tickleState == 0) {
				m_tickleState = MxPresenter::e_starting;
			}
			m_speed = 10;
		}
	}
}

// FUNCTION: LEGO1 0x1007f5f0
void MxBackgroundAudioManager::Enable(MxBool p_enable)
{
	if (this->m_enabled != p_enable) {
		this->m_enabled = p_enable;

		if (!p_enable) {
			Stop();
		}
	}
}

// FUNCTION: LEGO1 0x1007f610
// FUNCTION: BETA10 0x100e95ee
MxResult MxBackgroundAudioManager::SetPendingPresenter(
	MxPresenter* p_presenter,
	MxS32 p_speed,
	MxPresenter::TickleState p_tickleState
)

{
	m_pendingPresenter = (MxAudioPresenter*) p_presenter;
	m_targetVolume = ((MxDSSound*) m_pendingPresenter->GetAction())->GetVolume();

	((MxWavePresenter*) m_pendingPresenter)->SetVolume(0);

	m_speed = p_speed;
	m_tickleState = p_tickleState;
	return SUCCESS;
}

// FUNCTION: LEGO1 0x1007f650
// FUNCTION: BETA10 0x100e9663
void MxBackgroundAudioManager::Init()
{
	this->m_activePresenter = NULL;
	this->m_tickleState = MxPresenter::e_idle;
}
