#include "towtrack.h"

#include "extensions/multiplayer.h"
#include "isle.h"
#include "isle_actions.h"
#include "jukebox_actions.h"
#include "legoanimationmanager.h"
#include "legocontrolmanager.h"
#include "legogamestate.h"
#include "legonavcontroller.h"
#include "legopathstruct.h"
#include "legoutils.h"
#include "legovariables.h"
#include "legoworld.h"
#include "misc.h"
#include "mxactionnotificationparam.h"
#include "mxbackgroundaudiomanager.h"
#include "mxmisc.h"
#include "mxsoundpresenter.h"
#include "mxtimer.h"
#include "mxtransitionmanager.h"
#include "mxvariabletable.h"

#include <stdio.h>

using namespace Extensions;

DECOMP_SIZE_ASSERT(TowTrack, 0x180)
DECOMP_SIZE_ASSERT(TowTrackMissionState, 0x28)

// Flags used in isle.cpp
extern MxU32 g_isleFlags;

// FUNCTION: LEGO1 0x1004c720
TowTrack::TowTrack()
{
	m_unused0x168 = 0;
	m_actorId = -1;
	m_state = NULL;
	m_treeBlockageTriggered = 0;
	m_lastAction = IsleScript::c_noneIsle;
	m_speedComplaintTriggered = 0;
	m_lastAnimation = IsleScript::c_noneIsle;
	m_maxLinearVel = 40.0;
	m_fuel = 1.0;
}

// FUNCTION: LEGO1 0x1004c970
TowTrack::~TowTrack()
{
	ControlManager()->Unregister(this);
}

// FUNCTION: LEGO1 0x1004c9e0
// FUNCTION: BETA10 0x100f6bf1
MxResult TowTrack::Create(MxDSAction& p_dsAction)
{
	MxResult result = IslePathActor::Create(p_dsAction);

	if (result == SUCCESS) {
		m_world = CurrentWorld();

		if (m_world) {
			m_world->Add(this);
		}

		m_state = (TowTrackMissionState*) GameState()->GetState("TowTrackMissionState");
		if (!m_state) {
			m_state = new TowTrackMissionState();
			m_state->m_state = TowTrackMissionState::e_none;
			GameState()->RegisterState(m_state);
		}
	}

	VariableTable()->SetVariable(g_varTOWFUEL, "1.0");
	m_fuel = 1.0;
	m_time = Timer()->GetTime();
	return result;
}

// FUNCTION: LEGO1 0x1004cb10
void TowTrack::Animate(float p_time)
{
	IslePathActor::Animate(p_time);

	if (UserActor() == this) {
		char buf[200];
		float speed = abs(m_worldSpeed);
		float maxLinearVel = NavController()->GetMaxLinearVel();

		sprintf(buf, "%g", speed / maxLinearVel);
		VariableTable()->SetVariable(g_varTOWSPEED, buf);

		m_fuel += (p_time - m_time) * -3.333333333e-06f;
		if (m_fuel < 0) {
			m_fuel = 0;
		}

		m_time = p_time;

		sprintf(buf, "%g", m_fuel);
		VariableTable()->SetVariable(g_varTOWFUEL, buf);

		if (p_time - m_state->m_startTime > 100000.0f && m_state->m_state == TowTrackMissionState::e_started &&
			!m_state->m_takingTooLong) {
			PlayAction(IsleScript::c_Avo909In_PlayWav);
			m_state->m_takingTooLong = TRUE;
		}
	}
}

// FUNCTION: LEGO1 0x1004cc40
void TowTrack::CreateState()
{
	m_state = (TowTrackMissionState*) GameState()->GetState("TowTrackMissionState");
	if (m_state == NULL) {
		m_state = (TowTrackMissionState*) GameState()->CreateState("TowTrackMissionState");
	}
}

// FUNCTION: LEGO1 0x1004cc80
// FUNCTION: BETA10 0x100f6de2
MxLong TowTrack::Notify(MxParam& p_param)
{
	MxLong result = 0;
	MxNotificationParam& param = (MxNotificationParam&) p_param;

	switch (param.GetNotification()) {
	case c_notificationType0:
		result = HandleNotification0();
		break;
	case c_notificationEndAction:
		result = HandleEndAction((MxEndActionNotificationParam&) p_param);
		break;
	case c_notificationClick:
		result = HandleClick();
		break;
	case c_notificationControl:
		result = HandleControl((LegoControlManagerNotificationParam&) p_param);
		break;
	case c_notificationEndAnim:
		result = HandleEndAnim((LegoEndAnimNotificationParam&) p_param);
		break;
	case c_notificationPathStruct:
		result = HandlePathStruct((LegoPathStructNotificationParam&) p_param);
		break;
	}

	return result;
}

// FUNCTION: LEGO1 0x1004cd30
MxLong TowTrack::HandleEndAnim(LegoEndAnimNotificationParam& p_param)
{
	return 1;
}

// FUNCTION: LEGO1 0x1004cd40
// FUNCTION: BETA10 0x100f6f1f
MxLong TowTrack::HandleEndAction(MxEndActionNotificationParam& p_param)
{
	if (p_param.GetAction() != NULL) {
		IsleScript::Script objectId = (IsleScript::Script) p_param.GetAction()->GetObjectId();

		if (m_lastAnimation == objectId) {
			m_lastAnimation = IsleScript::c_noneIsle;
		}

		if (m_lastAction == objectId) {
			if (m_lastAnimation == IsleScript::c_noneIsle) {
				BackgroundAudioManager()->RaiseVolume();
			}

			m_lastAction = IsleScript::c_noneIsle;
		}
		else if (objectId == IsleScript::c_wrt060bm_RunAnim) {
			if (m_actorId < LegoActor::c_pepper || m_actorId > LegoActor::c_laura) {
				m_actorId = LegoActor::c_laura;
			}

			switch (1 + SDL_rand(m_actorId != 4 ? 4 : 3)) {
			case 1:
				PlayFinalAnimation(IsleScript::c_wrt074sl_RunAnim);
				break;
			case 2:
				PlayFinalAnimation(IsleScript::c_wrt075rh_RunAnim);
				break;
			case 3:
				PlayFinalAnimation(IsleScript::c_wrt076df_RunAnim);
				break;
			case 4:
				PlayFinalAnimation(IsleScript::c_wrt078ni_RunAnim);
				break;
			}
		}
		else if (objectId == IsleScript::c_wrt074sl_RunAnim || objectId == IsleScript::c_wrt075rh_RunAnim || objectId == IsleScript::c_wrt076df_RunAnim || objectId == IsleScript::c_wrt078ni_RunAnim) {
			m_state->m_state = TowTrackMissionState::e_hookedUp;
			CurrentWorld()->PlaceActor(UserActor());
			HandleClick();
		}
		else if (objectId == IsleScript::c_wgs083nu_RunAnim) {
			if (m_actorId < LegoActor::c_pepper || m_actorId > LegoActor::c_laura) {
				m_actorId = LegoActor::c_laura;
			}

			switch (m_actorId) {
			case c_pepper:
				PlayActorAnimation(IsleScript::c_wgs085nu_RunAnim);
				break;
			case c_mama:
				PlayActorAnimation(IsleScript::c_wgs086nu_RunAnim);
				break;
			case c_papa:
				PlayActorAnimation(IsleScript::c_wgs088nu_RunAnim);
				break;
			case c_nick:
				PlayActorAnimation(IsleScript::c_wgs087nu_RunAnim);
				break;
			case c_laura:
				PlayActorAnimation(IsleScript::c_wgs089nu_RunAnim);
				break;
			}

			m_state->UpdateScore(LegoState::e_red, m_actorId);

			AnimationManager()->FUN_1005f6d0(TRUE);
			g_isleFlags |= Isle::c_playMusic;
			AnimationManager()->EnableCamAnims(TRUE);
		}
		else if (objectId == IsleScript::c_wgs090nu_RunAnim) {
			if (m_actorId < LegoActor::c_pepper || m_actorId > LegoActor::c_laura) {
				m_actorId = LegoActor::c_laura;
			}

			switch (m_actorId) {
			case c_pepper:
				PlayActorAnimation(IsleScript::c_wgs091nu_RunAnim);
				break;
			case c_mama:
				PlayActorAnimation(IsleScript::c_wgs092nu_RunAnim);
				break;
			case c_papa:
				PlayActorAnimation(IsleScript::c_wgs094nu_RunAnim);
				break;
			case c_nick:
				PlayActorAnimation(IsleScript::c_wgs093nu_RunAnim);
				break;
			case c_laura:
				PlayActorAnimation(IsleScript::c_wgs095nu_RunAnim);
				break;
			}

			m_state->UpdateScore(LegoState::e_blue, m_actorId);
		}
		else if (objectId == IsleScript::c_wgs097nu_RunAnim) {
			if (m_actorId < LegoActor::c_pepper || m_actorId > LegoActor::c_laura) {
				m_actorId = LegoActor::c_laura;
			}

			switch (m_actorId) {
			case c_pepper:
				PlayActorAnimation(IsleScript::c_wgs098nu_RunAnim);
				break;
			case c_mama:
				PlayActorAnimation(IsleScript::c_wgs099nu_RunAnim);
				break;
			case c_papa:
				PlayActorAnimation(IsleScript::c_wgs101nu_RunAnim);
				break;
			case c_nick:
				PlayActorAnimation(IsleScript::c_wgs100nu_RunAnim);
				break;
			case c_laura:
				PlayActorAnimation(IsleScript::c_wgs102nu_RunAnim);
				break;
			}

			m_state->UpdateScore(LegoState::e_yellow, m_actorId);
		}
		else if (objectId == IsleScript::c_wgs098nu_RunAnim || objectId == IsleScript::c_wgs099nu_RunAnim || objectId == IsleScript::c_wgs100nu_RunAnim || objectId == IsleScript::c_wgs101nu_RunAnim || objectId == IsleScript::c_wgs102nu_RunAnim || objectId == IsleScript::c_wgs085nu_RunAnim || objectId == IsleScript::c_wgs086nu_RunAnim || objectId == IsleScript::c_wgs087nu_RunAnim || objectId == IsleScript::c_wgs088nu_RunAnim || objectId == IsleScript::c_wgs089nu_RunAnim || objectId == IsleScript::c_wgs091nu_RunAnim || objectId == IsleScript::c_wgs092nu_RunAnim || objectId == IsleScript::c_wgs093nu_RunAnim || objectId == IsleScript::c_wgs094nu_RunAnim || objectId == IsleScript::c_wgs095nu_RunAnim) {
			((Act1State*) GameState()->GetState("Act1State"))->m_state = Act1State::e_none;
			AnimationManager()->FUN_1005f6d0(TRUE);
			g_isleFlags |= Isle::c_playMusic;
			AnimationManager()->EnableCamAnims(TRUE);
		}
	}

	return 1;
}

// FUNCTION: LEGO1 0x1004d330
// FUNCTION: BETA10 0x100f74c0
MxLong TowTrack::HandlePathStruct(LegoPathStructNotificationParam& p_param)
{
	MxDSAction action;

	// 0x168 corresponds to the path at the gas station
	if (p_param.GetData() == 0x168) {
		m_fuel = 1.0f;
	}

	if (UserActor() != this) {
		return 0;
	}

	if (m_state->m_state == TowTrackMissionState::e_hookedUp &&
		((p_param.GetTrigger() == LegoPathStruct::c_camAnim && (p_param.GetData() == 9 || p_param.GetData() == 8)) ||
		 (p_param.GetTrigger() == LegoPathStruct::c_missionFinalWaypoint && p_param.GetData() == 0x169))) {
		m_state->m_state = TowTrackMissionState::e_none;

		MxLong time = Timer()->GetTime() - m_state->m_startTime;
		Leave();

		if (time < 200000) {
			PlayFinalAnimation(IsleScript::c_wgs083nu_RunAnim);
		}
		else if (time < 300000) {
			PlayFinalAnimation(IsleScript::c_wgs090nu_RunAnim);
		}
		else {
			PlayFinalAnimation(IsleScript::c_wgs097nu_RunAnim);
		}
	}
	else if (m_state->m_state == TowTrackMissionState::e_started && p_param.GetTrigger() == LegoPathStruct::c_camAnim && p_param.GetData() == 0x37) {
		m_state->m_state = TowTrackMissionState::e_hookingUp;
		StopActions();

		if (m_lastAction != IsleScript::c_noneIsle) {
			InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL);
		}

		Leave();
		PlayFinalAnimation(IsleScript::c_wrt060bm_RunAnim);
	}
	else if (p_param.GetTrigger() == LegoPathStruct::c_missionFinalWaypoint && m_state->m_state == TowTrackMissionState::e_started) {
		if (p_param.GetData() == 0x15f) {
			if (m_treeBlockageTriggered == 0) {
				m_treeBlockageTriggered = 1;
				InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_wns050p1_RunAnim, NULL);
			}
		}
		else if (p_param.GetData() == 0x160) {
			if (m_speedComplaintTriggered == 0) {
				m_speedComplaintTriggered = 1;
				InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_wns046mg_RunAnim, NULL);
			}

			if (!m_state->m_takingTooLong && m_lastAction == IsleScript::c_noneIsle) {
				if (m_actorId < LegoActor::c_pepper || m_actorId > LegoActor::c_laura) {
					m_actorId = LegoActor::c_laura;
				}

				IsleScript::Script objectId;

				switch (m_actorId) {
				case c_pepper:
					objectId = IsleScript::c_wns034na_PlayWav;
					break;
				case c_mama:
					switch (1 + SDL_rand(2)) {
					case 1:
						objectId = IsleScript::c_wns037na_PlayWav;
						break;
					case 2:
						objectId = IsleScript::c_wns038na_PlayWav;
						break;
					}
					break;
				case c_papa:
					switch (1 + SDL_rand(2)) {
					case 1:
						objectId = IsleScript::c_wns041na_PlayWav;
						break;
					case 2:
						objectId = IsleScript::c_wns042na_PlayWav;
						break;
					}
					break;
				case c_nick:
					switch (1 + SDL_rand(2)) {
					case 1:
						objectId = IsleScript::c_wns039na_PlayWav;
						break;
					case 2:
						objectId = IsleScript::c_wns040na_PlayWav;
						break;
					}
					break;
				case c_laura:
					switch (1 + SDL_rand(2)) {
					case 1:
						objectId = IsleScript::c_wns043na_PlayWav;
						break;
					case 2:
						objectId = IsleScript::c_wns044na_PlayWav;
						break;
					}
					break;
				}

				PlayAction(objectId);
			}
		}
	}

	return 0;
}

// FUNCTION: LEGO1 0x1004d690
MxLong TowTrack::HandleClick()
{
	if (((Act1State*) GameState()->GetState("Act1State"))->m_state != Act1State::e_towtrack) {
		return 1;
	}

	if (m_state->m_state == TowTrackMissionState::e_hookingUp) {
		return 1;
	}

	Disable(TRUE, 0);
	((Isle*) CurrentWorld())->SetDestLocation(LegoGameState::e_towtrack);
	TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);

	if (UserActor()->GetActorId() != GameState()->GetActorId()) {
		((IslePathActor*) UserActor())->Exit();
	}

	m_time = Timer()->GetTime();
	m_actorId = UserActor()->GetActorId();

	Enter();
	InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_TowTrackDashboard, NULL);
	ControlManager()->Register(this);

	if (m_state->m_state == TowTrackMissionState::e_none) {
		return 1;
	}

	if (m_state->m_state == TowTrackMissionState::e_hookedUp) {
		SpawnPlayer(LegoGameState::e_towTrackHookedUp, TRUE, 0);
		FindROI("rcred")->SetVisibility(FALSE);
	}
	else {
		SpawnPlayer(LegoGameState::e_garageExited, TRUE, 0);
		m_lastAction = IsleScript::c_noneIsle;
		m_lastAnimation = IsleScript::c_noneIsle;
		m_state->m_startTime = Timer()->GetTime();
		m_state->m_takingTooLong = FALSE;
		InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_wns057rd_RunAnim, NULL);
		InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_wns048p1_RunAnim, NULL);
		InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_wns049p1_RunAnim, NULL);
		InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_wns051bd_RunAnim, NULL);
		InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_wns053pr_RunAnim, NULL);
		InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_wns045di_RunAnim, NULL);
		InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_pns123pr_RunAnim, NULL);
	}

	return 1;
}

// FUNCTION: LEGO1 0x1004d8f0
void TowTrack::Exit()
{
	GameState()->m_currentArea = LegoGameState::e_garageExterior;
	StopActions();
	Reset();
	Leave();
}

// FUNCTION: LEGO1 0x1004d920
void TowTrack::Leave()
{
	IslePathActor::Exit();
	CurrentWorld()->RemoveActor(this);
	m_roi->SetVisibility(FALSE);
	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_TowTrackDashboard_Bitmap);
	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_TowTrackArms_Ctl);
	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_TowHorn_Ctl);
	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_TowHorn_Sound);
	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_TowInfo_Ctl);
	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_TowSpeedMeter);
	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_TowFuelMeter);
	ControlManager()->Unregister(this);
}

// FUNCTION: LEGO1 0x1004d9e0
MxLong TowTrack::HandleControl(LegoControlManagerNotificationParam& p_param)
{
	MxLong result = 0;

	if (p_param.m_enabledChild == 1) {
		switch (p_param.m_clickedObjectId) {
		case IsleScript::c_TowTrackArms_Ctl:
			Exit();
			GameState()->m_currentArea = LegoGameState::e_vehicleExited;
			result = 1;
			break;
		case IsleScript::c_TowInfo_Ctl:
			((Isle*) CurrentWorld())->SetDestLocation(LegoGameState::e_infomain);
			TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
			Exit();
			GameState()->m_currentArea = LegoGameState::e_vehicleExited;
			result = 1;
			break;
		case IsleScript::c_TowHorn_Ctl:
			MxSoundPresenter* presenter = (MxSoundPresenter*) CurrentWorld()->Find("MxSoundPresenter", "TowHorn_Sound");
			presenter->Enable(p_param.m_enabledChild);
			Extension<MultiplayerExt>::Call(MP::HandleHornPressed, (MxU32) p_param.m_clickedObjectId);
			break;
		}
	}

	return result;
}

// FUNCTION: LEGO1 0x1004dab0
void TowTrack::Init()
{
	m_state->m_state = TowTrackMissionState::e_started;
	HandleClick();
}

// FUNCTION: LEGO1 0x1004dad0
void TowTrack::ActivateSceneActions()
{
	PlayMusic(JukeboxScript::c_JBMusic2);

	if (m_state->m_state != TowTrackMissionState::e_none) {
		if (m_state->m_state == TowTrackMissionState::e_hookedUp) {
			PlayAction(IsleScript::c_wrt082na_PlayWav);
		}
		else {
			PlayAction(IsleScript::c_wgs032nu_PlayWav);
		}
	}
}

// FUNCTION: LEGO1 0x1004db10
void TowTrack::StopActions()
{
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_wns050p1_RunAnim, NULL);
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_wns046mg_RunAnim, NULL);
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_wns057rd_RunAnim, NULL);
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_wns048p1_RunAnim, NULL);
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_wns049p1_RunAnim, NULL);
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_wns051bd_RunAnim, NULL);
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_wns053pr_RunAnim, NULL);
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_wns045di_RunAnim, NULL);
	InvokeAction(Extra::e_stop, *g_isleScript, IsleScript::c_pns123pr_RunAnim, NULL);
}

// FUNCTION: LEGO1 0x1004dbe0
void TowTrack::Reset()
{
	if (m_lastAction != -1) {
		InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL);
	}

	((Act1State*) GameState()->GetState("Act1State"))->m_state = Act1State::e_none;
	m_state->m_state = TowTrackMissionState::e_none;
	g_isleFlags |= Isle::c_playMusic;
	AnimationManager()->EnableCamAnims(TRUE);
	AnimationManager()->FUN_1005f6d0(TRUE);
	m_state->m_startTime = INT_MIN;
	m_state->m_takingTooLong = FALSE;
	m_state = NULL;
	m_treeBlockageTriggered = 0;
	m_speedComplaintTriggered = 0;
}

// FUNCTION: LEGO1 0x1004dc80
// FUNCTION: BETA10 0x100f86a0
void TowTrack::PlayFinalAnimation(IsleScript::Script p_objectId)
{
	AnimationManager()
		->FUN_10060dc0(p_objectId, NULL, TRUE, LegoAnimationManager::e_unk0, NULL, FALSE, FALSE, FALSE, TRUE);
	m_lastAnimation = p_objectId;
}

// FUNCTION: LEGO1 0x1004dcb0
void TowTrack::PlayActorAnimation(IsleScript::Script p_objectId)
{
	AnimationManager()->FUN_1005f6d0(TRUE);
	AnimationManager()
		->FUN_10060dc0(p_objectId, NULL, TRUE, LegoAnimationManager::e_unk1, NULL, FALSE, TRUE, TRUE, TRUE);
	m_lastAnimation = p_objectId;
}

// FUNCTION: LEGO1 0x1004dcf0
void TowTrack::PlayAction(IsleScript::Script p_objectId)
{
	if (p_objectId != IsleScript::c_noneIsle) {
		InvokeAction(Extra::e_start, *g_isleScript, p_objectId, NULL);
	}

	m_lastAction = p_objectId;
	BackgroundAudioManager()->LowerVolume();
}

// FUNCTION: LEGO1 0x1004dd30
TowTrackMissionState::TowTrackMissionState()
{
	m_state = TowTrackMissionState::e_none;
	m_startTime = 0;
	m_takingTooLong = FALSE;
	m_peScore = 0;
	m_maScore = 0;
	m_paScore = 0;
	m_niScore = 0;
	m_laScore = 0;
	m_peHighScore = 0;
	m_maHighScore = 0;
	m_paHighScore = 0;
	m_niHighScore = 0;
	m_laHighScore = 0;
}
