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

stealth_corpse_enemy_main()
{
	self enemy_init();

	self thread enemy_corpse_logic();
	self thread enemy_Corpse_Loop();
}

/************************************************************************************************************/
/*												BEHAVIOR LOOPS												*/
/************************************************************************************************************/
enemy_Corpse_Loop()
{
	self endon( "death" );
	self endon( "pain_death" );

	self thread enemy_found_corpse_loop();

	while ( 1 )
	{
		self waittill( "_stealth_saw_corpse" );

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

		self enemy_saw_corpse_logic();
	}
}

enemy_found_corpse_loop()
{
	spotted_flag = self group_get_flagname( "_stealth_spotted" );
	corpse_flag = self group_get_flagname( "_stealth_found_corpse" );

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

	if ( flag( spotted_flag ) )
		return;
	level endon( spotted_flag );

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

		if ( self ent_flag_exist( "_stealth_behavior_asleep" ) )
			self ent_flag_waitopen( "_stealth_behavior_asleep" );

		self stealth_group_corpse_flag_wait();

		if ( !self ent_flag( "_stealth_enabled" ) )
			continue;
		while ( self stealth_group_corpse_flag() )
		{
			if ( !self ent_flag( "_stealth_enabled" ) )
				break;

			self enemy_corpse_found_wrapper();

			level waittill( corpse_flag );
		}
	}
}

enemy_saw_corpse_logic()
{
	spotted_flag = self group_get_flagname( "_stealth_spotted" );
	corpse_flag = self group_get_flagname( "_stealth_found_corpse" );

	if ( flag( spotted_flag ) )
		return;
	level endon( spotted_flag );
	
	self endon( "attack" );

	level endon( corpse_flag );
	
	while ( 1 )
	{
		self ent_flag_waitopen( "_stealth_enemy_alert_level_action" );

		self enemy_corpse_saw_wrapper();

		// the only reason we failed - and didn't end this function 
		// is if we had an alert change so wait to go back to normal 
		// and then resume looking for the corpse
		self waittill( "normal" );
	}
}

enemy_corpse_saw_wrapper()
{
	self enemy_find_original_goal();

	// if he maybe saw an enemy - that's more important
	self endon( "enemy_alert_level_change" );

	self thread enemy_announce_huh();

	//self enemy_stop_current_behavior();
	self ent_flag_set( "_stealth_running_to_corpse" );
	self ent_flag_set( "_stealth_override_goalpos" );

	funcs = self._stealth.behavior.ai_functions[ "corpse" ];
	self [[ funcs[ "saw" ] ]]();// ::enemy_corpse_saw_behavior()
}

enemy_corpse_found_wrapper()
{
	self enemy_find_original_goal();

	if ( !self ent_flag( "_stealth_found_corpse" ) )
		self notify( "awareness_corpse", "heard_corpse", ( 0, 0, 0 ) );

	self enemy_reaction_state_alert();

	if ( self.type == "dog" )
		self ent_flag_set( "_stealth_override_goalpos" );

	self thread enemy_corpse_reset_wrapper();

	funcs = self._stealth.behavior.ai_functions[ "corpse" ];
	self [[ funcs[ "found" ] ]]();// ::enemy_corpse_found_behavior()
}

enemy_corpse_reset_wrapper()
{
	spotted_flag = self group_get_flagname( "_stealth_spotted" );

	//if an event happens - we want to forget about this
	self endon( "death" );
	self endon( "_stealth_enemy_alert_level_change" );
	level endon( spotted_flag );
	//in this frame we'll receive this notify from our own event...so wait a frame so we dont end on it
	waittillframeend;
	self endon( "enemy_awareness_reaction" );

	self stealth_group_corpse_flag_waitopen();

	self ent_flag_set( "_stealth_normal" );
		
	funcs = self._stealth.behavior.ai_functions[ "corpse" ];
	self thread [[ funcs[ "reset" ] ]]();// ::enemy_corpse_reset_behavior()
}

/************************************************************************************************************/
/*												DEFAULT BEHAVIOR											*/
/************************************************************************************************************/
enemy_corpse_saw_behavior()
{
	self.disableArrivals = false;
	self.disableExits = false;

	if ( self.type != "dog" )
		self set_generic_run_anim( "_stealth_combat_jog" );
	else
	{
		self clear_run_anim();
		self.script_growl = 1;
		self.script_nobark = 1;
	}

	self.goalradius = 80;// the animation of finding the corpse
	self setgoalpos( self._stealth.logic.corpse.origin );
}

enemy_corpse_found_behavior()
{
	if ( self.type == "dog" )
	{
		self setgoalpos( self.origin );
		return;
	}

	//self enemy_stop_current_behavior();

	node = enemy_find_free_pathnode_near( level._stealth.logic.corpse.last_pos, 512, 40 );

	if ( !isdefined( node ) )
		return;

	self thread enemy_runto_and_lookaround( node );
}

enemy_corpse_reset_behavior()
{
	wait randomfloatrange( 0, 5 );
	self enemy_stop_current_behavior();
	
	self maps\_stealth_threat_enemy::enemy_alert_level_change( "reset" );
}

/************************************************************************************************************/
/*													LOGIC													*/
/************************************************************************************************************/
player_can_see_corpse( corpse_location )
{
	player = get_closest_player( corpse_location );
	d = distance( player.origin, corpse_location );

	//if very close line of sight doesnt matter
	if ( d < 150 )
		return true;

	//if very far from nearest player line of sight doesnt matter
	if ( d > level._stealth.logic.corpse.player_distsqrd )
		return false;

	return sightTracePassed( ( corpse_location + ( 0, 0, 48 ) ), player geteye(), false, player );
}

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

	self thread enemy_corpse_found_loop();
	while ( 1 )
	{
		if ( self ent_flag_exist( "_stealth_behavior_asleep" ) )
			self ent_flag_waitopen( "_stealth_behavior_asleep" );

		self ent_flag_wait( "_stealth_enabled" );

		while ( !self stealth_group_spotted_flag() && !self ent_flag( "_stealth_attack" ) )
		{
			found = false;
			saw = false;
			corpse = undefined;
			dist = undefined;

			corpseArray = GetCorpseArray();
			
			for ( i = 0; i < corpseArray.size; i++ )
			{
				corpse = corpseArray[ i ];
				
				if ( isdefined( corpse.found ) )
					continue;

				if( !isdefined( level._corpse_behavior_doesnt_require_player_sight ) )
					if ( !player_can_see_corpse( corpse.origin ) )
						continue;

				distsqrd = distancesquared( self.origin, corpse.origin );

				if ( self.type != "dog" )
					dist = level._stealth.logic.corpse.found_distsqrd;
				else
					dist = level._stealth.logic.corpse.found_dog_distsqrd;

				//are we so close that we actually found one?
				if ( distsqrd < dist )
				{
					found = true;
					break;
				}

				//that's the only check for finding a guy - now lets see if we just see anyone
				//and make sure not to make any duplicates...we don't want to notify seeing the 
				//same corpse multiple times

				//have we already seen this guy?
				if ( isdefined( self._stealth.logic.corpse.corpse_entity ) )
				{
					if ( self._stealth.logic.corpse.corpse_entity == corpse )
						continue;

					//ok so it's a new guy - is this one closer than the one we already have?
					distsqrd2 = distancesquared( self.origin, self._stealth.logic.corpse.corpse_entity.origin );
					if ( distsqrd2 <= distsqrd )
						continue;
				}

				//are we close enough to check?
				if ( distsqrd > level._stealth.logic.corpse.sight_distsqrd )
					continue;

				//ok how about close enough to automatically see one?
				if ( distsqrd < level._stealth.logic.corpse.detect_distsqrd )
				{
					//do we have clear line of sight to the corpse
					if ( self cansee( corpse ) )
					{
						saw = true;
						break;
					}
				}

				//if not do we happen to look at him at this distance?
				angles = self gettagangles( "tag_eye" );
				origin = self getEye();

				sight 			 = anglestoforward( angles );
				vec_to_corpse 	 = vectornormalize( corpse.origin - origin );

				//are we looking towards a corpse
				if ( vectordot( sight, vec_to_corpse ) > .55 )
				{
					//do we have clear line of sight to the corpse
					if ( self cansee( corpse ) )
					{
						saw = true;
						break;
					}
				}
			}
			
			if ( found )
			{
				if ( !ent_flag( "_stealth_found_corpse" ) )
					self ent_flag_set( "_stealth_found_corpse" );
				else
					self notify( "_stealth_found_corpse" );

				//if he found it then we can clear his seeing one
				self ent_flag_clear( "_stealth_saw_corpse" );

				self thread enemy_corpse_found( corpse );

				self notify( "awareness_corpse", "found_corpse", corpse );
			}
			else if ( saw )
			{
				self._stealth.logic.corpse.corpse_entity = corpse;
				self._stealth.logic.corpse.origin = corpse.origin;

				if ( !ent_flag( "_stealth_saw_corpse" ) )
					self ent_flag_set( "_stealth_saw_corpse" );
				else
					self notify( "_stealth_saw_corpse" );

				level notify( "_stealth_saw_corpse" );
				self notify( "awareness_corpse", "saw_corpse", corpse );
			}

			wait .5;// was .05
		}

		self remove_corpse_loop_while_stealth_broken();
		self stealth_group_spotted_flag_waitopen();
		self ent_flag_waitopen( "_stealth_attack" );
	}
}

remove_corpse_loop_while_stealth_broken()
{
	spotted_flag = self group_get_flagname( "_stealth_spotted" );
	
	while ( flag( spotted_flag ) )
	{
		corpseArray = GetCorpseArray();
		
		for ( i = 0; i < corpseArray.size; i++ )
		{
			corpse = corpseArray[ i ];
			
			if ( isdefined( corpse.found ) )
				continue;
				
			distsqrd = distancesquared( self.origin, corpse.origin );

			if ( self.type != "dog" )
				dist = level._stealth.logic.corpse.found_distsqrd;
			else
				dist = level._stealth.logic.corpse.found_dog_distsqrd;
				
			if ( distsqrd < dist )
			{				
				corpse setCorpseRemoveTimer( 10 );
				corpse.found = true;
			}
		}
		
		 wait 0.5;
	}
}

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

	_flag = self stealth_get_group_corpse_flag();

	while ( 1 )
	{
		level waittill( _flag );

		//make sure the flag's not notifying because it's getting cleared
		if ( !self stealth_group_corpse_flag() )
			continue;

		self enemy_corpse_alert_level();
	}
}

enemy_corpse_alert_level()
{
	enemy = undefined;

	if ( isdefined( self.enemy ) )
		enemy = self.enemy;
	else
		enemy = random( level._players );

	//we want the ai to detect an enemy without actually causing the behavior to happen
	//so we can't use the regular alert function, because that causes a notify

	if ( !isdefined( enemy._stealth.logic.spotted_list[ self.unique_id ] ) )
		enemy._stealth.logic.spotted_list[ self.unique_id ] = 0;

	//basically take up their alert level each time they see a corpse...but not enough
	//to start attacking the player	
	if ( enemy._stealth.logic.spotted_list[ self.unique_id ] < self._stealth.logic.alert_level._max_warnings )
	{
		enemy._stealth.logic.spotted_list[ self.unique_id ]++ ;// this takes it to 1
		self thread enemy_alert_level_forget( enemy );
	}
}

enemy_corpse_found( corpse )
{
	self endon( "death" );

	level._stealth.logic.corpse.last_pos = corpse.origin;
	corpse setCorpseRemoveTimer( level._stealth.logic.corpse.reset_time );
	corpse.found = true;

	//give a chance
	if ( self.type == "dog" && self ent_flag_exist( "_stealth_behavior_reaction_anim_in_progress" ) )
	{
		wait .1;
		self ent_flag_waitopen( "_stealth_behavior_reaction_anim_in_progress" );
		wait .5;
	}
	else
		wait 2;

	self thread enemy_announce_corpse();
	wait 2;

	corpse_flag = self group_get_flagname( "_stealth_found_corpse" );
	if ( !self stealth_group_corpse_flag() )
		self group_flag_set( "_stealth_found_corpse" );
	else
		level notify( corpse_flag );

	self thread enemy_corpse_clear();
}

enemy_corpse_clear()
{
	corpse_flag = self group_get_flagname( "_stealth_found_corpse" );
	group = self.script_stealthgroup;

	level endon( corpse_flag );

	waittill_dead_or_dying( group_get_ai_in_group( group ), undefined, level._stealth.logic.corpse.reset_time );
	
	thread group_flag_clear( "_stealth_found_corpse", group );
}

/************************************************************************************************************/
/*													SETUP													*/
/************************************************************************************************************/
enemy_init()
{
	self._stealth.logic.corpse = spawnstruct();
	self._stealth.logic.corpse.corpse_entity = undefined;

	self ent_flag_init( "_stealth_saw_corpse" );
	self ent_flag_init( "_stealth_found_corpse" );

	self enemy_default_corpse_behavior();
	self enemy_default_corpse_anim_behavior();

	self._stealth.plugins.corpse = true;
}

enemy_default_corpse_anim_behavior()
{

	if ( self.type == "dog" )
	{
		self ai_create_behavior_function( "animation", "heard_corpse", 		::dog_animation_generic );
		self ai_create_behavior_function( "animation", "saw_corpse", 		::dog_animation_sawcorpse );
		self ai_create_behavior_function( "animation", "found_corpse", 		::dog_animation_foundcorpse );
		self ai_create_behavior_function( "animation", "howl", 				::dog_animation_howl );
	}
	else
	{
		self ai_create_behavior_function( "animation", "heard_corpse", 		::enemy_animation_generic );
		self ai_create_behavior_function( "animation", "saw_corpse", 		::enemy_animation_sawcorpse );
		self ai_create_behavior_function( "animation", "found_corpse", 		::enemy_animation_foundcorpse );
	}
}

enemy_default_corpse_behavior()
{
	array = [];
	array[ "saw" ] 		 = ::enemy_corpse_saw_behavior;
	array[ "found" ] 	 = ::enemy_corpse_found_behavior;
	array[ "reset" ] 	 = ::enemy_corpse_reset_behavior;

	self enemy_custom_corpse_behavior( array );
}

enemy_custom_corpse_behavior( array )
{
	foreach ( key, function in array )
		self ai_create_behavior_function( "corpse", key, function );
}