#using scripts\shared\ai_shared;
#using scripts\shared\math_shared;
#using scripts\shared\sound_shared;
#using scripts\shared\throttle_shared;
#using scripts\shared\util_shared;

#using scripts\shared\ai\systems\debug;
#using scripts\shared\ai\systems\init;
#using scripts\shared\ai\archetype_utility;
#using scripts\shared\ai\systems\weaponList;

    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          
                                                                  	                             	  	                                      

#using_animtree("generic");

function autoexec main()
{
	level.ai_weapon_throttle = new Throttle();
	[[ level.ai_weapon_throttle ]]->Initialize( 1, 0.1 );
}

function private _throwStowedWeapon( entity, weapon, weaponModel )
{
	entity waittill( "death" );
	
	if(IsDefined(entity))
	{		
		weaponModel Unlink();
		entity shared::ThrowWeapon( weapon, getTagForPos( "back" ), false );
	}
		
	weaponModel Delete();
}

function StowWeapon( weapon, positionOffset, orientationOffset )
{
	entity = self;
	
	if ( !IsDefined( positionOffset ) )
	{
		positionOffset = (0, 0, 0);
	}
	
	if ( !IsDefined( orientationOffset ) )
	{
		orientationOffset = (0, 0, 0);
	}

	weaponModel = Spawn( "script_model", (0, 0, 0) );
	weaponModel SetModel( weapon.worldmodel );
	
	weaponModel LinkTo( entity, "tag_stowed_back", positionOffset, orientationOffset );
	
	entity thread _throwStowedWeapon( entity, weapon, weaponModel );
}

function placeWeaponOn( weapon, position )
{	
	self notify("weapon_position_change");
	
	if ( IsString( weapon ) )
	{
		weapon = GetWeapon( weapon );
	}

	if (!isdefined(self.weaponInfo[weapon.name]))
		self init::initWeapon( weapon );
		
	curPosition = self.weaponInfo[weapon.name].position;
	
	// make sure we're not out of sync
	assert( curPosition == "none" || self.a.weaponPos[curPosition] == weapon );

	if ( !IsArray( self.a.weaponPos ) )
	{
		self.a.weaponPos = [];
	}

	assert( IsArray( self.a.weaponPos ) );
	assert( position == "none" || IsDefined( self.a.weaponPos[position] ), "Weapon position \"" + position + "\"" );
	assert( IsWeapon( weapon ) );

	// weapon already in place
	if ( position != "none" && self.a.weaponPos[position] == weapon )
		return;
		
	//println("detach all (" + weapon.name + ", " + position + ")"); 
	self detachAllWeaponModels();
	
	// detach if we're already in a position
	if ( curPosition != "none" )
		self detachWeapon( weapon );
			
	// nothing more to do
	if ( position == "none" )
	{
		//println("update(1) all (" + weapon.name + ", " + position + ")"); 
		self updateAttachedWeaponModels();
		self AiUtility::setCurrentWeapon( level.weaponNone );
		return;
	}

	if ( self.a.weaponPos[position] != level.weaponNone )
		self detachWeapon( self.a.weaponPos[position] );

	// to ensure that the correct tags for the active weapon are used, we need to make sure it gets attached first
	if ( position == "left" || position == "right" )
	{
		self updateScriptWeaponInfoAndPos( weapon, position );
		self AiUtility::setCurrentWeapon(weapon);		
	}
	else
	{
		self updateScriptWeaponInfoAndPos( weapon, position );
	}

	self updateAttachedWeaponModels();
	
	// make sure we don't have a weapon in each hand
	assert( self.a.weaponPos["left"] == level.weaponNone || self.a.weaponPos["right"] == level.weaponNone );
}	

function detachWeapon( weapon )
{
	self.a.weaponPos[self.weaponInfo[weapon.name].position] = level.weaponNone;
	self.weaponInfo[weapon.name].position = "none";
}


function updateScriptWeaponInfoAndPos( weapon, position )
{
	self.weaponInfo[weapon.name].position = position;
	self.a.weaponPos[position] = weapon;
}

function detachAllWeaponModels()
{
	if( isdefined(self.weapon_positions) )
	{
		for ( index = 0; index < self.weapon_positions.size; index++ )
		{
			weapon = self.a.weaponPos[self.weapon_positions[index]];
			
			if ( weapon == level.weaponNone )
				continue;
							
			self SetActorWeapon( level.weaponNone, self GetActorWeaponOptions() );
		}
	}
}

function updateAttachedWeaponModels()
{
	if( isdefined(self.weapon_positions) )
	{
		for ( index = 0; index < self.weapon_positions.size; index++ )
		{
			weapon = self.a.weaponPos[self.weapon_positions[index]];
			
			if ( weapon == level.weaponNone )
				continue;
			
			// weapon should only be assigned (means self.weapon will be set) to the weapon	if its set to go to "right"
			if( self.weapon_positions[index] != "right" )
				continue;
			
			self SetActorWeapon( weapon, self GetActorWeaponOptions() );

			if ( self.weaponInfo[weapon.name].useClip && !self.weaponInfo[weapon.name].hasClip )
				self hidepart( "tag_clip" );
		}
	}
}

function getTagForPos( position )
{
	switch ( position )
	{
		case "chest":
			return "tag_weapon_chest";
		case "back":
			return "tag_stowed_back";
		case "left":
			return "tag_weapon_left";
		case "right":
			return "tag_weapon_right";
		case "hand":
			return "tag_inhand";
		default:
			assertMsg( "unknown weapon placement position: " + position );
		break;
	}
}

function ThrowWeapon( weapon, positionTag, scavenger )
{
	waitTime = 0.1;
	linearScalar = 2.0;
	angularScalar = 10;
	
	// Calculate the linear and angular velocity to launch the weapon.
	startPosition = self GetTagOrigin( positionTag );
	startAngles = self GetTagAngles( positionTag );
	
	// Wait two server frames to calculate velocity.
	wait( waitTime );
	
	if ( IsDefined( self ) )
	{
		endPosition = self GetTagOrigin( positionTag );
		endAngles = self GetTagAngles( positionTag );
			
		linearVelocity = ( endPosition - startPosition ) * ( 1.0 / waitTime ) * linearScalar;
		angularVelocity = VectorNormalize( endAngles - startAngles ) * angularScalar;
			
		throwWeapon = self DropWeapon( weapon, positionTag, linearVelocity, angularVelocity, scavenger );
		if ( IsDefined( throwWeapon ) )
		{
			throwWeapon SetContents( throwWeapon SetContents( 0 ) & ~( (1 << 15) | (1 << 26) | (1 << 23) | (1 << 25) ) );
		}
		return throwWeapon;
	}
}

function DropAIWeapon()
{
	self endon("death");
	
	if( self.weapon == level.weaponNone )
	{
		return;
	}

	if (( isdefined( self.script_nodropsecondaryweapon ) && self.script_nodropsecondaryweapon ) && (self.weapon == self.initial_secondaryweapon))
	{
		/#PrintLn("Not dropping secondary weapon '" + self.weapon.name + "'");#/
		return;
	}
	else if (( isdefined( self.script_nodropsidearm ) && self.script_nodropsidearm ) && (self.weapon == self.sidearm))
	{
		/#PrintLn("Not dropping sidearm '" + self.weapon.name + "'");#/
		return;
	}
	
	[[ level.ai_weapon_throttle ]]->WaitInQueue( self );
	
	current_weapon = self.weapon;
	dropWeaponName = player_weapon_drop( current_weapon );
	position = self.weaponInfo[ current_weapon.name ].position;
	
	shouldDropWeapon = !IsDefined( self.dontDropWeapon ) || self.dontDropWeapon === false;
	
	if( current_weapon.isScavengable == false )
	{
		shouldDropWeapon = false;
	}
	
	if ( shouldDropWeapon && self.dropWeapon )
	{
		self.dontDropWeapon = true;
		
		positionTag = getTagForPos( position );
		
		ThrowWeapon( dropWeaponName, positionTag, false );
	}
	
	if ( self.weapon != level.weaponNone )
	{
		shared::placeWeaponOn( current_weapon, "none" );
	
		if( self.weapon == self.primaryweapon )
		{
			self AiUtility::setPrimaryWeapon( level.weaponNone );
		}
		else if( self.weapon == self.secondaryweapon )
		{
			self AiUtility::setSecondaryWeapon( level.weaponNone );
		}
	}
		
	self AiUtility::setCurrentWeapon( level.weaponNone );
}

function DropAllAIWeapons()
{
	if (( isdefined( self.a.dropping_weapons ) && self.a.dropping_weapons ))
	{
		// already called
		return;
	}

	if( !self.dropweapon )
	{
		if( self.weapon != level.weaponNone )
		{
			shared::placeWeaponOn( self.weapon, "none" );
			self AiUtility::setCurrentWeapon( level.weaponNone );
		}

		return;
	}

	self.a.dropping_weapons = true;

	self detachAllWeaponModels();
	
	droppedSideArm = false;
	
	if( isdefined(self.weapon_positions) )
	{
		for ( index = 0; index < self.weapon_positions.size; index++ )
		{
			weapon = self.a.weaponPos[ self.weapon_positions[ index ] ];

			if ( weapon != level.weaponNone )
			{
				self.weaponInfo[ weapon.name ].position = "none";			
				self.a.weaponPos[ self.weapon_positions[ index ] ] = level.weaponNone;

				if (( isdefined( self.script_nodropsecondaryweapon ) && self.script_nodropsecondaryweapon ) && (weapon == self.initial_secondaryweapon))
				{
					/#PrintLn("Not dropping secondary weapon '" + weapon.name + "'");#/
				}
				else if (( isdefined( self.script_nodropsidearm ) && self.script_nodropsidearm ) && (weapon == self.sidearm))
				{
					/#PrintLn("Not dropping sidearm '" + weapon.name + "'");#/
				}
				else
				{

					velocity = self GetVelocity();
					speed = Length( velocity ) * 0.5;
					
					weapon = player_weapon_drop(weapon);
					
					droppedWeapon = self DropWeapon( weapon, self.weapon_positions[ index ], speed );
					
					if ( self.sideArm != level.weaponNone )
					{
						if ( weapon == self.sideArm )
							droppedSideArm = true;
					}						
				}
			}
		}
	}
	
	if( !droppedSideArm && self.sideArm != level.weaponNone )
	{
		// 10% chance of dropping sidearm
		if( RandomInt(100) <= 10 )
		{
			velocity = self GetVelocity();
			speed = Length( velocity ) * 0.5;
	
			droppedWeapon = self DropWeapon( self.sideArm, "chest", speed );
		}
	}
			
	self AiUtility::setCurrentWeapon( level.weaponNone );

	self.a.dropping_weapons = undefined;
}

function player_weapon_drop( weapon )
{
	if ( IsSubStr( weapon.name, "rpg" ) )
	{
		return GetWeapon( "rpg_player" );
	}
	
	return weapon;
}


function HandleNoteTrack( note, flagName, customFunction, var1 )
{

}

// DoNoteTracks waits for and responds to standard noteTracks on the animation, returning when it gets an "end" or a "finish"
// For level scripts, a pointer to a custom function should be passed as the second argument, which handles notetracks not
// already handled by the generic function. This call should take the form DoNoteTracks(flagName,&customFunction);
// The custom function will be called for each notetrack not recognized, and will pass the notetrack name. Note that this
// function could be called multiple times for a single animation.
function DoNoteTracks( flagName, customFunction, debugIdentifier, var1 ) // debugIdentifier isn't even used. we should get rid of it.
{
	for (;;)
	{
		self waittill (flagName, note);

		if ( !isdefined( note ) )
		{
			note = "undefined";
		}
		
		val = self HandleNoteTrack( note, flagName, customFunction, var1 );
		
		if ( isdefined( val ) )
		{
			return val;
		}
	}
}


function DoNoteTracksIntercept( flagName, interceptFunction, debugIdentifier ) // debugIdentifier isn't even used. we should get rid of it.
{
	assert( isdefined( interceptFunction ) );
	
	for (;;)
	{
		self waittill ( flagName, note );

		if ( !isdefined( note ) )
		{
			note = "undefined";
		}

		intercepted = [[interceptFunction]]( note );
		if ( isdefined( intercepted ) && intercepted )
		{
			continue;
		}

		val = self HandleNoteTrack( note, flagName );
		
		if ( isdefined( val ) )
		{
			return val;
		}
	}
}


function DoNoteTracksPostCallback( flagName, postFunction )
{
	assert( isdefined( postFunction ) );
	
	for (;;)
	{
		self waittill ( flagName, note );

		if ( !isdefined( note ) )
		{
			note = "undefined";
		}

		val = self HandleNoteTrack( note, flagName );
		
		[[postFunction]]( note );
		
		if ( isdefined( val ) )
		{
			return val;
		}
	}
}

// Don't call this function except as a thread you're going to kill - it lasts forever.
function DoNoteTracksForever(flagName, killString, customFunction, debugIdentifier)
{
	DoNoteTracksForeverProc(&DoNoteTracks, flagName, killString, customFunction, debugIdentifier);
}

function DoNoteTracksForeverIntercept(flagName, killString, interceptFunction, debugIdentifier)
{
	DoNoteTracksForeverProc(&DoNoteTracksIntercept, flagName, killString, interceptFunction, debugIdentifier );
}

function DoNoteTracksForeverProc( notetracksFunc, flagName, killString, customFunction, debugIdentifier )
{
	if (isdefined (killString))
	{
		self endon (killString);
	}

	self endon ("killanimscript");
	
	if (!isdefined(debugIdentifier))
	{
		debugIdentifier = "undefined";
	}

	for (;;)
	{
		time = GetTime();
		returnedNote = [[notetracksFunc]](flagName, customFunction, debugIdentifier);
		timetaken = GetTime() - time;
		if ( timetaken < 0.05)
		{
			time = GetTime();
			returnedNote = [[notetracksFunc]](flagName, customFunction, debugIdentifier);
			timetaken = GetTime() - time;
			if ( timetaken < 0.05)
			{
				/#println (GetTime()+" "+debugIdentifier+" shared::DoNoteTracksForever is trying to cause an infinite loop on anim "+flagName+", returned "+returnedNote+".");#/
				wait ( 0.05 - timetaken );
			}
		}
		//(GetTime()+" "+debugIdentifier+" DoNoteTracksForever returned in "+timetaken+" ms.");#/
	}	
}

// Designed for using DoNoteTracks on looping animations, so you can wait for a time instead of the "end" parameter
function DoNoteTracksForTime(time, flagName, customFunction, debugIdentifier)
{
	ent = SpawnStruct();
	ent thread doNoteTracksForTimeEndNotify(time);
	DoNoteTracksForTimeProc(&DoNoteTracksForever, time, flagName, customFunction, debugIdentifier, ent);
}

function DoNoteTracksForTimeIntercept( time, flagName, interceptFunction, debugIdentifier)
{
	ent = SpawnStruct();
	ent thread doNoteTracksForTimeEndNotify(time);
	DoNoteTracksForTimeProc(&DoNoteTracksForeverIntercept, time, flagName, interceptFunction, debugIdentifier, ent);
}

function DoNoteTracksForTimeProc( doNoteTracksForeverFunc, time, flagName, customFunction, debugIdentifier, ent)
{
	ent endon ("stop_notetracks");
	[[doNoteTracksForeverFunc]](flagName, undefined, customFunction, debugIdentifier);
}

function doNoteTracksForTimeEndNotify(time)
{
	wait (time);
	self notify ("stop_notetracks");
}

