class TerritoryFlag extends BaseBuildingBase
{
	const float MAX_ACTION_DETECTION_ANGLE_RAD 		= 1.3;		//1.3 RAD = ~75 DEG
	const float MAX_ACTION_DETECTION_DISTANCE 		= 2.0;		//meters
	
	bool m_RefresherActive;
	bool m_RefresherActiveLocal;
	bool m_RefresherInitialized;
	int m_RefresherTimeRemaining;
	int m_RefreshTimeCounter;
	
	protected int m_FlagRefresherFrequency = GameConstants.REFRESHER_FREQUENCY_DEFAULT; 		//how often does the flag increase lifetimes
	protected int m_FlagRefresherMaxDuration = GameConstants.REFRESHER_MAX_DURATION_DEFAULT; 	//how long will the refresher run; multiple of m_FlagRefresherFrequency by default
	
	void TerritoryFlag()
	{
		m_RefresherActive = false;
		m_RefresherActiveLocal = false;
		m_RefresherInitialized = false;
		m_RefresherTimeRemaining = 0;
		
		if ( GetCEApi() )
		{
			InitRefresherData();
		}
		RegisterNetSyncVariableBool("m_RefresherActive");
	}
	
	void ~TerritoryFlag()
	{
		RemoveRefresherPosition();
	}
	
	void InitRefresherData()
	{
		int frequency = GetCEApi().GetCEGlobalInt("FlagRefreshFrequency");
		int max_duration = GetCEApi().GetCEGlobalInt("FlagRefreshMaxDuration");
		
		if ( frequency > 0)
		{
			m_FlagRefresherFrequency = frequency;
		}
		if ( max_duration > 0 )
		{
			m_FlagRefresherMaxDuration = max_duration;
		}
		m_RefresherInitialized = true;
	}
	
	override string GetConstructionKitType()
	{
		return "TerritoryFlagKit";
	}
	
	override int GetMeleeTargetType()
	{
		return EMeleeTargetType.NONALIGNABLE;
	}
	
	/*override bool NameOverride(out string output)
	{
		if ( m_GateState != GATE_STATE_NONE )
		{
			output = "#str_cfgvehicles_construction_part_gate";  //changes object displayed name if in 'gate' state
			output.ToUpper();
			return true;
		}
		return false;
	}*/
	
	//--- CONSTRUCTION KIT
	override vector GetKitSpawnPosition()
	{
		if ( MemoryPointExists( "kit_spawn_position" ) )
		{
			vector position;
			position = GetMemoryPointPos( "kit_spawn_position" );
			
			return ModelToWorld( position );
		}
		
		return GetPosition();
	}
	
	override bool CanDisplayAttachmentCategory( string category_name )
	{
		if ( category_name == "Base" && !HasBase() )
			return true;
		else if ( category_name == "Support" && HasBase() && !GetConstruction().IsPartConstructed("support") )
			return true;
		else if ( category_name == "Pole" && GetConstruction().IsPartConstructed("support") && !GetConstruction().IsPartConstructed("pole") )
			return true;
		else if ( category_name == "Flag" && GetConstruction().IsPartConstructed("pole") )
			return true;
		else
			return false;
	}
	// ---
	
	// --- EVENTS
	override void OnStoreSave( ParamsWriteContext ctx )
	{   
		super.OnStoreSave( ctx );
		
		ctx.Write( m_RefresherTimeRemaining );
		ctx.Write( m_RefreshTimeCounter );
		ctx.Write( m_FlagRefresherMaxDuration );
		ctx.Write( m_RefresherActive );
	}
	
	override bool OnStoreLoad( ParamsReadContext ctx, int version )
	{
		if ( !super.OnStoreLoad( ctx, version ) )
			return false;
		
		//int loaded_frequency;
		int loaded_max_duration;
		
		if ( !ctx.Read( m_RefresherTimeRemaining ) )
		{
			return false;
		}
		
		if ( !ctx.Read( m_RefreshTimeCounter ) )
		{
			return false;
		}
		
		if ( !ctx.Read( loaded_max_duration ) )
		{
			return false;
		}
		
		if ( version >= 118 && !ctx.Read( m_RefresherActive ) )
		{
			return false;
		}
		
		CheckLoadedVariables(loaded_max_duration);
		AnimateFlag(1 - GetRefresherTime01());
		
		return true;
	}
	
	override void AfterStoreLoad()
	{
		super.AfterStoreLoad();
		
		if (!m_RefresherInitialized && GetCEApi())
		{
			InitRefresherData();
		}
		SetSynchDirty();
		UpdateVisuals();
	}
	
	override void OnCEUpdate()
	{
		super.OnCEUpdate();
		
		int time_elapsed_rounded = Math.Round(m_ElapsedSinceLastUpdate);
		
		if ( m_RefresherTimeRemaining > 0 )
		{
			m_RefreshTimeCounter += time_elapsed_rounded;
			if ( m_RefreshTimeCounter >= m_FlagRefresherFrequency )
			{
				GetCEApi().RadiusLifetimeReset(GetPosition(),GameConstants.REFRESHER_RADIUS);
				m_RefresherTimeRemaining = Math.Clamp(m_RefresherTimeRemaining - m_RefreshTimeCounter,0,m_FlagRefresherMaxDuration);
				m_RefreshTimeCounter = 0; //possibly carry over the rest?
				AnimateFlag( 1 - GetRefresherTime01() );
			}
		}
		
		SetRefresherActive(m_RefresherTimeRemaining > 0);
	}
	
	//! Saves positions of active lifetime refreshers to MissionGameplay / MissionServer
	void HandleRefreshers()
	{
		Mission mission = GetGame().GetMission();
		if (!mission.m_ActiveRefresherLocations)
			return;
		
		int idx = mission.m_ActiveRefresherLocations.Find(GetPosition());
		if ( m_RefresherActive && idx == -1 )
		{
			InsertRefresherPosition();
		}
		else if ( !m_RefresherActive && idx > -1 )
		{
			RemoveRefresherPosition(idx);
		}
	}
	
	void SetRefresherActive(bool state)
	{
		if ( m_RefresherActive != state )
		{
			m_RefresherActive = state;
			SetSynchDirty();
			
			HandleRefreshers();
			
			//update on refresher activation / last update on refresher deactivation
			if (GetCEApi())
				GetCEApi().RadiusLifetimeReset(GetPosition(),GameConstants.REFRESHER_RADIUS);
		}
	}
	
	void InsertRefresherPosition()
	{
		Mission mission = GetGame().GetMission();
		mission.m_ActiveRefresherLocations.Insert(GetPosition());
	}
	
	void RemoveRefresherPosition(int idx = -2)
	{
		if (!GetGame())
			return;
		
		Mission mission = GetGame().GetMission();
		if (!mission || !mission.m_ActiveRefresherLocations)
			return;
		
		if (idx == -2)
		{
			idx = mission.m_ActiveRefresherLocations.Find(GetPosition());
		}
		
		if (idx > -1)
		{
			mission.m_ActiveRefresherLocations.Remove(idx);
		}
	}
	
	override void OnVariablesSynchronized()
	{
		super.OnVariablesSynchronized();
		
		if ( m_RefresherActive != m_RefresherActiveLocal )
		{
			HandleRefreshers();
			m_RefresherActiveLocal = m_RefresherActive;
		}
	}
	
	//--- BUILD EVENTS
	//CONSTRUCTION EVENTS
	override void OnPartBuiltServer( notnull Man player, string part_name, int action_id )
	{
		//ConstructionPart constrution_part = GetConstruction().GetConstructionPart( part_name );
		
		super.OnPartBuiltServer( player, part_name, action_id );
		
		//update visuals (server)
		UpdateVisuals();
	}
	
	override void OnPartDismantledServer( notnull Man player, string part_name, int action_id )
	{
		ConstructionPart constrution_part = GetConstruction().GetConstructionPart( part_name );
		
		super.OnPartDismantledServer( player, part_name, action_id );
		
		//update visuals (server)
		UpdateVisuals();
	}
	
	override void OnPartDestroyedServer( Man player, string part_name, int action_id, bool destroyed_by_connected_part = false )
	{
		super.OnPartDestroyedServer( player, part_name, action_id );
		
		//update visuals (server)
		UpdateVisuals();
	}

	//--- ATTACHMENT & CONDITIONS
	override void EEItemDetached( EntityAI item, string slot_name )
	{
		super.EEItemDetached( item, slot_name );
		
		if (GetGame().IsServer())	// reset totem state is flag is decrafted  while raised
		{
			float state = GetAnimationPhase("flag_mast");
			if (state < 1)
			{
				AnimateFlag(1);	
				SetRefreshTimer01(0);
			}
		}
	}
	
	override bool CanReceiveAttachment( EntityAI attachment, int slotId ) //TODO
	{
		if ( !super.CanReceiveAttachment(attachment, slotId) )
			return false;
		
		//manage action initiator (AT_ATTACH_TO_CONSTRUCTION)
		if ( !GetGame().IsDedicatedServer() )
		{
			PlayerBase player = PlayerBase.Cast( GetGame().GetPlayer() );
			if ( player )
			{
				ConstructionActionData construction_action_data = player.GetConstructionActionData();
				
				//reset action initiator
				construction_action_data.SetActionInitiator( NULL );				
			}
		}
		
		//conditions
		string slot_name = InventorySlots.GetSlotName(slotId);
		//flag item attachment
		if ( slot_name == "Material_FPole_Flag" )
		{
			if ( GetConstruction().IsPartConstructed("pole") )
				return true;
			else
				return false;
		}
		//materials
		ConstructionPart part;
		ConstructionPart part_lowest;
		int part_id = -1;
		for (int i = 0; i < GetConstruction().GetConstructionParts().Count(); i++)
		{
			part = GetConstruction().GetConstructionParts().GetElement(i);
			if (!part.IsBuilt())
			{
				if (part_id == -1 || part_id > part.GetId())
				{
					part_lowest = part;
					part_id = part.GetId();
				}
			}
		}
		
		if (part_lowest /*&& !part_lowest.IsBuilt()*/)
		{
			string cfg_path = "cfgVehicles " + GetType() + " Construction " + part_lowest.GetMainPartName() + " " + part_lowest.GetPartName() + " Materials";
			string material_path;
			if ( GetGame().ConfigIsExisting(cfg_path) )
			{
				string child_name;
				string child_slot_name;
				for ( i = 0; i < GetGame().ConfigGetChildrenCount( cfg_path ); i++ )
				{
					GetGame().ConfigGetChildName( cfg_path, i, child_name );
					material_path = "" + cfg_path + " " + child_name + " slot_name";
					if ( GetGame().ConfigGetText(material_path,child_slot_name) && child_slot_name == slot_name )
					{
						return true;
					}
				}
			}
		}
		
		return false;
	}
	
	//hands
	override bool CanPutIntoHands( EntityAI parent )
	{
		return false;
	}
	
	override bool CanBeRepairedToPristine()
	{
		return true;
	}
	
	//--- ACTION CONDITIONS
	override bool IsPlayerInside( PlayerBase player, string selection )
	{
		return true; //TODO
		
		vector player_pos = player.GetPosition();
		vector fence_pos = GetPosition();
		vector ref_dir = GetDirection();
		ref_dir[1] = 0;
		ref_dir.Normalize();
		 
		vector x[2];
		vector b1,b2;
		GetCollisionBox(x);
		b1 = x[0];
		b2 = x[1];

		vector dir_to_fence = fence_pos - player_pos;
		dir_to_fence[1] = 0;
		float len = dir_to_fence.Length();

		dir_to_fence.Normalize();
		
		vector ref_dir_angle = ref_dir.VectorToAngles();
		vector dir_to_fence_angle = dir_to_fence.VectorToAngles();
		vector test_angles = dir_to_fence_angle - ref_dir_angle;
		
		vector test_position = test_angles.AnglesToVector() * len;
		
		if(test_position[0] < b1[0] || test_position[0] > b2[0] || test_position[2] < 0.2 || test_position[2] > 2.2 )
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	
	override bool IsFacingPlayer( PlayerBase player, string selection )
	{
		return true; //TODO
		
		vector fence_pos = GetPosition();
		vector player_pos = player.GetPosition();
		vector ref_dir = GetDirection();
		
		//vector fence_player_dir = player_pos - fence_pos;
		vector fence_player_dir = player.GetDirection();
		fence_player_dir.Normalize();
		fence_player_dir[1] = 0; 	//ignore height
		
		ref_dir.Normalize();
		ref_dir[1] = 0;			//ignore height
		
		if ( ref_dir.Length() != 0 )
		{
			float angle = Math.Acos( fence_player_dir * ref_dir );
			
			if ( angle >= MAX_ACTION_DETECTION_ANGLE_RAD )
			{
				return true;
			}
		}
		
		return false;
	}
	
	override bool IsFacingCamera( string selection )
	{
		return false; //TODO
		
		vector ref_dir = GetDirection();
		vector cam_dir = GetGame().GetCurrentCameraDirection();
		
		//ref_dir = GetGame().GetCurrentCameraPosition() - GetPosition();
		ref_dir.Normalize();
		ref_dir[1] = 0;		//ignore height
		
		cam_dir.Normalize();
		cam_dir[1] = 0;		//ignore height
		
		if ( ref_dir.Length() != 0 )
		{
			float angle = Math.Acos( cam_dir * ref_dir );
			
			if ( angle >= MAX_ACTION_DETECTION_ANGLE_RAD )
			{
				return true;
			}
		}

		return false;
	}
	
	override bool HasProperDistance( string selection, PlayerBase player )
	{
		//TODO
		if ( MemoryPointExists( selection ) )
		{
			vector selection_pos = ModelToWorld( GetMemoryPointPos( selection ) );
			float distance = vector.Distance( selection_pos, player.GetPosition() );
			if ( distance >= MAX_ACTION_DETECTION_DISTANCE )
			{
				return false;
			}
		}
		
		return true;
	}
	
	//================================================================
	// SOUNDS
	//================================================================
	//TODO
	
	override void SetActions()
	{
		super.SetActions();
		
		AddAction(ActionRaiseFlag);
		AddAction(ActionLowerFlag);
		AddAction(ActionFoldBaseBuildingObject);
	}
	
	/*override void UpdateVisuals()
	{
		super.UpdateVisuals();
	}*/
	
	//================================================================
	// FLAG MANIPULATION + REFRESHER
	//================================================================
	void AnimateFlagEx(float delta, PlayerBase player = null)
	{
		float temp = Math.Clamp( delta,0,1 );
		float phase_new;
		if (temp < 0.01 || temp > 0.99)
		{
			phase_new = Math.Round(temp);
		}
		else
			phase_new = temp;
		
		SetAnimationPhase("flag_mast",phase_new);
		
		if (player)
			LogAnimateFlag(phase_new, player);
		
		GetInventory().SetSlotLock(InventorySlots.GetSlotIdFromString("Material_FPole_Flag"),phase_new != 1);
		
	}
	
	void AnimateFlag(float delta)
	{
		AnimateFlagEx(delta);
	}
	
	protected void LogAnimateFlag(float newPhase, notnull PlayerBase player)
	{
		PluginAdminLog logs = PluginAdminLog.Cast(GetPlugin(PluginAdminLog));
		if (newPhase == 0)
		{
			// at the top(it's inverted)
			logs.TotemFlagChange(true, player, this);
		}
		else if (newPhase == 1)
		{
			// at the bottom(it's inverted)
			logs.TotemFlagChange(false, player, this);
		}

	}
	
	void SetRefreshTimer01(float fraction)
	{
		float temp = Math.Clamp(m_FlagRefresherMaxDuration * fraction, 0, m_FlagRefresherMaxDuration);
		m_RefresherTimeRemaining = Math.Round(temp);
	}
	
	/*void AddRefresherTimeFlat(float seconds) //seconds?
	{
		float temp = Math.Clamp(m_RefresherTimeRemaining + seconds, 0, m_FlagRefresherMaxDuration);
		m_RefresherTimeRemaining = Math.Round(temp);
		SetRefresherActive(m_RefresherTimeRemaining > 0);
	}*/
	
	void AddRefresherTime01(float fraction)
	{
		float temp = Math.Clamp(m_RefresherTimeRemaining + (fraction * m_FlagRefresherMaxDuration), 0, m_FlagRefresherMaxDuration);
		m_RefresherTimeRemaining = Math.Round(temp);
		SetRefresherActive(m_RefresherTimeRemaining > 0);
		//Print(m_RefresherTimeRemaining);
	}
	
	float GetRefresherTime01()
	{
		return m_RefresherTimeRemaining / m_FlagRefresherMaxDuration;
	}
	
	void CheckLoadedVariables(int max_duration)
	{
		if (max_duration != m_FlagRefresherMaxDuration)
		{
			m_RefresherTimeRemaining = m_FlagRefresherMaxDuration * m_RefresherTimeRemaining / max_duration;
		}
	}
	
	//================================================================
	// DEBUG
	//================================================================	
	/*
	override void DebugCustomState()
	{
		//debug
		m_SyncParts01 = 881;		//full fence with gate
		m_HasHinges = true;
		m_HasBase = true;
		
		OnVariablesSynchronized();
	}
	*/
	
	//Debug menu Spawn Ground Special
	override void OnDebugSpawn()
	{
		super.OnDebugSpawn();

		GetInventory().CreateInInventory("Flag_DayZ");
		AnimateFlag(0);
		AddRefresherTime01(1);
	}
}
