/*
host_cmd.c - dedicated and normal host
Copyright (C) 2017 Uncle Mike

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.
*/

#include "common.h"
#include "platform/platform.h"

void COM_InitHostState( void )
{
	memset( GameState, 0, sizeof( game_status_t ));
}

static void Host_SetState( host_state_t newState, qboolean clearNext )
{
	if( clearNext )
		GameState->nextstate = newState;
	GameState->curstate = newState;

	if( clearNext && newState == STATE_RUNFRAME )
	{
		// states finished here
		GameState->backgroundMap = false;
		GameState->loadGame = false;
		GameState->newGame = false;
	}
}

static void Host_SetNextState( host_state_t nextState )
{
	ASSERT( GameState->curstate == STATE_RUNFRAME );
	GameState->nextstate = nextState;
}

void COM_NewGame( char const *pMapName )
{
	if( GameState->nextstate != STATE_RUNFRAME )
		return;

	if( UI_CreditsActive( ))
		return;

	Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName ));
	Host_SetNextState( STATE_LOAD_LEVEL );

	GameState->backgroundMap = false;
	GameState->landmarkName[0] = 0;
	GameState->loadGame = false;
	GameState->newGame = true;

	SV_ShutdownGame(); // exit from current game
}

void COM_LoadLevel( char const *pMapName, qboolean background )
{
	if( GameState->nextstate != STATE_RUNFRAME )
		return;

	if( UI_CreditsActive( ))
		return;

	Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName ));
	Host_SetNextState( STATE_LOAD_LEVEL );

	GameState->backgroundMap = background;
	GameState->landmarkName[0] = 0;
	GameState->loadGame = false;
	GameState->newGame = false;

	SV_ShutdownGame(); // exit from current game
}

void COM_LoadGame( char const *pMapName )
{
	if( GameState->nextstate != STATE_RUNFRAME )
		return;

	if( UI_CreditsActive( ))
		return;

	Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName ));
	Host_SetNextState( STATE_LOAD_GAME );
	GameState->backgroundMap = false;
	GameState->newGame = false;
	GameState->loadGame = true;
}

void COM_ChangeLevel( char const *pNewLevel, char const *pLandmarkName, qboolean background )
{
	if( GameState->nextstate != STATE_RUNFRAME )
		return;

	if( UI_CreditsActive( ))
		return;

	Q_strncpy( GameState->levelName, pNewLevel, sizeof( GameState->levelName ));
	GameState->backgroundMap = background;

	if( COM_CheckString( pLandmarkName ))
	{
		Q_strncpy( GameState->landmarkName, pLandmarkName, sizeof( GameState->landmarkName ));
		GameState->loadGame = true;
	}
	else
	{
		GameState->landmarkName[0] = 0;
		GameState->loadGame = false;
	}

	Host_SetNextState( STATE_CHANGELEVEL );
	GameState->newGame = false;
}

static void Host_ShutdownGame( void )
{
	SV_ShutdownGame();

	switch( GameState->nextstate )
	{
	case STATE_LOAD_GAME:
	case STATE_LOAD_LEVEL:
		Host_SetState( GameState->nextstate, true );
		break;
	default:
		Host_SetState( STATE_RUNFRAME, true );
		break;
	}
}

static void Host_RunFrame( float time )
{
	// at this time, we don't need to get events from OS on dedicated
#if !XASH_DEDICATED
	Platform_RunEvents();
#endif // XASH_DEDICATED

	// engine main frame
	Host_Frame( time );

	switch( GameState->nextstate )
	{
	case STATE_RUNFRAME:
		break;
	case STATE_LOAD_GAME:
	case STATE_LOAD_LEVEL:
		SCR_BeginLoadingPlaque( GameState->backgroundMap );
		// intentionally fallthrough
	case STATE_GAME_SHUTDOWN:
		Host_SetState( STATE_GAME_SHUTDOWN, false );
		break;
	case STATE_CHANGELEVEL:
		SCR_BeginLoadingPlaque( GameState->backgroundMap );
		Host_SetState( GameState->nextstate, true );
		break;
	default:
		Host_SetState( STATE_RUNFRAME, true );
		break;
	}
}

void COM_Frame( float time )
{
	int	loopCount = 0;

	if( setjmp( host.abortframe ))
		return;

	while( 1 )
	{
		int	oldState = GameState->curstate;

		// execute the current state (and transition to the next state if not in STATE_RUNFRAME)
		switch( GameState->curstate )
		{
		case STATE_LOAD_LEVEL:
			SV_ExecLoadLevel();
			Host_SetState( STATE_RUNFRAME, true );
			break;
		case STATE_LOAD_GAME:
			SV_ExecLoadGame();
			Host_SetState( STATE_RUNFRAME, true );
			break;
		case STATE_CHANGELEVEL:
			SV_ExecChangeLevel();
			Host_SetState( STATE_RUNFRAME, true );
			break;
		case STATE_RUNFRAME:
			Host_RunFrame( time );
			break;
		case STATE_GAME_SHUTDOWN:
			Host_ShutdownGame();
			break;
		}

		if( oldState == STATE_RUNFRAME )
			break;

		if(( GameState->curstate == oldState ) || ( ++loopCount > 8 ))
			Sys_Error( "state infinity loop!\n" );
	}
}
