#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
#include common_scripts\utility;
#include maps\mp\agents\_agent_utility;
//#include maps\mp\bots\_bots_strategy;
#include maps\mp\agents\_scriptedAgents;
#include maps\mp\_vl_camera;
#include maps\mp\_vl_firingrange;
#include maps\mp\_vl_selfiebooth;

PARTYSTATUS_EMPTY = 0;
PARTYSTATUS_ZOMBIE = 1;
PARTYSTATUS_ANONYMOUS = 2;
PARTYSTATUS_JOINED = 3;
PARTYSTATUS_COMMITTED = 4;
PARTYSTATUS_PRESENT = 5;


vlprint( str )
{
	print( "VLOBBY["+GetTime()+"]: " + str );
}

vlprintln( str )
{
	println( "VLOBBY["+GetTime()+"]: " + str );
}

vl_init()
{
	level.vl_onSpawnPlayer = ::onSpawnPlayer;
	vl_main();
}


vl_main()
{
	SetDvar("r_dof_physical_enable", 1);
	SetDvar("r_dof_physical_bokehEnable", 1);
	SetDvar("r_adaptiveSubdiv", 0);
	SetDvar("r_eyePupil", 0.15);
	SetDvar("r_uiblurdstmode", 3);
	SetDvar("r_blurdstgaussianblurradius", 1.5);
	ResetEntPlayerXuidForEmblems();
	
	//level.PartyMemberClassChange_cb = ::PartyMemberClassChange;
	level.PartyMembers_cb = ::party_members;
	level.vlavatars = [];
	level.xuid2ownerId = [];
	level.vl_focus = 0;
	level.avatarInfo = [];
	maxAvatars = 18;
	if ( level.ps3 || level.xenon )
		maxAvatars = 12;
	for ( i=0; i<maxAvatars; i++ )
	{
		level.avatarInfo[i] = SpawnStruct();
		level.avatarInfo[i].timeToDelete = 0;
		level.avatarInfo[i].xuid = "";
	}
	level.maxAvatars = maxAvatars;
	
	thread monitor_member_class_changes();
	thread monitor_member_timeouts();

	SetDvar( "virtuallobbymembers", 0 );	// ensure this is cleared whenever we enter the vl
	/#
	SetDvarIfUninitialized( "scr_vl_addfakemembers", "0" );
	SetDvarIfUninitialized( "scr_vl_debugfly", "0" );
	SetDvarIfUninitialized( "scr_vl_printcostume", "0" );
	level.vl_fakemembers = 0;
	#/
	level.num_lobby_idles = 4;
	init_firingrange();
	init_selfiebooth();
	//lighying values
	//level.sunpitch_damage = 0;
	//waitframe();
	//level.sunpitch = GetEnt("sun_pitch_swap", "targetname");
	//level.sunpitch_origin = level.sunpitch getOrigin();	
}

init_avatars()
{
	EXTRA_AVATARS = 1;	// for cao
	maxAvatars = level.maxAvatars + EXTRA_AVATARS;
	level.vlavatarpool = [];
	for ( i=0; i<maxAvatars; i++ )
	{
		avatar = getFreeAgent();
		level.vlavatarpool[i] = avatar;
		avatar SpawnAgent( (0,0,0), (0,0,0) );
		avatar set_agent_values( "spectator", "none" );
		avatar maps\mp\agents\_agent_common::set_agent_health( 100 );
		
		avatar BotClearScriptEnemy();
		avatar BotClearScriptGoal();
		avatar bot_disable_tactical_goals();
		avatar BotSetFlag("disable_movement", 1);
		avatar BotSetFlag("disable_rotation",1); // they shouldn't try and rotate
		avatar.isfree = true;
	}
}

alloc_avatar()
{
	if ( !IsDefined(level.vlavatarpool) )
		init_avatars();
	
	foreach ( avatar in level.vlavatarpool )
	{
		if ( avatar.isfree )
		{
			avatar.isfree = false;
			return avatar;
		}
	}
	assertmsg( "Unable to alloc_avatar" );
}

free_avatar( avatar )
{
	assert( !avatar.isfree );
	avatar notify( "free_avatar" );
	avatar.isfree = true;
}

onSpawnPlayer()
{
	level.agent_funcs["player"]["on_killed"] = ::on_agent_player_killed;
	self thread vlobby_player();
	level.playerStatus[0] = PARTYSTATUS_JOINED;
	self thread monitor_member_focus_change();
	self thread monitor_create_an_operator(0);
	self thread monitor_create_a_class(0);
	self thread monitor_clans();
	thread monitor_move_btn_fr_vl( self );
	self disable_player_controls();
	// TODO needs to replinish grenade after player uses one in firing range.  It's weird after you use one and hold it too long nothing happens but it eats your grenade.
 	self SetDemiGod(true); /// needed to skip mod_suicide death call from holding a grenade too long.  Grenade suicide called in code
	self SetMLGSpectator( 0 ); // needed to catch a bug where we don't properly clear this when the host of a game ends the match while in MLG Broadcaster
}

on_agent_player_killed(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
{
	// don't do anything
}


player_sticks_in_lefty_config()
{
	if ( self is_player_gamepad_enabled() )
	{
		sticksLayoutName = self GetLocalPlayerProfileData( "gpadSticksConfig" );
		return IsDefined( sticksLayoutName ) && ( ( sticksLayoutName == "thumbstick_southpaw" ) || ( sticksLayoutName == "thumbstick_legacy" ) );
	}
	
	return false;
}

player_setup_lefty_angle( avatar )
{
	if ( !IsDefined( avatar.fakeAngle ) )
	{
		if ( IsDefined( avatar.storedRightStickY ) )
		{
			avatar.fakeAngle = avatar.storedRightStickY;
		}
		else
		{
			avatar.fakeAngle = 0;
		}
	}	
}

player_get_right_stick_y( avatar )
{
	if ( self player_sticks_in_lefty_config() )
	{
		player_setup_lefty_angle( avatar );
		return avatar.fakeAngle;
	}
	else
	{
		rightStick = self GetUnNormalizedCameraMovement();
		return rightStick[1];
	}
}

player_update_right_stick_y( avatar )
{
	
	rightStickY = 0;
	if ( self player_sticks_in_lefty_config() )
	{
		player_setup_lefty_angle( avatar );
		
		rightStick = self GetNormalizedMovement();
		rotSpeed = -12;
		rotDelta = rightStick[1] * rotSpeed;
		avatar.fakeAngle = AngleClamp( avatar.fakeAngle + rotDelta );
		rightStickY = avatar.fakeAngle;
	}
	else
	{
		rightStick = self GetUnNormalizedCameraMovement();
		rightStickY = rightStick[1];
		
		if ( IsDefined( avatar.fakeAngle ) )
		{
			avatar.fakeAngle = undefined;
		}
	}
	
	return rightStickY;
}

prep_for_controls( avatar, set_avatar_angles )
{
	avatar.storedRightStickY = self player_get_right_stick_y( avatar ); // storing where right stick is so we can track changes
	avatar.rotation_total = 0; // setting difference in right stick change
	avatar.storedangleY = avatar.angles[1];
	avatar.mouseRot = 0;
	avatar.positivewrap = 0;
	avatar.negativewrap = 0;
	avatar.addtobaseangle = 0;
	set_avatar_angles = (0, set_avatar_angles[1], 0); // forcing X and Z.  Avatar should only spin
	if(IsDefined(set_avatar_angles))
	{
		if(IsAgent(avatar))
		{
			avatar HackAgentAngles(set_avatar_angles);
		}
		else
			avatar.angles = set_avatar_angles;
	}
}

avatar_scheduled_for_removal( ownerid )
{
	assert( level.avatarInfo[ownerid].timeToDelete == 0 ); // this should never get set now that we never truly free avatars
	return level.avatarInfo[ownerid].timeToDelete > 0;
}

schedule_remove_avatar( ownerid, dly )
{
	remove_avatar( ownerid );
}

all_avatars_scheduled_for_delete()
{
	if (level.vlavatars.size == 0)
		return false;
	foreach ( ownerId, avatarInfo in level.avatarInfo )
	{
		if ( !IsDefined( level.vlavatars[ownerId] ) )
			continue;
		if ( avatarInfo.timeToDelete == 0 )
			return false;
	}
	return true;
}

reuse_avatar( xuid )
{
	assert( level.vlavatars.size == 1 );
	avatar = undefined;
	foreach ( xuidtmp, idx in level.xuid2ownerId )
	{
		avatar = level.vlavatars[idx];
		level.vlavatars[idx] = undefined;
		setEntPlayerXuidForEmblem( avatar, xuidtmp, true );	// release this xuid
		level.xuid2ownerId[xuidtmp] = undefined;
	}
	
	level.xuid2ownerId[xuid] = 0;
	level.avatarInfo[0].timeToDelete = 0;
	level.avatarInfo[0].xuid = xuid;
	if ( isDefined(avatar) )
	{
		setEntPlayerXuidForEmblem( avatar, xuid );	// associate with new xuid
		level.vlavatars[0] = avatar;
	}
}

add_avatar( xuid )
{
	// first find an unused ownerId
	ownerId = -1;
	oId = -1;
	while (oId == ownerId)
	{
		ownerId++;
		foreach ( oId in level.xuid2ownerId )
		{
			if (oId == ownerId)
				break;
		}
	}
	vlprint( "Adding new xuid "+xuid+" with ownerId="+ownerId+"\n");
	level.xuid2ownerId[xuid] = ownerId;
	level.avatarInfo[ownerId].xuid = xuid;
	level.avatarInfo[ownerId].timeToDelete = 0;
	return ownerId;
}

avatar_after_spawn( ownerId )
{
	if ( isdefined(level.needToPresent) )
	{
		thread SetVirtualLobbyPresentable();
	}
}

update_avatars()
{
	anyRemoved = false;
	foreach ( ownerId, avatarInfo in level.avatarInfo )
	{
		if ( ( avatarInfo.timeToDelete > 0 ) && ( GetTime() > avatarInfo.timeToDelete ) )
		{
			vlprint( "update_avatars removing ownerId"+ownerId + "\n");
			remove_avatar( ownerId );
			anyRemoved = true;
		}
	}
	if ( anyRemoved )
		wait 0.1;	// to deal with an issue where we can't spawn an agent we just deleted.
}

hide_non_owner_avatars()
{
	foreach ( idx, avatar in level.vlavatars )
	{
		if ( idx == 0 )
			continue;
		hide_avatar( avatar );
	}
	if(level.camParams.mode != "prelobby") // camera cut if we are not in pre lobby
	{
		level.camParams.camera.cut = true;
	}
	
	level.vl_focus = 0;
	level.old_vl_focus = level.vl_focus;

}

show_non_owner_avatars()
{
	foreach ( idx, avatar in level.vlavatars )
	{
		if ( idx == 0 )
			continue;
		show_avatar( avatar );
	}
}

RightStickRotateAvatar(targetAvatar, ratio)
{
	rightStickY = self player_update_right_stick_y( targetAvatar );
	rightStickDiffY = AngleClamp(rightStickY - targetAvatar.storedRightStickY);
	
	mouseRot = GetDvarFloat( "ui_mouse_char_rot", 0 );
	if ( mouseRot != 0 )
	{
		targetAvatar.mouseRot = AngleClamp(targetAvatar.mouseRot + mouseRot);
		SetDynamicDvar( "ui_mouse_char_rot", 0 ); // rotation is "consumed", reset it, UI will continue adding each frame
	}
	
	AddtoAngle = GetModifiedRotationAngle(targetAvatar, rightStickDiffY, ratio);
	AddtoAngle *= -1; // reversing rotation
	AvatarYAngle = AngleClamp(targetAvatar.storedangleY + AddtoAngle + targetAvatar.mouseRot); 
	avatar_angles = ( 0, AvatarYAngle, 0 ); // forcing X and Z.  Avatar should only spin
//	targetAvatar SetPlayerAngles(avatar_angles);
	if(IsAgent(targetAvatar))
	{
		// targetAvatar ScrAgentSetOrientMode("face angle abs", avatar_angles, avatar_angles); // doesn't work anymore on agents?
			targetAvatar SetPlayerAngles(avatar_angles);
	}
	else
	{
		// for CAC weapons or other non agents
		targetAvatar.angles = avatar_angles;
	}
}
PlayerHasTouchedStick(targetAvatar)
{
	rightStickY = self player_get_right_stick_y( targetAvatar );
	rightStickDiffY = AngleClamp(rightStickY - targetAvatar.storedRightStickY);
	if(self.HasTouchedStick == false)
	{
		if(abs(rightStickDiffY) >= 1 )
		{
			targetAvatar.storedangleY = targetAvatar.angles[1];
			return true;
		}
		else 
			return false;
	}
	else
	{
		// targetAvatar.storedangleY = targetAvatar.angles;
		return true;
	}
}

disable_player_controls()
{
	self notify("kill_enable_weapons");
//	self freezeControlsWrapper(true);
	
	self AllowFire(false);
//	self DisableWeapons();
//	self DisableOffhandWeapons();
//	self DisableWeaponSwitch();
	
}

enable_player_controls()
{
	self endon("enter_lobby");
	self endon("kill_enable_weapons");
	
//	self freezeControlsWrapper(false);
	
//	wait(2);
	
	val = GetDvarInt( "virtualLobbyInFiringRange", 0 );
	if ( ( val == 1 ) && level.in_firingrange == true)
	{
	//	self freezeControlsWrapper(false);
		self AllowFire(true);
	//	self EnableWeapons();
	//	self EnableOffhandWeapons();
	//	self EnableWeaponSwitch();
	}	
	
//	self playerAllowHighJump(false);
//	self playerAllowHighJumpDrop(false);
//	self playerAllowBoostJump(false);
//	self playerAllowPowerSlide(false);
//	self playerAllowDodge(false);
	
}

enter_vlobby( player )
{
	deactivate_targets();
	//SetDvar( "virtualLobbyInFiringRange", "0" );
	
	camera = player.camera;
	player SetOrigin(camera.origin);
	player PlayerLinkTo( camera, "tag_player" );
	player CameraLinkTo( camera, "tag_player" );
	player SetClientDVar( "cg_fovscale", "0.6153" );

	//player VisionSetNakedForPlayer("mp_virtual_lobby", 0);
	player VisionSetNakedForPlayer("mp_virtual_lobby_cac", 0);
	//player LightSetForPlayer("mp_vl_create_a_class");
	
	if(isdefined(level.vlavatars) && isdefined(level.old_vl_focus) && isdefined(level.vlavatars[level.old_vl_focus]))
	{
		player prep_for_controls(level.vlavatars[level.old_vl_focus], level.vlavatars[level.old_vl_focus].angles);
	}
	
	level.in_firingrange = false;
	player AllowFire(false);
	updateSessionState("spectator");
}

monitor_move_btn_fr_vl( player )
{
	while (true)
	{
		if ( isDefined( level.in_firingrange ) )
		{
			val = GetDvarInt( "virtualLobbyInFiringRange", 0 );
			if ( ( val == 1 ) && !level.in_firingrange )
			{
				// attempting to wait until weapon model is actually loaded	
				class_num = getClassIndex( "lobby"+(player.currentSelectedClass+1) );
				classLoc = cac_getCustomClassLoc();
				loadout = player.loadouts[classLoc][class_num];
				loadoutPrimary = loadout["primary"];
				loadoutSecondary = loadout["secondary"];
				
				if(isdefined(level.vlavatars) && isdefined(level.old_vl_focus) && isdefined(level.vlavatars[level.old_vl_focus]))
				{
					player prep_for_controls(level.vlavatars[level.old_vl_focus], level.vlavatars[level.old_vl_focus].angles);
				}
				
				WeaponNames = [];
				{
					if(isdefined(loadoutPrimary) && loadoutPrimary !="specialty_null")
					{
						WeaponNames[WeaponNames.size] = maps\mp\gametypes\_class::buildWeaponName( loadoutPrimary, loadout["primaryAttachment1"], loadout["primaryAttachment2"], loadout["primaryAttachment3"], 0, 0  );
					}
					if(isdefined(loadoutSecondary) && loadoutSecondary !="specialty_null")
					{
						WeaponNames[WeaponNames.size] = maps\mp\gametypes\_class::buildWeaponName( loadoutSecondary, loadout["secondaryAttachment1"], loadout["secondaryAttachment2"], loadout["secondaryAttachment3"], 0, 0  );
					}
				}
				
				while(WeaponNames.size > 0)
				{
					HasLoadededWeapons = player loadweapons(WeaponNames);
					if(HasLoadededWeapons == true)
					{
						break;
					}
					wait(0.05);
				}
						
				player ShowViewModel();
				
				enter_firingrange( player );
				player clientclearsoundsubmix( "mp_no_foley", 1 );				
				SetDvar("r_dof_physical_bokehEnable", 0);
				SetDvar("r_dof_physical_enable", 0);
				SetDvar("r_uiblurdstmode", 0);
				SetDvar("r_blurdstgaussianblurradius", 1);				
			}
			else if ( ( val == 0 ) && level.in_firingrange )
			{
				player HideViewModel();
				player FiringRangeCleanup();
				player disable_player_controls();
				
				player notify("enter_lobby");
				enter_vlobby( player );
				player clientaddsoundsubmix( "mp_no_foley", 1 );
				SetDvar("r_dof_physical_enable", 1);
				SetDvar("r_dof_physical_bokehEnable", 1);
				SetDvar("r_uiblurdstmode", 3);
				SetDvar("r_blurdstgaussianblurradius", 1.5);	
			}
		}
		
		wait 0.05;
	}
}

FiringRangeCleanup()
{
	player = self;
	player GrenadeCleanup(); // clean up grenades left in firing range
	player thread RiotShieldCleanup();
	weaponListOffhand = player GetWeaponsListOffhands();
	foreach( weapon in weaponListOffhand )
	{
		player maps\mp\gametypes\_class::takeOffhand( weapon );  // remove any exo abilities
	}
}


