//-----------------------------------------------------------------------------
//
//  $Logfile:: /game/gamescript.cpp                                           $
// $Revision:: 2                                                              $
//   $Author:: Burgermac                                                      $
//     $Date:: 12/27/00 7:08 PM                                               $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// Subclass of script that preprocesses labels
//

#include "g_local.h"
#include "script.h"
#include "gamescript.h"

ScriptLibrarian ScriptLib;

CLASS_DECLARATION( Class, GameScriptMarker, NULL );

ResponseDef GameScriptMarker::Responses[] =
{
	{ NULL, NULL }
};

CLASS_DECLARATION( Class, ScriptLibrarian, NULL );

ResponseDef ScriptLibrarian::Responses[] =
{
	{ NULL, NULL }
};

ScriptLibrarian::~ScriptLibrarian()
{
	int i;
	int num;

	num = scripts.NumObjects();
	for( i = 1; i <= num; i++ )
	{
		delete scripts.ObjectAt( i );
	}
}

void ScriptLibrarian::CloseScripts( void )
{
	int i;
	int num;
	GameScript* scr;

	// Clear out the game and dialog scripts
	SetGameScript( "" );
	SetDialogScript( "" );

	num = scripts.NumObjects();
	for( i = num; i > 0; i-- )
	{
		scr = scripts.ObjectAt( i );
		scripts.RemoveObjectAt( i );
		delete scr;
	}
}

void ScriptLibrarian::SetDialogScript( str scriptname )
{
	dialog_script = scriptname;
}

void ScriptLibrarian::SetGameScript( str scriptname )
{
	game_script = scriptname;
}

const char* ScriptLibrarian::GetGameScript( void )
{
	return game_script.c_str();
}

GameScript* ScriptLibrarian::FindScript( const char* name )
{
	int i;
	int num;
	GameScript* scr;
	str n;

	// Convert all forward slashes to back slashes
	n = G_FixSlashes( name );

	num = scripts.NumObjects();
	for( i = 1; i <= num; i++ )
	{
		scr = scripts.ObjectAt( i );

		if( scr->Filename() == n )
		{
			return scr;
		}
	}

	return NULL;
}

GameScript* ScriptLibrarian::GetScript( const char* name )
{
	GameScript* scr;
	str n;

	n = G_FixSlashes( name );
	scr = FindScript( n.c_str() );
	if( !scr && ( gi.LoadFile( name, NULL, 0 ) != -1 ) )
	{
		scr = new GameScript();
		scr->LoadFile( n.c_str() );
		scripts.AddObject( scr );
	}

	return scr;
}

qboolean ScriptLibrarian::Goto( GameScript* scr, const char* name )
{
	const char* p;
	GameScript* s;
	const char* ScriptName;
	qboolean Result;

	p = strstr( name, "::" );
	if( !p )
	{
		return scr->Goto( name );
	}
	str n( name, 0, p - name );
	if( !strcmp( n.c_str(), "dialog" ) )
	{
		ScriptName = dialog_script.c_str();
	}
	else
	{
		ScriptName = n.c_str();
	}
	s = GetScript( ScriptName );
	Result = false;
	if( s )
	{
		p += 2;
		if( s->labelExists( p ) )
		{
			scr->SetSourceScript( s );
			Result = scr->Goto( p );
		}
	}
	return Result;
}

qboolean ScriptLibrarian::labelExists( GameScript* scr, const char* name )
{
	const char* p;
	GameScript* s;
	const char* ScriptName;
	qboolean Result;

	p = strstr( name, "::" );
	if( !p )
	{
		return scr->labelExists( name );
	}
	str n( name, 0, p - name );
	if( !strcmp( n.c_str(), "dialog" ) )
	{
		ScriptName = dialog_script.c_str();
	}
	else
	{
		ScriptName = n.c_str();
	}
	s = GetScript( ScriptName );
	Result = false;
	if( s )
	{
		p += 2;
		Result = s->labelExists( p );
	}
	return Result;
}

CLASS_DECLARATION( Script, GameScript, NULL );

ResponseDef GameScript::Responses[] =
{
	{ NULL, NULL }
};

GameScript::GameScript()
{
	sourcescript = this;
	labelList = NULL;
	crc = 0;
}

GameScript::GameScript( GameScript* scr )
{
	crc = 0;
	labelList = NULL;
	SetSourceScript( scr );
}

GameScript::~GameScript()
{
	Close();
}

void GameScript::Close( void )
{
	FreeLabels();
	Script::Close();
	sourcescript = this;
	crc = 0;
}

void GameScript::SetSourceScript( GameScript* scr )
{
	if( scr != this )
	{
		Close();

		sourcescript = scr->sourcescript;
		crc = sourcescript->crc;
		Parse( scr->buffer, scr->length, scr->Filename() );
	}
}

void GameScript::FreeLabels( void )
{
	int i;
	int num;

	if( labelList )
	{
		num = labelList->NumObjects();
		for( i = 1; i <= num; i++ )
		{
			delete labelList->ObjectAt( i );
		}

		delete labelList;
	}

	labelList = NULL;
}

void GameScript::LoadFile( const char* name )
{
	str n;

	// Convert all forward slashes to back slashes
	n = name;
	G_FixSlashes( n.c_str() );

	sourcescript = this;
	Script::LoadFile( n.c_str() );
	FindLabels();

	crc = gi.CalcCRC( ( unsigned char* )buffer, length );
}

void GameScript::FindLabels( void )
{
	scriptmarker_t mark;
	const char*     tok;
	script_label_t* label;
	int len;

	FreeLabels();

	labelList = new Container<script_label_t*>;

	MarkPosition( &mark );

	Reset();

	while( TokenAvailable( true ) )
	{
		tok = GetToken( true );
		// see if it is a label
		if( tok )
		{
			len = (int)strlen( tok );
			if( len && tok[ len - 1 ] == ':' )
			{
				if( !labelExists( tok ) )
				{
					label = new script_label_t;
					MarkPosition( &label->pos );
					label->labelname = tok;
					labelList->AddObject( label );
				}
				else
				{
					warning( "FindLabels", "Duplicate labels %s\n", tok );
				}
			}
		}
	}

	RestorePosition( &mark );
}

qboolean GameScript::labelExists( const char* name )
{
	str labelname;
	script_label_t* label;
	int i;
	int num;

	if( !sourcescript->labelList )
	{
		return false;
	}

	labelname = name;
	if( !labelname.length() )
	{
		return false;
	}

	if( labelname[ labelname.length() - 1 ] != ':' )
	{
		labelname += ":";
	}

	num = sourcescript->labelList->NumObjects();
	for( i = 1; i <= num; i++ )
	{
		label = sourcescript->labelList->ObjectAt( i );
		if( labelname == label->labelname )
		{
			return true;
		}
	}

	return false;
}

qboolean GameScript::Goto( const char* name )
{
	script_label_t* label;
	int i;
	int num;

	if( sourcescript->labelList )
	{
		str labelname( name );
		i = labelname.length();
		if( i )
		{
			if( labelname[i - 1] != ':' )
			{
				labelname += ":";
			}

			num = sourcescript->labelList->NumObjects();
			for( i = 1; i <= num; i++ )
			{
				label = sourcescript->labelList->ObjectAt( i );
				if( !strcmp( labelname.c_str(), label->labelname.c_str() ) )
				{
					RestorePosition( &label->pos );
					return true;
				}
			}
		}
	}
	return false;
}

void GameScript::Mark( GameScriptMarker* mark )
{
	assert( mark );
	assert( sourcescript );

	mark->filename = sourcescript->Filename();
	MarkPosition( &mark->scriptmarker );
}

void GameScript::Restore( GameScriptMarker* mark )
{
	// If we change this function, we must update the unarchive function as well
	GameScript* scr;

	assert( mark );

	scr = ScriptLib.FindScript( mark->filename.c_str() );
	if( scr )
	{
		SetSourceScript( scr );
	}
	else
	{
		LoadFile( mark->filename.c_str() );
	}

	RestorePosition( &mark->scriptmarker );
}
