#include common_scripts\utility;
#include maps\_utility;
#include maps\_anim;
#include maps\_stealth_utility;
#include maps\_stealth_shared_utilities;

stealth_visibility_system_main()
{
	system_init();

	thread system_message_loop();
	array_thread( getentarray( "stealth_clipbrush", "targetname" ), ::system_handle_clipbrush );
}

/************************************************************************************************************/
/*												SYSTEM LOGIC												*/
/************************************************************************************************************/
system_message_loop()
{
	flag = "_stealth_spotted";

	while ( 1 )
	{
		flag_wait( "_stealth_enabled" );

		flag_wait( flag );

		if ( !flag( "_stealth_enabled" ) )
			continue;

		system_event_change( "spotted" );

		thread system_state_spotted();

		flag_waitopen( flag );

		if ( !flag( "_stealth_enabled" ) )
			continue;

		system_event_change( "hidden" );

		//make sure everything gets a notify and does what it needs to do 
		//before possibly being reset within the same frame back to spotted
		waittillframeend;
	}
}

//this function basically sets the ai event distance handlers based on the global awareness of ai...
system_event_change( name )
{
	level._stealth.logic.detection_level = name;

	foreach ( key, event in level._stealth.logic.ai_event )
	{
		setsaveddvar( key, event[ name ] );
		
		// Set ai_busyEvent* dvars too for now
		busyEventDvarName = "ai_busyEvent" + getsubstr( key, 8 ); // strlen( "ai_event" ) = 8
		setsaveddvar( busyEventDvarName, event[ name ] );
	}
}

//if system specific settings need to be made for this state...they go there
system_state_spotted()
{
	while ( flag( "_stealth_spotted" ) )
	{
		flag_wait( "_stealth_enabled" );

		array = level._stealth.group.groups;

		foreach ( group_name, group in array )
		{
			//is this group spotted?
			_flag = group_get_flagname_from_group( "_stealth_spotted", group_name );
			if ( !flag( _flag ) )
				continue;

			thread system_state_try_clear_flag( group_name );
		}
		//the most the last function will take is 1 second to complete...so lets wait a little longer 
		flag_waitopen_or_timeout( "_stealth_spotted", 1.25 );
	}
}

system_state_try_clear_flag( group_name )
{
	clear = system_state_check_no_enemy( group_name );

	if ( !clear )
		return;
	//basically if everyone lost their enemy...then we're back to hidden
	//there might be guys still looking so give them 1 second and check again
	wait 1;

	clear = system_state_check_no_enemy( group_name );

	if ( !clear )
		return;
	//so if we got here, then we passed the second test, if that's the case, then clear the flag
	group_flag_clear( "_stealth_spotted", group_name );
}

system_state_check_no_enemy( group_name )
{
	group = group_get_ai_in_group( group_name );

	foreach ( key, ai in group )
	{
		if ( !isalive( ai.enemy ) )
			continue;

		return false;
	}

	return true;
}

/************************************************************************************************************/
/*												PLAYER LOGIC												*/
/************************************************************************************************************/
system_save_processes()
{
	flag_init( "_stealth_player_nade" );
	level._stealth.logic.player_nades = 0;
	
	array_thread( level._players, ::player_grenade_check );
}

player_grenade_check()
{
	while ( 1 )
	{
		//this one hit's as soon as the button is pressed - that's why we want
		//to set the flag here and not after the grenade has left the hand
		//with "grenade fire" 
		self waittill( "grenade_pullback" );
		flag_set( "_stealth_player_nade" );

		self waittill( "grenade_fire", grenade );
		thread player_grenade_check_dieout( grenade );
	}
}

player_grenade_check_dieout( grenade )
{
	level._stealth.logic.player_nades++ ;
	grenade waittill_notify_or_timeout( "death", 10 );
	level._stealth.logic.player_nades -- ;

	//give stealth a chance to notify of any issues because of the grenade before we clear the flag
	//so that the system checking for saving the game can verify those notifies first
	waittillframeend;

	if ( !level._stealth.logic.player_nades )
		flag_clear( "_stealth_player_nade" );
}

system_init_shadows()
{
	array_thread( getentarray( "_stealth_shadow", "targetname" ), ::stealth_shadow_volumes );
	array_thread( getentarray( "stealth_shadow", "targetname" ), ::stealth_shadow_volumes );
}

stealth_shadow_volumes()
{
	self endon( "death" );// it can be deleted

	while ( 1 )
	{
		self waittill( "trigger", other );

		if ( !isalive( other ) )
			continue;

		if ( other ent_flag( "_stealth_in_shadow" ) )
			continue;

		other thread stealth_shadow_ai_in_volume( self );
	}
}

stealth_shadow_ai_in_volume( volume )
{
	self endon( "death" );

	self ent_flag_set( "_stealth_in_shadow" );

	while ( self istouching( volume ) )
		wait .05;

	self ent_flag_clear( "_stealth_in_shadow" );
}

/************************************************************************************************************/
/*											CLIP BRUSH LOGIC												*/
/************************************************************************************************************/
system_handle_clipbrush()
{
	self endon( "death" );

	if ( isdefined( self.script_flag_wait ) )
		flag_wait( self.script_flag_wait );

	waittillframeend;

	spotted_flag = "_stealth_spotted";
	corpse_flag = "_stealth_found_corpse";
	event_flag = "_stealth_event";

	if ( isdefined( self.script_stealthgroup ) )
	{
		group_wait_group_spawned( string( self.script_stealthgroup ) );

		spotted_flag = group_get_flagname_from_group( spotted_flag, self.script_stealthgroup );
		corpse_flag = group_get_flagname_from_group( corpse_flag, self.script_stealthgroup );
		event_flag = group_get_flagname_from_group( event_flag, self.script_stealthgroup );
	}

	self setcandamage( true );

	self add_wait( ::waittill_msg, "damage" );
	level add_wait( ::flag_wait, spotted_flag );
	level add_wait( ::flag_wait, corpse_flag );
	level add_wait( ::flag_wait, event_flag );
	do_wait_any();

	if ( self.spawnflags & 1 )
		self connectpaths();

	self delete();
}

/************************************************************************************************************/
/*													SETUP													*/
/************************************************************************************************************/
system_init()
{
	flag_init( "_stealth_spotted" );
	flag_init( "_stealth_event" );
	flag_init( "_stealth_enabled" );
	flag_set( "_stealth_enabled" );

	 /#
		thread stealth_flag_debug_print( "_stealth_spotted" );
	#/
	//under stealth we have a logic struct and a behavior struct...the behavior struct is created and
	//handled in the _stealth_behavior system OR in the designers own script
	level._stealth = spawnstruct();
	level._stealth.logic = spawnstruct();
	level._stealth.group = spawnstruct();
	level._stealth.group.flags = [];
	level._stealth.group.groups = [];

	//friendly and player detection initilization
	level._stealth.logic.detection_level = "hidden";
	level._stealth.logic.detect_range = [];
	level._stealth.logic.detect_range[ "hidden" ] = [];
	level._stealth.logic.detect_range[ "spotted" ] = [];
	system_default_detect_ranges();

	//these are event handlers...they're already running in the game normally, but with these numbers we can
	//tweak how well they AI can detect these events...for stealth gameplay we bring the numbers for 
	//footsteps, death of a teammate, etc, etc rediculously lower than normal COD gameplay
	level._stealth.logic.ai_event = [];

	level._stealth.logic.ai_event[ "ai_eventDistDeath" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistPain" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistExplosion" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistBullet" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistFootstep" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistFootstepWalk" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistFootstepSprint" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistGunShot" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistGunShotTeam" ] = [];
	level._stealth.logic.ai_event[ "ai_eventDistNewEnemy" ] = [];
	system_default_event_distances();

	system_event_change( "hidden" );

	system_save_processes();
	system_init_shadows();
	
	stealth_alert_level_duration( 0.5 );
}

/************************************************************************************************************/
/*												UTILITIES													*/
/************************************************************************************************************/
system_default_detect_ranges()
{
	//these values represent the BASE huristic for max visible distance base meaning 
	//when the character is completely still and not turning or moving
	//HIDDEN is self explanatory
	hidden = [];
	hidden[ "prone" ]	 = 70;
	hidden[ "crouch" ]	 = 600;
	hidden[ "stand" ]	 = 1024;

	//SPOTTED is when they are completely aware and go into NORMAL COD AI mode...however, the
	//distance they can see you is still limited by these numbers because of the assumption that
	//you're wearing a ghillie suit in woodsy areas
	spotted = [];
	spotted[ "prone" ]	 = 512;
	spotted[ "crouch" ]	 = 5000;
	spotted[ "stand" ]	 = 8000;

	system_set_detect_ranges( hidden, spotted );
}

system_set_detect_ranges( hidden, spotted )
{
	//these values represent the BASE huristic for max visible distance base meaning 
	//when the character is completely still and not turning or moving

	//HIDDEN is self explanatory
	if ( isdefined( hidden ) )
	{
		level._stealth.logic.detect_range[ "hidden" ][ "prone" ]	 = hidden[ "prone" ];
		level._stealth.logic.detect_range[ "hidden" ][ "crouch" ]	 = hidden[ "crouch" ];
		level._stealth.logic.detect_range[ "hidden" ][ "stand" ]	 = hidden[ "stand" ];
	}
	//SPOTTED is when they are completely aware and go into NORMAL COD AI mode...however, the
	//distance they can see you is still limited by these numbers because of the assumption that
	//you're wearing a ghillie suit in woodsy areas
	if ( isdefined( spotted ) )
	{
		level._stealth.logic.detect_range[ "spotted" ][ "prone" ]	 = spotted[ "prone" ];
		level._stealth.logic.detect_range[ "spotted" ][ "crouch" ]	 = spotted[ "crouch" ];
		level._stealth.logic.detect_range[ "spotted" ][ "stand" ]	 = spotted[ "stand" ];
	}
}

system_default_event_distances()
{
	ai_event[ "ai_eventDistDeath" ] 		 = [];
	ai_event[ "ai_eventDistPain" ] 			 = [];
	ai_event[ "ai_eventDistExplosion" ] 	 = [];
	ai_event[ "ai_eventDistBullet" ] 		 = [];
	ai_event[ "ai_eventDistFootstep" ] 		 = [];
	ai_event[ "ai_eventDistFootstepWalk" ] 	 = [];
	ai_event[ "ai_eventDistFootstepSprint" ] = [];
	ai_event[ "ai_eventDistGunShot" ] 		 = [];
	ai_event[ "ai_eventDistGunShotTeam" ]	 = [];
	ai_event[ "ai_eventDistNewEnemy" ] 		 = [];

	ai_event[ "ai_eventDistDeath" ][ "spotted" ] 		 = getdvar( "ai_eventDistDeath" );// 1024
	ai_event[ "ai_eventDistDeath" ][ "hidden" ] 		 = 512; // used to be 256

	ai_event[ "ai_eventDistPain" ][ "spotted" ] 		 = getdvar( "ai_eventDistPain" );// 512
	ai_event[ "ai_eventDistPain" ][ "hidden" ] 		 = 256; // used to be 256

	ai_event[ "ai_eventDistExplosion" ][ "spotted" ]	 = 4000;
	ai_event[ "ai_eventDistExplosion" ][ "hidden" ] 	 = 4000;

	ai_event[ "ai_eventDistBullet" ][ "spotted" ]		 = 96;// getdvar( "ai_eventDistBullet" );// 96
	ai_event[ "ai_eventDistBullet" ][ "hidden" ] 		 = 64;

	ai_event[ "ai_eventDistFootstep" ][ "spotted" ]		 = 350;// getdvar( "ai_eventDistFootstep" );// 512
	ai_event[ "ai_eventDistFootstep" ][ "hidden" ] 	 = 64;

	ai_event[ "ai_eventDistFootstepWalk" ][ "spotted" ]	 = 256;// getdvar( "ai_eventDistFootstepWalk" );// 256
	ai_event[ "ai_eventDistFootstepWalk" ][ "hidden" ] = 32;
	
	ai_event[ "ai_eventDistFootstepSprint" ][ "spotted" ]	= 400;// getdvar( "ai_eventDistFootstepSprint" );// 400
	ai_event[ "ai_eventDistFootstepSprint" ][ "hidden" ] = 400;
	
	ai_event[ "ai_eventDistGunShot" ][ "spotted" ]		 = 2048;
	ai_event[ "ai_eventDistGunShot" ][ "hidden" ] 		 = 2048;
		
	//added these ones when I added stealth groups...
	//want to make it harder for 2 groups to hear eachother's gunshots
	ai_event[ "ai_eventDistGunShotTeam" ][ "spotted" ]		 = 750;	// 2048
	ai_event[ "ai_eventDistGunShotTeam" ][ "hidden" ] 		 = 750;
	
	//want to make it harder for 2 groups to give eachother info
	ai_event[ "ai_eventDistNewEnemy" ][ "spotted" ]		 = 750;	// 1024
	ai_event[ "ai_eventDistNewEnemy" ][ "hidden" ] 	 = 750;

	system_set_event_distances( ai_event );
}

system_set_event_distances( array )
{
	foreach ( event, event_array in array )
	{
		foreach ( state, value in event_array )
		{
			level._stealth.logic.ai_event[ event ][ state ] = value;
		}
	}
}