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

stealth_visibility_enemy_main()
{
	self enemy_init();

	self thread enemy_threat_logic();

}


stealth_visibility_miniuav_main()
{
	self enemy_init();

	self thread enemy_miniuav_threat_logic();

}

/************************************************************************************************************/
/*													ENEMY LOGIC												*/
/************************************************************************************************************/
MIN_TIME_TO_LOSE_ENEMY = 20 * 1000;

enemy_threat_logic()
{
	self endon( "death" );
	self endon( "pain_death" );

	while ( 1 )
	{
		self ent_flag_wait( "_stealth_enabled" );

		self waittill( "enemy" );

		if ( !self ent_flag( "_stealth_enabled" ) )
			continue;

		if ( !isalive( self.enemy ) )
			continue;

		if ( !self stealth_group_spotted_flag() )
		{
			if ( !self enemy_alert_level_logic( self.enemy ) )
				continue;
		}
		else
		{
			// if we hit this line it means we're not the first ones to find the enemy
			self maps\_stealth_threat_enemy::enemy_alert_level_change( "attack" );
		}

		self thread enemy_threat_set_spotted();

		//wait a minimum of 10 seconds before trying to lose your enemy
		wait 10;

		// must not have gotten any event from enemy for MIN_TIME_TO_LOSE_ENEMY and must be out of maxVisibleDist
		while ( isdefined( self.enemy ) && self ent_flag( "_stealth_enabled" ) )
		{
			time_past_last_event = gettime() - self lastKnownTime( self.enemy );
			
			if ( MIN_TIME_TO_LOSE_ENEMY > time_past_last_event )
			{
				wait ( ( MIN_TIME_TO_LOSE_ENEMY - time_past_last_event ) * 0.001 );
				continue;
			}
			
			if ( distance( self.origin, self.enemy.origin ) > self.enemy.maxVisibleDist )
				break;

			wait .5;
		}

		if ( !self ent_flag( "_stealth_enabled" ) )
			continue;
			
		//if we ever break out - if means everyone actually managed to hide...unbelievable
		if ( isdefined( self.enemy ) )
			enemy_alert_level_forget( self.enemy, 0 );
			
		self clearenemy();
		self maps\_stealth_threat_enemy::enemy_alert_level_change( "reset" );
	}
}

enemy_miniuav_threat_logic()
{
	self endon( "death" );

	while ( 1 )
	{
		self waittill( "enemy" );
	
		if ( !self ent_flag( "_stealth_enabled" ) )
			continue;
	
		if ( !isalive( self.eTarget ) )
			continue;
		
		self thread enemy_threat_set_spotted();
		break;
	}
}

enemy_alert_level_logic_start_attacking( enemy )
{
	//the first check means that a gun shot or something equally bad happened	
	//the second check is to see if you've been spotted already twice before	
	if ( self ent_flag( "_stealth_bad_event_listener" ) || enemy._stealth.logic.spotted_list[ self.unique_id ] > self._stealth.logic.alert_level._max_warnings )
	{
		 /#
			if ( self ent_flag( "_stealth_bad_event_listener" ) )
				self stealth_debug_print( "BROKEN STEALTH. Received ent '" + enemy.unique_id + "' as an enemy from code. Attacked because the reason was a bad_event_listener...ie a gunshot or something equally bad" );
			else
				self stealth_debug_print( "BROKEN STEALTH. Received ent '" + enemy.unique_id + "' as an enemy from code. Attacked because " + enemy.unique_id + " had been spotted more than the max_warning amount of " + self._stealth.logic.alert_level._max_warnings );
		#/
		self maps\_stealth_threat_enemy::enemy_alert_level_change( "attack" );
		return true;
	}
	
	return false;
}


enemy_recheck_time = 500;

enemy_alert_level_logic( enemy )
{
	// enemy is not stealthy one bit
	if ( !isdefined( enemy._stealth ) )
		return true;

	//add this ai to this spotted list
	if ( !isdefined( enemy._stealth.logic.spotted_list[ self.unique_id ] ) )
		enemy._stealth.logic.spotted_list[ self.unique_id ] = 0;

	while (1)
	{
		enemy._stealth.logic.spotted_list[ self.unique_id ]++;

		if ( enemy_alert_level_logic_start_attacking( enemy ) )
			return true;

		//this makes the ai look smart by being aware of your presence
		number = enemy._stealth.logic.spotted_list[ self.unique_id ];
		self maps\_stealth_threat_enemy::enemy_alert_level_change( "warning" + number );

	    //forget about him after a while
	    self thread enemy_alert_level_forget( enemy );
		//give the player a chance to hide with this
		self enemy_alert_level_waittime( enemy );
		
		if ( gettime() - self lastKnownTime( enemy ) > enemy_recheck_time )
		{
			self clearenemy();
			return false;
		}
	}
}

enemy_threat_set_spotted()
{
	self endon( "death" );
	self endon( "pain_death" );

	// If this is a miniuav, eTarget will be defined instead of enemy
	if ( !isdefined( self.enemy ) && isdefined( self.eTarget ) )
	{
		enemy = self.eTarget;
	}
	else
	{
		enemy = self.enemy;
	}
	
	self.dontEverShoot = undefined;
	
	self [[ self._stealth.logic.pre_spotted_func ]]();
	
	if ( isdefined( enemy ) )
		level._stealth.group.spotted_enemy[ self.script_stealthgroup ] = enemy;
		
	self group_flag_set( "_stealth_spotted" );
}

enemy_prespotted_func_default()
{
	wait 2.25;// randomfloatrange( 2, 2.5 ); // used to be .25, .5
}


enemy_alert_level_waittime( enemy )
{
	//this makes sure that if someone else spots you...then this quits earler
	//than the givin amount of time for the player to try and hide again
	if ( self stealth_group_corpse_flag() || self ent_flag( "_stealth_bad_event_listener" ) )
		return;

	timefrac = distance( self.origin, enemy.origin ) * .0005;
	waittime = level._stealth.logic.min_alert_level_duration + timefrac;
	
	self stealth_debug_print( "WARNING time = " + waittime );
	//iprintlnbold( waittime );

	level endon( group_get_flagname( "_stealth_spotted" ) );
	self endon( "_stealth_bad_event_listener" );
	
	wait( waittime );
}

/************************************************************************************************************/
/*													EVENTS													*/
/************************************************************************************************************/
enemy_event_listeners_logic( type )
{
	self endon( "death" );
	
	while ( 1 )
	{
		self waittill( type, subtype, param );	// subtype and param for debugging

		if ( !self ent_flag( "_stealth_enabled" ) )
			continue;

		if ( self ent_flag_exist( "_stealth_behavior_asleep" ) && self ent_flag( "_stealth_behavior_asleep" ) )
			continue;

		self ent_flag_set( "_stealth_bad_event_listener" );
	}
}

//this function resets all event listeners after they happen...so that we can detect each one multiple times
enemy_event_listeners_proc()
{
	self endon( "death" );
	
	while ( 1 )
	{
		self ent_flag_wait( "_stealth_bad_event_listener" );

		wait .65;
		//this time is set so high because apparently the ai can take up to .5 seconds to 
		//detect you as an enemy after they have received an event listener...
		//EDIT: after testing i've noticed that they still miss the event because they 
		//receive an enemy even after .65 seconds of receiving the event...but it's more
		//fun this way actually...to get away with it once in a while.
		self ent_flag_clear( "_stealth_bad_event_listener" );
	}
}

enemy_event_awareness_notify( type, param )
{
	self ent_flag_clear( "_stealth_normal" );

	self._stealth.logic.event.awareness_param[ type ] = param;
	self notify( "event_awareness", type );
	level notify( "event_awareness", type );
}

// for major categories with subtypes (ai_event, awareness_alert_level, awareness_corpse)
enemy_event_category_awareness( type )
{
	self endon( "death" );
	self endon( "pain_death" );

	while ( 1 )
	{
		self waittill( type, subtype, param );

		if ( !self ent_flag( "_stealth_enabled" ) )
			continue;
			
		//
		// special check for dogs deleted from here, see revision history #15
		//
		
		switch( type )
		{
			case "awareness_alert_level":
				break;
				
			case "ai_event":
				if ( !isdefined( self._stealth.logic.event.aware_aievents[ subtype ] ) )
					continue;
				//this makes sure that magic bullets and friendly bullets that don't cause an enemy notify don't cause guys to break out of animations	
				if( subtype == "bulletwhizby" && ( !isdefined( param.team ) || param.team == self.team ) )
					continue;
				// fall through
				
			default:
				group_flag_set( "_stealth_event" );
				level thread enemy_event_handle_clear( self.script_stealthgroup );
				break;
		}

		enemy_event_awareness_notify( subtype, param );

		waittillframeend;// wait a frame to make sure stealth_spotted didn't get set this frame
	}
}

// for special awareness events
enemy_event_awareness( type )
{
	self endon( "death" );
	self endon( "pain_death" );

	//just to create the key so it exists so other scripts (mainly behavior)
	//can reference it and see what awareness options it has
	self._stealth.logic.event.awareness_param[ type ] = true;

	while ( 1 )
	{
		self waittill( type, param );

		if ( !self ent_flag( "_stealth_enabled" ) )
			continue;

		group_flag_set( "_stealth_event" );
		level thread enemy_event_handle_clear( self.script_stealthgroup );

		enemy_event_awareness_notify( type, param );

		waittillframeend;// wait a frame to make sure stealth_spotted didn't get set this frame
	}
}

enemy_event_handle_clear( name )
{
	end_msg = "enemy_event_handle_clear:" + name + " Proc";
	wait_msg = "enemy_event_handle_clear:" + name + " Cleared";

	level notify( end_msg );
	level endon( end_msg );

	wait 2;

	ai = group_get_ai_in_group( name );

	if ( ai.size )
	{
		level add_wait( ::array_wait, ai, "event_awareness_waitclear_ai" );
		level add_endon( end_msg );
		level add_func( ::send_notify, wait_msg );
		level thread do_wait();

		array_thread( ai, ::event_awareness_waitclear_ai, end_msg );

		level waittill( wait_msg );
	}

	group_flag_clear( "_stealth_event", name );
}

event_awareness_waitclear_ai( end_msg )
{
	level endon( end_msg );

	self event_awareness_waitclear_ai_proc();
	self notify( "event_awareness_waitclear_ai" );
}

event_awareness_waitclear_ai_proc()
{
	self endon( "death" );

	waittillframeend;// make sure these flag's are set;

	check1 = false;
	if ( isdefined( self.ent_flag[ "_stealth_behavior_first_reaction" ] ) )
		check1 = self ent_flag( "_stealth_behavior_first_reaction" );

	check2 = false;
	if ( isdefined( self.ent_flag[ "_stealth_behavior_reaction_anim" ] ) )
		check1 = self ent_flag( "_stealth_behavior_reaction_anim" );

	if ( !check1 && !check2 )
		return;

	self add_wait( ::waittill_msg, "death" );
	self add_wait( ::waittill_msg, "going_back" );
	do_wait_any();

	self endon( "goal" );

	allies = array_combine( getaiarray( "allies" ), level._players );
	dist = level._stealth.logic.detect_range[ "hidden" ][ "crouch" ];
	distsquared = dist * dist;
	loop = true;

	if ( loop )
	{
		loop = false;
		foreach ( actor in allies )
		{
			if ( distancesquared( self.origin, actor.origin ) < distsquared )
				continue;
			loop = true;
		}
		wait 1;
	}
}

enemy_event_declare_to_team( type, name )
{
	other = undefined;
	team = self.team;

	while ( 1 )
	{
		if ( !isalive( self ) )
			return;

		self waittill( type, var1, var2 );

		if ( isalive( self ) && !self ent_flag( "_stealth_enabled" ) )
			continue;

		switch( type )
		{
			case "death":
				other = var1;
				break;
			case "damage":
				other = var2;
				break;
		}

		if ( !isdefined( other ) )
			continue;

		if ( isplayer( other ) || ( isdefined( other.team ) && other.team != team ) )
			break;
	}

	if ( !isdefined( self ) )
	{
	 	// in case of deletion
		return;
	}

	ai = getaispeciesarray( "bad_guys", "all" );

	check = int( level._stealth.logic.ai_event[ name ][ level._stealth.logic.detection_level ] );

	for ( i = 0; i < ai.size; i++ )
	{
		if ( !isalive( ai[ i ] ) )
			continue;
		if ( !isdefined( ai[ i ]._stealth ) )
			continue;
		if ( distance( ai[ i ].origin, self.origin ) > check )
			continue;
		if ( ai[ i ] ent_flag_exist( "_stealth_behavior_asleep" ) && ai[ i ] ent_flag( "_stealth_behavior_asleep" ) )
			continue;
		ai[ i ] ent_flag_set( "_stealth_bad_event_listener" );
	}
}

/************************************************************************************************************/
/*													SETUP													*/
/************************************************************************************************************/
enemy_init()
{
	assertex( !isdefined( self._stealth ), "you called maps\_stealth_logic::enemy_init() twice on the same ai" );

	self clearenemy();
	self._stealth = spawnstruct();
	self._stealth.logic = spawnstruct();

	self ent_flag_init( "_stealth_enabled" );
	self ent_flag_set( "_stealth_enabled" );
	
	self ent_flag_init( "_stealth_normal" );
	self ent_flag_set( "_stealth_normal" );
	
	self ent_flag_init( "_stealth_attack" );

	self group_flag_init( "_stealth_spotted" );
	self group_flag_init( "_stealth_event" );
	self group_flag_init( "_stealth_found_corpse" );

	self group_add_to_global_list();
	if ( !isdefined( level._stealth.behavior.sound[ "spotted" ][ self.script_stealthgroup ] ) )
		level._stealth.behavior.sound[ "spotted" ][ self.script_stealthgroup ] = false;

	self._stealth.logic.alert_level 				 = spawnstruct();
	self._stealth.logic.alert_level._max_warnings	 = 0;
	self enemy_alert_level_default_pre_spotted_func();

	self enemy_event_listeners_init();
}

enemy_event_listeners_init()
{
	self ent_flag_init( "_stealth_bad_event_listener" );

	self._stealth.logic.event = spawnstruct();
	
	self addAIEventListener( "grenade danger" );
	self addAIEventListener( "gunshot" );
	self addAIEventListener( "gunshot_teammate" );
	self addAIEventListener( "silenced_shot" );
	self addAIEventListener( "bulletwhizby" );
	self addAIEventListener( "projectile_impact" );

	self thread enemy_event_listeners_logic( "ai_event" );	// catch all of the above eventListener events
		
	self thread enemy_event_declare_to_team( "damage", "ai_eventDistPain" );
	self thread enemy_event_declare_to_team( "death", "ai_eventDistDeath" );
	
	self thread enemy_event_listeners_proc();

	self._stealth.logic.event.awareness_param = [];

	//a lot of these overlap with event listeners - because even though the event 
	//listeners above will cause a spotted state - we still want to know
	//why the ai got an enemy and perhaps do specific animations based on that	

	self._stealth.logic.event.aware_aievents = [];
	self._stealth.logic.event.aware_aievents[ "bulletwhizby" ] = true;
	self._stealth.logic.event.aware_aievents[ "projectile_impact" ] = true;
	self._stealth.logic.event.aware_aievents[ "gunshot_teammate" ] = true;
	self._stealth.logic.event.aware_aievents[ "grenade danger" ] = true;

	self thread enemy_event_category_awareness( "ai_event" );
	self thread enemy_event_category_awareness( "awareness_alert_level" );	// this is actually notified in this script
	self thread enemy_event_category_awareness( "awareness_corpse" );		// this is called from corpse

	 /#
		//these are for extra debug prints
		self thread enemy_event_debug_print( "awareness_alert_level" );
		self thread enemy_event_debug_print( "awareness_corpse" );
		self thread enemy_event_debug_print( "ai_event" );
	#/
}

enemy_alert_level_set_pre_spotted_func( func )
{
	self._stealth.logic.pre_spotted_func = func;
}

enemy_alert_level_default_pre_spotted_func()
{
	self._stealth.logic.pre_spotted_func = ::enemy_prespotted_func_default;
}