if( TestMap == null )
{
	// 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 ( typeId( Map.DoObj ) == 0 )
				{ TestMap.DoObj = true; }

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

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

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

		foreach ( i and param in _params )
		{
			if ( param == "obj" )
			{
				TestMap.DoObj = true;
				continue;
			}

			if ( param == "noobj" )
			{
				TestMap.DoObj = false;
				continue;
			}

			if ( param == "snipe" )
			{
				TestMap.DoSnipe = true;
				continue;
			}

			if ( param == "nosnipe" )
			{
				TestMap.DoSnipe = false;
				continue;
			}

			if ( param == "mg42" )
			{
				TestMap.DoMG42 = true;
				continue;
			}

			if ( param == "nomg42" )
			{
				TestMap.DoMG42 = false;
				continue;
			}

			if ( param == "sentry" )
			{
				TestMap.DoSentry = true;
				continue;
			}

			if ( param == "nosentry" )
			{
				TestMap.DoSentry = false;
				continue;
			}

			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 ( index and script in Map.Scripts )
			{
				logfilename = script + ".log";

				if ( System.FileExists( logfilename ) )
					{ numlogfilesfound += 1; }
			}

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

				gmfilename = "testmap/" + Map.Scripts[ 0 ] +".gm";
				TestMap.testname = Map.Scripts[ 0 ];
			}
			else
			{
				foreach ( index and script in Map.Scripts )
				{
					logfilename = script + ".log";

					if ( !System.FileExists( logfilename ) )
					{
						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.Mid( 0, 8 ) == " failed " )
			{ ServerCommand( Util.TimeLimit1 ); }
	};

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

	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;

		goalvartype = typeId( goal );

		if ( goalvartype == 5 )
		{
			// Goal as a string
			goalname = goal;
			mapgoal = true;

			if ( goal.Mid( 0, 9 ) == "MOUNTMG42" )
				{ mountgoal = true; }

			if ( goal.Mid( 0, 5 ) == "SNIPE" )
				{ atgoal = true; }

			if ( goal.Mid( 0, 10 ) == "REPAIRMG42" )
			{
				repairgoal = true;
				length = goal.Length() - 10;
				mountname = "MOUNTMG42" +
					goal.Mid( 10, length );
			}

			if ( goal.Mid( 0, 6 ) =="SENTRY" )
				{ sentrygoal = true; }

			if ( goal.Mid( 0, 10 ) == "MOBILEMG42" )
				{ mobilemg42goal = true; }
		}
		else if ( goalvartype == 6 )
		{
			if ( typeId( goal.WaypointName ) == 5 )
			{
				goalname = goal.WaypointName;
				switchgoal = true;
			}

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

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

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


		if ( typeId( delay ) == 0 )
			{ 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");}

			foreach ( id and bot in BotTable )
			{
				if ( bot )
					{ break; }
			}

			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 ( typeId( delay ) == 0 )
			{ delay = 0; }

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

		mountgoal = false;

		foreach ( index and 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; }

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

		ServerCommand( "bot debugbot all fpinfo" );

		if ( lastgoal.Mid( 0, 9 ) == "MOUNTMG42" )
			{ mountgoal = true; }

		if ( lastgoal.Mid( 0, 5 ) == "SNIPE" )
			{ atgoal = true; }

		foreach ( index and 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 ( index and 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;

		while ( true )
		{
			TestMap.Time = TestMap.Time + 50;

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

			yield();
		}
	};

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

	TestMap.Mountit = function( goal )
	{
		foreach ( id and bot in BotTable )
		{
			if ( bot )
				{ break; }
		}

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

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

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

	TestMap.Atit = function( goal )
	{
		foreach ( id and bot in BotTable )
		{
			if ( bot )
				{ break; }
		}

		timeAtSpot = 0;
		goalPos = Util.GetMapGoalPosition( goal );

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

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

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

			sleep( 1 );
		}

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

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

	TestMap.Target = function( goalname )
	{
		foreach ( id and bot in BotTable )
		{
			if ( bot )
				{ break; }
		}

		goal = GetGoal( goalname );
		goalent = goal.GetEntity();

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

		TestMap.Pass();
	};

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

	TestMap.Repair = function( goalname )
	{
		foreach ( id and bot in BotTable )
		{
			if ( bot )
				{ break; }
		}

		goal = GetGoal( goalname );
		goalent = goal.GetEntity();
		goalHealthArmor = {};

		while ( goalHealthArmor.Health < 100)
		{
			GetEntHealthAndArmor(goalent, goalHealthArmor);
			yield();
		}

		TestMap.Pass();
	};

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

	TestMap.BuildSentry = function( goalname )
	{
		foreach ( id and bot in BotTable )
		{
			if ( bot )
				{ break; }
		}

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

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

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

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

	TestMap.SetMobileMG42 = function( goalname )
	{
		foreach ( id and bot in BotTable )
		{
			if ( bot )
				{ break; }
		}

		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; }

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

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

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

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

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

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

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

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

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

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