enum LockAction
{
	NONE,
	DIAL_NUMBER_CHANED,
	DIAL_INDEX_CHANGED,
	LOCKED,
	UNLOCKED,
	
	COUNT
}

class CombinationLock extends ItemBase
{
	int m_LockDigits;					//how many digits will the combination contain
	int m_Combination;					//actual combination that is dialed on lock
	int m_CombinationLocked;			//combination that was dialed on lock before the shuffle
	int m_DialIndex;					//index of current combination dial
	protected bool m_IsLocked;
	
	protected LockAction m_LockActionPerformed 		= LockAction.NONE;
	
	protected bool m_IsInitialized;
	
	//Sounds
	//build
	const string SOUND_LOCK_OPEN 			= "combinationlock_open_SoundSet";
	const string SOUND_LOCK_CLOSE 			= "combinationlock_close_SoundSet";
	const string SOUND_LOCK_CHANGE_NUMBER	= "combinationlock_changenumber_SoundSet";
	const string SOUND_LOCK_CHANGE_DIAL		= "combinationlock_changedial_SoundSet";

	protected EffectSound m_Sound;

	void CombinationLock()
	{
		SetBaseLockValues();
		
		//synchronized variables
		int combination_length = Math.Pow( 10, m_LockDigits );
		RegisterNetSyncVariableBool( "m_IsLocked" );
		RegisterNetSyncVariableInt( "m_Combination", 0, combination_length - 1 );
		RegisterNetSyncVariableInt( "m_DialIndex", 0, m_LockDigits - 1 );
		RegisterNetSyncVariableInt( "m_LockActionPerformed", 0, LockAction.COUNT );
	}
	
	protected void SetBaseLockValues()
	{
		//set lock init values
		m_LockDigits 			= 3;
		m_Combination		 	= 111;
		m_CombinationLocked 	= 999;		
		m_IsLocked 				= false;
	}

	override void EEInit()
	{
		super.EEInit();
		
		GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( SetInitialized, 1000, false );
		//SetInitialized();
		
		//set visual on init
		UpdateVisuals();
	}
	
	void SetInitialized()
	{
		m_IsInitialized = true;
	}
	
	override bool IsInitialized()
	{
		return m_IsInitialized;
	}
	
	override void OnItemLocationChanged( EntityAI old_owner, EntityAI new_owner ) 
	{
		super.OnItemLocationChanged( old_owner, new_owner );
		
		//Check combination lock
		if ( GetGame().IsServer() )
		{
			if ( IsInitialized() && new_owner && new_owner.IsInherited( BaseBuildingBase ) )
			{
				LockServer( new_owner );
			}
		}		
	}	
	
	// --- EVENTS
	override void OnStoreSave( ParamsWriteContext ctx )
	{   
		super.OnStoreSave( ctx );
		
		//write data
		ctx.Write( m_Combination );
		ctx.Write( m_CombinationLocked );
	}
	
	override bool OnStoreLoad( ParamsReadContext ctx, int version )
	{
		if ( !super.OnStoreLoad( ctx, version ) )
			return false;
		
		//--- Combination Lock data ---
		//combination
		if ( !ctx.Read( m_Combination ) )
		{
			m_Combination = 0;
			return false;
		}
		
		//combination locked
		if ( !ctx.Read( m_CombinationLocked ) )
		{
			m_CombinationLocked = 0;
			return false;
		}
		
		//is lock attached
		if ( version < 105 )					//removed in 105
		{
			bool is_lock_attached;
			if ( !ctx.Read( is_lock_attached ) )
			{
				return false;
			}
		}

		return true;
	}
	
	override void AfterStoreLoad()
	{	
		super.AfterStoreLoad();		
		
		//Check combination lock
		if ( GetGame().IsServer() )
		{
			EntityAI parent = GetHierarchyParent();
			if ( parent && parent.IsInherited( BaseBuildingBase ) )
			{
				LockServer( parent, true );
			}
		}		
		
		//synchronize
		Synchronize();
	}
	
	// --- SYNCHRONIZATION
	void Synchronize()
	{
		if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] CombinationLock.Synchronize " + " m_Combination=" + m_Combination + " m_CombinationLocked=" + m_CombinationLocked);
		if ( GetGame().IsServer() )
		{
			SetSynchDirty();
			GetGame().GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( ResetActionVar, 1000);//synced var used to trigger client sound needs to be reset after triggering the sound
			UpdateVisuals();
		}
	}

	
	void ResetActionVar()
	{
		m_LockActionPerformed = LockAction.NONE;
	}
	
	override void OnVariablesSynchronized()
	{
		super.OnVariablesSynchronized();
		//update visuals (client)
		UpdateVisuals();
		
		//update sound (client)
		if (m_LockActionPerformed)
			UpdateSound();
		
		if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] CombinationLock.OnVariablesSynchronized " + " m_Combination=" + m_Combination + " m_CombinationLocked=" + m_CombinationLocked);
	}
	
	void SetCombination( int combination )
	{
		m_Combination = combination;
	}
	
	void SetCombinationLocked( int combination )
	{
		m_CombinationLocked = combination;
	}
	
	int GetCombination()
	{
		return m_Combination;
	}
	
	int GetLockDigits()
	{
		return m_LockDigits;
	}
	
	// --- ACTIONS
	void DialNextNumber()
	{
		string combination_text = m_Combination.ToString();
		string dialed_text;
		
		//insert zeros to dials with 0 value
		int length_diff = m_LockDigits - combination_text.Length();
		for ( int i = 0; i < length_diff; ++i )
		{
			combination_text = "0" + combination_text;
		}
		
		//assemble the whole combination with increased part
		for ( int j = 0; j < combination_text.Length(); ++j )
		{
			if ( j == m_DialIndex )
			{
				int next_dialed_number = combination_text.Get( j ).ToInt() + 1;
				if ( next_dialed_number > 9 )
				{
					next_dialed_number = 0;
				}
				
				dialed_text += next_dialed_number.ToString();
			}
			else
			{
				dialed_text += combination_text.Get( j );
			}
		}
		
		//set new number		
		SetCombination( dialed_text.ToInt() );
		m_LockActionPerformed = LockAction.DIAL_INDEX_CHANGED;
		CheckLockedStateServer();
		
		//synchronize
		Synchronize();
	}
	
	int GetDialIndex()
	{
		return m_DialIndex;
	}
	
	void SetNextDial()
	{
		if ( m_LockDigits > 1 )
		{
			if ( m_DialIndex <= m_LockDigits - 2 )
			{
				m_DialIndex++;
			}
			else if ( m_DialIndex >= m_LockDigits >  - 1 )
			{
				m_DialIndex = 0;
			}
		}
		else
		{
			m_DialIndex = 0;
		}
		
		//performed action
		m_LockActionPerformed = LockAction.DIAL_NUMBER_CHANED;
		//synchronize
		Synchronize();
	}	
	
	//Lock lock
	void LockServer( EntityAI parent, bool ignore_combination = false )
	{
		if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] CombinationLock.LockServer " + " m_Combination=" + m_Combination + " m_CombinationLocked=" + m_CombinationLocked);
		if ( IsLockAttached() )
		{
			if ( !ignore_combination )
			{
				SetCombinationLocked( m_Combination );
			
				//set slot lock
				InventoryLocation inventory_location = new InventoryLocation;
				GetInventory().GetCurrentInventoryLocation( inventory_location );		
				parent.GetInventory().SetSlotLock( inventory_location.GetSlot(), true );
			
				m_LockActionPerformed = LockAction.LOCKED;
			}
			ShuffleLock();
			SetTakeable(false);
			CheckLockedStateServer();
			//synchronize
			Synchronize();
		}
		
		//reset performed action
		//m_LockActionPerformed = LockAction.NONE;
	}
	
	void UnlockServer( EntityAI player, EntityAI parent )
	{
		if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] CombinationLock.UnlockServer " + " m_Combination=" + m_Combination + " m_CombinationLocked=" + m_CombinationLocked);
		if ( IsLockAttached() )
		{
			Fence fence = Fence.Cast( parent );
			
			//set slot unlock
			InventoryLocation inventory_location = new InventoryLocation;
			GetInventory().GetCurrentInventoryLocation( inventory_location );			
			fence.GetInventory().SetSlotLock( inventory_location.GetSlot(), false );			
	
			//drop entity from attachment slot
			if (GetGame().IsMultiplayer())
			{
				if (player)
					player.ServerDropEntity(this);
				else
					parent.GetInventory().DropEntity(InventoryMode.SERVER, parent, this);
			}
			else 
			{
				if (player)
					player.LocalDropEntity(this);
				else
					parent.GetInventory().DropEntity(InventoryMode.LOCAL, parent, this);
			}
				
			SetPosition( fence.GetKitSpawnPosition() );
			PlaceOnSurface();
			
			m_LockActionPerformed = LockAction.UNLOCKED;
			SetTakeable(true);
			CheckLockedStateServer();
			//synchronize
			Synchronize();
		}
		
		//reset performed action
		//m_LockActionPerformed = LockAction.NONE;		
	}
		
	//Shuffle lock
	void ShuffleLock()
	{
		string combination_text = m_Combination.ToString();
		string shuffled_text;
		
		//insert zeros to dials with 0 value
		int length_diff = m_LockDigits - combination_text.Length();
		for ( int i = 0; i < length_diff; ++i )
		{
			combination_text = "0" + combination_text;
		}
		
		//assemble the whole combination with increased part
		for ( int j = 0; j < combination_text.Length(); ++j )
		{
			int dial_number = combination_text.Get( j ).ToInt();
			dial_number = ( dial_number + Math.RandomInt( 1, 9 ) ) % 10;
			shuffled_text = shuffled_text + dial_number.ToString();
		}
		
		SetCombination( shuffled_text.ToInt() );
	}
	
	bool IsLocked()
	{
		return m_IsLocked;
	}
	
	void CheckLockedStateServer()
	{
		m_IsLocked = m_Combination != m_CombinationLocked;
	}
	
	bool IsLockedOnGate()
	{
		Fence fence = Fence.Cast( GetHierarchyParent() );
		if ( fence )
		{
			if ( IsLocked() )
			{
				return true;
			}
		}
		
		return false;
	}
	
	bool IsLockAttached()
	{
		Fence fence = Fence.Cast( GetHierarchyParent() );
		if ( fence )
		{
			return true;
		}
		
		return false;
	}
	
	//destroy lock
	void DestroyLock()
	{
		GetGame().ObjectDelete( this );
	}
		
	// --- VISUALS
	void UpdateVisuals()
	{
		//Client/Server
		if ( IsLockedOnGate() )
		{
			GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( HideItem, 		0, false );
			GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( ShowAttached, 	0, false );
		}
		else
		{
			GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( ShowItem, 		0, false );
			GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( HideAttached, 	0, false );
		}
	}
	
	void UpdateSound()
	{
		//was locked
		if ( m_LockActionPerformed == LockAction.LOCKED )
		{
			SoundLockClose();
		}
		
		//was unlocked
		if ( m_LockActionPerformed == LockAction.UNLOCKED )
		{
			SoundLockOpen();
		}
		
		//next dial index
		if ( m_LockActionPerformed == LockAction.DIAL_INDEX_CHANGED )
		{
			SoundLockChangeDial();
		}		
		
		//dialed new number
		if ( m_LockActionPerformed == LockAction.DIAL_NUMBER_CHANED )
		{
			SoundLockChangeNumber();
		}
	}
	
	//Show/Hide anims
	protected void ShowItem()
	{
		SetAnimationPhase( "Combination_Lock_Item", 0 );
		SetAnimationPhase( "Lock_Item_1", 			0 );
		SetAnimationPhase( "Lock_Item_2", 			0 );
	}
	
	protected void HideItem()
	{
		SetAnimationPhase( "Combination_Lock_Item", 1 );
		SetAnimationPhase( "Lock_Item_1", 			1 );
		SetAnimationPhase( "Lock_Item_2", 			1 );
	}
	
	protected void ShowAttached()
	{
		SetAnimationPhase( "Combination_Lock_Attached", 0 );
		SetAnimationPhase( "Lock_Attached_1", 			0 );
		SetAnimationPhase( "Lock_Attached_2", 			0 );
	}
	
	protected void HideAttached()
	{
		SetAnimationPhase( "Combination_Lock_Attached", 1 );
		SetAnimationPhase( "Lock_Attached_1", 			1 );
		SetAnimationPhase( "Lock_Attached_2", 			1 );
	}	
	// ---	
	
	//================================================================
	// SOUNDS
	//================================================================
	protected void SoundLockOpen()
	{
		PlaySoundSet( m_Sound, SOUND_LOCK_OPEN, 0, 0 );
	}

	protected void SoundLockClose()
	{
		PlaySoundSet( m_Sound, SOUND_LOCK_CLOSE, 0, 0 );
	}
	
	void SoundLockChangeNumber()
	{
		PlaySoundSet( m_Sound, SOUND_LOCK_CHANGE_NUMBER, 0, 0 );
	}

	void SoundLockChangeDial()
	{
		PlaySoundSet( m_Sound, SOUND_LOCK_CHANGE_DIAL, 0, 0 );
	}	
	
	override void SetActions()
	{
		super.SetActions();

		AddAction(ActionAttachToConstruction);		
		AddAction(ActionNextCombinationLockDial);
		AddAction(ActionDialCombinationLock);
		AddAction(ActionNextCombinationLockDialOnTarget);
		AddAction(ActionDialCombinationLockOnTarget);
	}
}
