if( TestMap ) { return; }
// Store it neatly under a global table.
global TestMap = table();

///////////////////////////////////////////////////////////////////////////////
TestMap.Debug = false;
TestMap.IgnoreFlag = false;
TestMap.DoObj = true;
TestMap.DoSnipe = false;
TestMap.DoMG42 = false;
TestMap.DoSentry = false;
TestMap.ModName = GetModName();
TestMap.statusstring = "";


///////////////////////////////////////////////////////////////////////////////

TestMap.ParseParams = function( _params )
{
	if ( !Map )
	{
		TestMap.DoObj = true;
		TestMap.DoSnipe = false;
		TestMap.DoMG42 = false;
		TestMap.DoSentry = false;

	}
	else
	{
		if ( IsNull( Map.DoObj ) )
			{ TestMap.DoObj = true; }

		if ( IsNull( Map.DoSnipe ) )
			{ TestMap.DoSnipe = false; }

		if ( IsNull( Map.DoMG42 ) )
			{ TestMap.DoMG42 = false; }

		if ( IsNull( Map.DoSentry ) )
			{ TestMap.DoSentry = false; }
	}

	foreach ( param in _params )
	{
		switch (param)
		{
			case "obj":
			{
				TestMap.DoObj = true;
			}
			case "noobj":
			{
				TestMap.DoObj = false;
			}
			case "snipe":
			{
				TestMap.DoSnipe = true;
			}
			case "nosnipe":
			{
				TestMap.DoSnipe = false;
			}
			case "mg42":
			{
				TestMap.DoMG42 = true;
			}
			case "nomg42":
			{
				TestMap.DoMG42 = false;
			}
			case "sentry":
			{
				TestMap.DoSentry = true;
			}
			case "nosentry":
			{
				TestMap.DoSentry = false;
			}
			default:
			{
				fileparam = param;
			}
		}
	}

	if ( fileparam )
	{
		TestMap.testname = fileparam.GetFilenameNoExt();
	}
	else
	{
		if ( GetGameName() == "RTCW-MP" && GetGameType() == 7 )
			{ MapName = GetMapName() + "_cp"; }
		else
			{ MapName = GetMapName(); }

		TestMap.testname = MapName + "_test";
	}

	TestMap.filename = "testmap/" + TestMap.testname +".gm";
	TestMap.logname = TestMap.testname + ".log";
};

///////////////////////////////////////////////////////////////////////////////

TestMap.RunTests = function( _params )
{
	TestMap.ParseParams( _params );

	if ( !ExecScript( TestMap.filename ) )
	{
		print( "ERROR: Can't find test:", TestMap.testname );
		return;
	}

	if ( Map.MapName && Map.MapName != GetMapName() )
	{
		errorstr = "ERROR: Test is for map " + Map.MapName;
		print( errorstr );
		EchoToScreen( 3, errorstr );
		return;
	}

	if ( Map.Scripts )
	{
		numscripts = tableCount( Map.Scripts );
		numlogfilesfound = 0;

		foreach ( script in Map.Scripts )
		{
			if ( System.FileExists( script + ".log" ) )
				{ ++numlogfilesfound; }
		}

		if ( numscripts == numlogfilesfound || numlogfilesfound == 0 )
		{
			if ( numscripts == numlogfilesfound )
			{
				foreach ( script in Map.Scripts )
				{
					System.FileDelete( script + ".log" );
				}
			}

			gmfilename = "testmap/" + Map.Scripts[ 0 ] +".gm";
			TestMap.testname = Map.Scripts[ 0 ];
		}
		else
		{
			foreach ( script in Map.Scripts )
			{
				if ( !System.FileExists( script + ".log" ) )
				{
					gmfilename = "testmap/" + script +".gm";
					TestMap.testname = script;
					break;
				}
			}
		}

		if ( ExecScript( gmfilename ) )
		{
			TestMap.filename = gmfilename;
			TestMap.logname = TestMap.testname + ".log";
		}
	}

	TestMap.LogFile = File();
	assert( TestMap.LogFile.Open( TestMap.logname, "text", false ) );

	TestMap.Output( "Running " + TestMap.testname );

	ServerCommand( Util.NoTimeLimit );

	Map.Tests();
	TestMap.Output( "Finished " + TestMap.testname );
	TestMap.LogFile.Close();

	if ( TestMap.statusstring.Left( 8 ) == " failed " )
		{ ServerCommand( Util.TimeLimit1 ); }
};

///////////////////////////////////////////////////////////////////////////////

TestMap.GetBot = function()
{
	foreach (bot in BotTable)
	{
		return bot;
	}
};

///////////////////////////////////////////////////////////////////////////////

TestMap.RunTest = function( team, class, goal, timeout, kickbots, delay )
{
	mapgoal = false;
	mountgoal = false;
	atgoal = false;
	switchgoal = false;
	movergoal = false;
	targetgoal = false;
	repairgoal = false;
	sentrygoal = false;
	mobilemg42goal = false;

	if ( IsString(goal) )
	{
		goalname = goal;
		mapgoal = true;

		if ( goal.Left( 9 ) == "MOUNTMG42" )
			{ mountgoal = true; }

		if ( goal.Left( 5 ) == "SNIPE" )
			{ atgoal = true; }

		if ( goal.Left( 10 ) == "REPAIRMG42" )
		{
			repairgoal = true;
			mountname = "MOUNTMG42" +  goal.RightAt( 10 );
		}

		if ( goal.Left( 6 ) =="SENTRY" )
			{ sentrygoal = true; }

		if ( goal.Left( 10 ) == "MOBILEMG42" )
			{ mobilemg42goal = true; }
	}
	else if ( IsTable(goal) )
	{
		if ( IsString( goal.WaypointName ) )
		{
			goalname = goal.WaypointName;
			switchgoal = true;
		}

		if ( IsString( goal.RideVehicleGoalName )  )
		{
			goalname = goal.RideVehicleGoalName;
			movergoal = true;
		}

		if ( IsString( goal.GrenadeTargetGoalName ) )
		{
			goalname = goal.GrenadeTargetGoalName;
			targetgoal = true;
		}
	}

	if ( TestMap.Debug )
		{ print("Processing goal:", goalname ); }


	if ( IsNull( delay ) )
		{ delay = 0; }

	teamname = Util.TeamNames[ team ][ 0 ];
	name = Util.BotNames[ team][ class ];

	numbots = tableCount( BotTable );

	if ( numbots == 0 || numbots > 1 )
	{
		if ( TestMap.Debug && numbots == 0 )
			{ print("No bots, add one"); }

		if ( numbots > 1 )
		{
			if ( TestMap.Debug )
				{ print("More than 1 bot, kick all and add a bot"); }

			KickAll();
			sleep( 2 );
		}

		bot = Util.AddBotTester( team, class, name );
		ServerCommand( "bot debugbot all fpinfo" );
	}
	else
	{
		if ( TestMap.Debug )
			{ print("Have one bot, check if right one");}

		bot = TestMap.GetBot();

		if ( class != bot.GetClass() || bot.Name != name )
		{
			if ( TestMap.Debug )
			{
				print("Wrong bot:", bot.GetTeam(), bot.GetClass(), bot.Name );
				print("kick all and add a bot");
			}

			KickAll();
			sleep( 2 );
			bot = Util.AddBotTester( team, class, name );
			ServerCommand( "bot debugbot all fpinfo" );
		}
	}

	if ( TestMap.Debug )
		{ print("Setting goal:", goal); }

	if ( mapgoal )
		{ SetAvailableMapGoals( team, true, goal ); }

	if ( switchgoal || movergoal || targetgoal )
	{
		goal.Enabled = true;
		goal.LimitTeam = ( 1 << team );
		goal.LimitClass = ( 1 << class);
	}

	timeitthread = thread( TestMap.Timeit, timeout );

	if ( mountgoal )
		{ mountitthread = thread( TestMap.Mountit, goal ); }

	if ( atgoal )
		{ atitthread = thread( TestMap.Atit, goal ); }

	if ( targetgoal )
		{ targetthread = thread( TestMap.Target, goalname ); }

	if ( repairgoal )
		{ repairthread = thread( TestMap.Repair, mountname ); }

	if ( sentrygoal )
		{ sentrythread = thread( TestMap.BuildSentry, goalname ); }

	if ( mobilemg42goal )
		{ mobilemg42thread = thread( TestMap.SetMobileMG42, goalname ); }

	if ( TestMap.Debug )
		{ print("Waiting for goal:", goalname); }

	if ( block( "pass", "fail" ) == "pass" )
		{ TestMap.statusstring = " passed "; }
	else
		{ TestMap.statusstring = " failed "; }

	threadKill( timeitthread );

	if ( TestMap.Debug )
		{ print("goal:", goalname, TestMap.statusstring); }

	if ( mountgoal )
		{ threadKill( mountitthread ); }

	if ( atgoal )
		{ threadKill( atitthread ); }

	if ( targetgoal )
	{
		threadKill( targetthread );
		goalname = "GrenadeTarget " + goalname;
	}

	if ( repairgoal )
		{ threadKill( repairthread ); }

	if ( sentrygoal )
		{ threadKill( sentrythread ); }

	if ( mobilemg42goal )
		{ threadKill( mobilemg42thread ); }

	TestMap.Output( teamname + " " + goalname + TestMap.statusstring +
		( TestMap.Time / 1000 ) );

	if ( TestMap.Debug )
		{ print("Clearing goal:", goalname); }

	if ( mapgoal )
		{ SetAvailableMapGoals( team, false, goal ); }

	if ( switchgoal || movergoal || targetgoal)
	{
		goal.Enabled = false;
		goal.LimitTeam = 0;
		goal.LimitClass = 0;
	}

	if ( kickbots )
	{
		if ( TestMap.Debug )
			{ print("Kicking bot for goal:", goal); }

		KickAll();
	}

	if ( delay > 0 )
		{ sleep( delay ); }
};

///////////////////////////////////////////////////////////////////////////////

TestMap.RunTest2 = function( team, classtable, goaltable, timeout, kickbots, delay )
{
	if ( IsNull( delay ) )
		{ delay = 0; }

	lastgoal = goaltable[ tableCount( goaltable ) - 1 ];

	mountgoal = false;

	foreach ( class in classtable )
	{
		teamname = Util.TeamNames[ team ][ 0 ];
		name = Util.BotNames[ team][ class ];

		bot = Util.GetBotByName( name );

		if ( bot && bot.GetTeam() == team and bot.GetClass() == class )
			{ continue; }

		Util.AddBotTester( team, class, name );
	}

	ServerCommand( "bot debugbot all fpinfo" );

	if ( lastgoal.Left( 9 ) == "MOUNTMG42" )
		{ mountgoal = true; }

	if ( lastgoal.Left( 5 ) == "SNIPE" )
		{ atgoal = true; }

	foreach ( goal in goaltable )
		{ SetAvailableMapGoals( team, true, goal ); }

	timeitthread = thread( TestMap.Timeit, timeout );

	if ( mountgoal )
		{ mountitthread = thread( TestMap.Mountit, lastgoal ); }

	if ( atgoal )
		{ atitthread = thread( TestMap.Atit, goal ); }

	if ( block( "pass", "fail" ) == "pass" )
		{ TestMap.statusstring = " passed "; }
	else
		{ TestMap.statusstring = " failed "; }

	threadKill( timeitthread );

	if ( mountgoal )
		{ threadKill( mountitthread ); }

	if ( atgoal )
		{ threadKill( atitthread ); }

	TestMap.Output( teamname + " " + lastgoal + TestMap.statusstring +
		( TestMap.Time / 1000 ) );

	foreach ( goal in goaltable )
		{ SetAvailableMapGoals( team, false, goal ); }

	if ( kickbots )
	{
		KickAll();
		sleep( 2 );
	}

	if ( delay > 0 )
		{ sleep( delay ); }
};

///////////////////////////////////////////////////////////////////////////////

TestMap.Output = function( params )
{
	print( params );
	assert( TestMap.LogFile.Write( params, System.NewLine ) );
};

///////////////////////////////////////////////////////////////////////////////

TestMap.Timeit = function( timeout )
{
	TestMap.Time = 0;
	endtime = timeout * 1000;

	for(;;)
	{
		TestMap.Time += 50;

		if ( TestMap.Time == endtime )
		{
			TestMap.Fail();
			break;
		}

		yield();
	}
};

///////////////////////////////////////////////////////////////////////////////

TestMap.Mountit = function( goal )
{
	bot = TestMap.GetBot();

	while ( !GetEntFlags(bot.GetGameEntity(), ENTFLAG.MNT_MG42) )
		{ yield(); }

	sleep( 2 );
	TestMap.Pass();
};

///////////////////////////////////////////////////////////////////////////////

TestMap.Atit = function( goal )
{
	bot = TestMap.GetBot();
	timeAtSpot = 0;
	goalPos = Util.GetMapGoalPosition( goal );

	for(;;)
	{
		distance = bot.DistanceTo( goalPos );
		//~ print("distance:", distance);

		if ( distance <= 64 )
		{
			++timeAtSpot;

			if ( timeAtSpot > 5 )
				{ break; }
		}
		else
			{ timeAtSpot = 0; }

		sleep( 1 );
	}

	sleep( 2 );
	TestMap.Pass();
};

///////////////////////////////////////////////////////////////////////////////

TestMap.Target = function( goalname )
{
	ent = GetGoal( goalname ).GetEntity();

	while ( !GetEntFlags(ent, ENTFLAG.DEAD) )
		{ yield(); }

	TestMap.Pass();
};

///////////////////////////////////////////////////////////////////////////////

TestMap.Repair = function( goalname )
{
	ent = GetGoal( goalname ).GetEntity();

	while ( Util.GetEntHealth(ent) < 100)
	{
		yield();
	}

	TestMap.Pass();
};

///////////////////////////////////////////////////////////////////////////////

TestMap.BuildSentry = function( goalname )
{
	bot = TestMap.GetBot();

	while ( bot.GetSentry() == null )
		{ yield(); }

	while ( bot.GetSentryLevel() < 3)
		{ yield(); }

	sleep( 2 );
	TestMap.Pass();
};

///////////////////////////////////////////////////////////////////////////////

TestMap.SetMobileMG42 = function( goalname )
{
	bot = TestMap.GetBot();

	while ( bot.GetCurrentWeapon() != WEAPON.MOBILE_MG42_SET )
		{ yield(); }

	sleep( 4 );
	TestMap.Pass();
};

///////////////////////////////////////////////////////////////////////////////

TestMap.AtPosition = function( goalname, signalname, tolerance )
{
	thread( TestMap.AtPositionThread, goalname, signalname, tolerance );
};

///////////////////////////////////////////////////////////////////////////////

TestMap.AtPositionThread = function( goalname, signalname, tolerance )
{
	Util.OnTriggerPosition( goalname, signalname, tolerance );
	block( signalname );
	TestMap.Pass();
};

///////////////////////////////////////////////////////////////////////////////

TestMap.OnFlag = function(trigger)
{
	if ( TestMap.IgnoreFlag ) { return; }

	a = trigger.Action;
	if(a == "item_pickedup" || a == "pickedup" || a == "item_returned" || a == "returned")
		{ TestMap.Pass(); }
};

///////////////////////////////////////////////////////////////////////////////

TestMap.OnCPFlag = function(trigger)
{
	if ( TestMap.IgnoreFlag ) { return; }

	a = trigger.Action;
	if(a == "item_pickedup" || a == "pickedup" || a == "item_removed" || a == "returned")
		{ TestMap.Pass(); }
};

///////////////////////////////////////////////////////////////////////////////

TestMap.Pass = function()
{
	signal( "pass" );
};

///////////////////////////////////////////////////////////////////////////////

TestMap.Fail = function()
{
	signal( "fail" );
};
