class ActionContinuousBaseCB : ActionBaseCB
{	
	bool m_inLoop = false;
	bool m_callLoopEnd = false;
		
	bool CancelCondition()
	{
		if ( !m_ActionData )
		{
			return DefaultCancelCondition();
		}
		if ((GetState() == STATE_LOOP_LOOP || GetState() == STATE_LOOP_LOOP2 || m_inLoop) )
		{
			ActionContinuousBase actionS = ActionContinuousBase.Cast(m_ActionData.m_Action);	
			if ( m_ActionData.m_State == UA_INITIALIZE )
			{
				actionS.OnStartAnimationLoop( m_ActionData );
				m_ActionData.m_State = UA_PROCESSING;
				m_callLoopEnd = true;
			}
			
			if (!m_Interrupted)
				actionS.Do(m_ActionData, m_ActionData.m_State);
		}
		else if (m_callLoopEnd == true)
		{
			ActionContinuousBase actionE = ActionContinuousBase.Cast(m_ActionData.m_Action);
			actionE.OnEndAnimationLoop( m_ActionData );
			m_callLoopEnd = false;
		}
		return DefaultCancelCondition();
	}
	
	void SetInLoop(bool value)
	{
		 m_inLoop = value;
	}
		
	override void InitActionComponent()
	{
		m_Interrupted = false;
		m_Canceled = false;
		m_inLoop = false;

		CreateActionComponent();
		if ( m_ActionData.m_ActionComponent ) 
		{
			m_ActionData.m_ActionComponent.Init(m_ActionData);		
		}
		m_ActionData.m_State = UA_INITIALIZE;
		RegisterAnimationEvent("ActionExecStart", UA_IN_START);
		RegisterAnimationEvent("ActionExecEnd", UA_IN_END);
		RegisterAnimationEvent("ActionExec", UA_ANIM_EVENT);
		EnableCancelCondition(true);
		m_SoundObject = m_ActionData.m_Action.PlayActionSound(m_ActionData.m_Player);
	}
	
	//TODO: consider this for general use
	/*override void EndActionComponent()
	{
		ActionContinuousBase action = ActionContinuousBase.Cast(m_ActionData.m_Action);
		
		if (m_ActionData.m_State != UA_FINISHED)
			m_Canceled = true;
		
		if (m_ActionData.m_State == UA_FINISHED || m_ActionData.m_State == UA_CANCEL)
		{
			if (action.UseAlternativeInterrupt(m_ActionData))
				SetCommand(DayZPlayerConstants.CMD_ACTIONINT_FINISH);
			else
				SetCommand(DayZPlayerConstants.CMD_ACTIONINT_END);
		}
		else
		{
			SetCommand(DayZPlayerConstants.CMD_ACTIONINT_INTERRUPT);
		}
	}*/
	
	override void EndActionComponent()
	{
		// TODO for second type animation SetCommand(DayZPlayerConstants.CMD_ACTIONINT_FINISH);
		if ( m_ActionData.m_State == UA_FINISHED )
		{
			SetCommand(DayZPlayerConstants.CMD_ACTIONINT_END);
		}
		else if (m_ActionData.m_State == UA_CANCEL )
		{
			ActionContinuousBase action = ActionContinuousBase.Cast(m_ActionData.m_Action);
			if(action.HasAlternativeInterrupt())
			{
				SetCommand(DayZPlayerConstants.CMD_ACTIONINT_FINISH);
			}
			else
			{
				SetCommand(DayZPlayerConstants.CMD_ACTIONINT_END);
			}
			m_Canceled = true;
			return;
			//Cancel();
		}
		else
		{
			m_Canceled = true;
			SetCommand(DayZPlayerConstants.CMD_ACTIONINT_END);
			return;
		}
		m_ActionData.m_State = UA_FINISHED;
	}
	
	void UserEndsAction()
	{
		if ( m_ActionData.m_ActionComponent ) 
		{
			m_ActionData.m_State = m_ActionData.m_ActionComponent.Cancel(m_ActionData);
		}
		//EndActionComponent(); //already handled in 'AnimatedActionBase::Do' on cancel
	}
};

class ActionContinuousBase : AnimatedActionBase
{
	PluginAdminLog 	m_AdminLog; 
	
	void ActionContinuousBase() 
	{
		m_CallbackClass = ActionContinuousBaseCB;
		m_CommandUID = DayZPlayerConstants.CMD_ACTIONMOD_EAT;
		
		if( GetGame() && GetGame().IsServer() )
		{
			m_AdminLog = PluginAdminLog.Cast( GetPlugin(PluginAdminLog) );
		}
	}
	
	override void OnEndInput( ActionData action_data )
	{
		ActionContinuousBaseCB callback;
		if( Class.CastTo(callback, action_data.m_Callback) )
		{
			if ( callback.GetState() != callback.STATE_LOOP_END && callback.GetState() != callback.STATE_LOOP_END2 )
			{
				callback.UserEndsAction();
			}
		}
	}
	
	bool HasAlternativeInterrupt()
	{
		return false;
	}
	
	bool UseAlternativeInterrupt(ActionData action_data)
	{
		return false;
	}
	
	// lock default camera while performing the action
	bool IsCameraLockOnPerform()
	{
		return true;
	}
	
	// camera up/down angle when performing a fullbody action
	// values beetwen 0 and -90 (looking at ground) / 0 and 90 (looking above)
	Vector2 GetCameraUDAngle()
	{
		Vector2 udAngle = new Vector2(-70, 45);
		return udAngle;
	}
	
	// camera (heading) left/right angle when performing a fullbody action
	// values beetwen 0 and -180 (left restriction) / 0 and 180 (right restriction)
	Vector2 GetCameraLRAngle()
	{
		Vector2 lrAngle = new Vector2(-70, 70);
		return lrAngle;
	}

	override typename GetInputType()
	{
		return ContinuousDefaultActionInput;
	}

	override int GetActionCategory()
	{
		return AC_CONTINUOUS;
	}
	
	override void OnAnimationEvent(ActionData action_data)	
	{
		super.OnAnimationEvent(action_data);
		ActionContinuousBaseCB callback;
		if (Class.CastTo(callback, action_data.m_Callback))
		{
			if (action_data.m_DelayedAnimationEventID == UA_IN_START) 
			{
				OnStartAnimationLoop(action_data);
				callback.SetInLoop(true);
			}
			else if (action_data.m_DelayedAnimationEventID == UA_IN_END) 
			{
				OnEndAnimationLoop(action_data);
				callback.SetInLoop(false);
			}
		}
	}
	
	void OnStartAnimationLoop( ActionData action_data )
	{
		if ( LogManager.IsActionLogEnable() )
		{
			Debug.ActionLog("Time stamp: " + action_data.m_Player.GetSimulationTimeStamp(), this.ToString() , "n/a", "OnStartAnimationLoop", action_data.m_Player.ToString() );
		}
		
		if (GetGame().IsServer())
		{
			OnStartAnimationLoopServer(action_data);
		}
		else
		{
			OnStartAnimationLoopClient(action_data);
		}
		action_data.m_WasExecuted = false;
	}
	
	void OnEndAnimationLoop( ActionData action_data )
	{
		if ( LogManager.IsActionLogEnable() )
		{
			Debug.ActionLog("Time stamp: " + action_data.m_Player.GetSimulationTimeStamp(), this.ToString() , "n/a", "OnEndAnimationLoop", action_data.m_Player.ToString() );
		}
		if (GetGame().IsServer())
		{
			OnEndAnimationLoopServer(action_data);
		}
		else
		{
			OnEndAnimationLoopClient(action_data);
		}
		action_data.m_WasExecuted = false;
	}
	
	void OnFinishProgress( ActionData action_data )
	{	
		if ( LogManager.IsActionLogEnable() )
		{
			Debug.ActionLog("Time stamp: " + action_data.m_Player.GetSimulationTimeStamp(), this.ToString() , "n/a", "OnFinishProgress", action_data.m_Player.ToString() );
		}	
		if (GetGame().IsServer())
		{
			OnFinishProgressServer(action_data);
			
			if ( m_AdminLog )
			{
				m_AdminLog.OnContinouousAction( action_data );
			}
		}
		else
		{
			OnFinishProgressClient(action_data);
		}
		action_data.m_WasExecuted = false;
	}
	
	
	protected void OnStartAnimationLoopServer( ActionData action_data ) //method called on start main animation loop (after in animation part )
	{
		action_data.m_WasActionStarted  = true;
	}
	
	protected void OnStartAnimationLoopClient( ActionData action_data ) //method called on start main animation loop (after in animation part )
	{
		action_data.m_WasActionStarted  = true;
	}
	
	protected void OnEndAnimationLoopServer( ActionData action_data ) //method called on finish main animation loop (before out animation part )
	{
	}
	protected void OnEndAnimationLoopClient( ActionData action_data ) //method called on finish main animation loop (before out animation part )
	{
	}
	
	protected void OnFinishProgressServer( ActionData action_data )
	{
	}
	
	protected void OnFinishProgressClient( ActionData action_data )
	{
	}
};