class PlatformController extends ScriptableDeviceComponent
{

	public const override function GetPS() : PlatformControllerPS
	{
		return ( ( PlatformControllerPS )( GetBasePS() ) );
	}

}

class PlatformControllerPS extends ScriptableDeviceComponentPS
{
	[ category = "Moving platform Setup" ][ tooltip = "Define node refs for your stops." ][ unsavable = "true" ]
	protected const instanceeditable persistent var m_floors : array< NodeRef >;
	[ category = "Moving platform Setup" ][ tooltip = "Starting point for your platform. Floors starts from 0." ][ rangeMin = "0" ][ unsavable = "true" ]
	protected instanceeditable persistent var m_startingFloor : Int32;
	[ category = "Moving platform Setup" ][ tooltip = "Speed of moving platform. Default floorIndex is 0.5f." ][ unsavable = "true" ]
	protected instanceeditable persistent var m_speed : Float;
	default m_speed = 0.5f;
	[ category = "Moving platform Setup" ][ tooltip = "Thanks to curves you can have controll over moving dynamic. \n base/gameplay/curves/devices/movingplatform.curveset" ][ unsavable = "true" ]
	protected instanceeditable persistent var m_curve : CName;
	default m_curve = 'cosine';
	private var m_errorMSG : String;
	private persistent var m_nextFloor : Int32;
	private persistent var m_prevFloor : Int32;
	private persistent var m_destinationFloor : Int32;
	private persistent var m_currentFloor : Int32;
	private persistent var m_isPlayerOnPlatform : Bool;
	private persistent var m_isMoving : Bool;
	private persistent var m_paused : Bool;
	private persistent var m_pausingTime : Float;

	protected function SetNextFloor( floorIndx : Int32 )
	{
		if( floorIndx >= m_floors.Size() )
		{
			m_nextFloor = m_floors.Size() - 1;
			return;
		}
		m_nextFloor = floorIndx;
	}

	protected function SetPrevFloor( floorIndx : Int32 )
	{
		if( floorIndx < 0 )
		{
			m_prevFloor = 0;
			return;
		}
		m_prevFloor = floorIndx;
	}

	protected function SetDestination( floorIndx : Int32 )
	{
		m_destinationFloor = Clamp( floorIndx, 0, m_floors.Size() - 1 );
		LinkPlatforms();
	}

	public function SetCurrentFloor( floorIndx : Int32 )
	{
		m_currentFloor = floorIndx;
		LinkPlatforms();
	}

	public function SetIsMoving( value : Bool )
	{
		m_isMoving = value;
	}

	public function SetPlayerOnPlatform( value : Bool ) : EntityNotificationType
	{
		SendPSChangedEvent();
		m_isPlayerOnPlatform = value;
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function SetPauseTime( time : Float )
	{
		m_pausingTime = time;
	}

	public function GetError() : String
	{
		return m_errorMSG;
	}

	public function GetSpeed() : Float
	{
		return m_speed;
	}

	public function GetCurveName() : CName
	{
		return m_curve;
	}

	public function GetFloorNode( floor : Int32 ) : NodeRef
	{
		return m_floors[ floor ];
	}

	public function GetDestinationNode() : NodeRef
	{
		return m_floors[ m_destinationFloor ];
	}

	public function IsPaused() : Bool
	{
		return m_paused;
	}

	public function GetResumeTime() : Float
	{
		return m_pausingTime;
	}

	public quest const function IsPlayerOnPlatform( isInverted : Bool ) : Bool
	{
		if( isInverted )
		{
			return !( m_isPlayerOnPlatform );
		}
		return m_isPlayerOnPlatform;
	}

	public quest const function IsMoving( isInverted : Bool ) : Bool
	{
		if( isInverted )
		{
			return !( m_isMoving );
		}
		return m_isMoving;
	}

	public quest const function IsAtFloor( floor : Int32 ) : Bool
	{
		return m_currentFloor == floor;
	}

	protected override function Initialize()
	{
		super.Initialize();
		if( !( IsInitialized() ) )
		{
			m_destinationFloor = m_startingFloor;
			m_currentFloor = m_startingFloor;
		}
		if( ValidateFloors() )
		{
			LinkPlatforms();
		}
	}

	private function ValidateFloors() : Bool
	{
		var i : Int32;
		var definedFloorsRef : Int32;
		for( i; i < m_floors.Size(); i += 1 )
		{
			if( IsNodeRefDefined( m_floors[ i ] ) )
			{
				definedFloorsRef += 1;
			}
			if( definedFloorsRef >= 2 )
			{
				return true;
			}
		}
		m_errorMSG = "Node ref is empty. Check your controller settings. You need at least 2 definedFloorsRef defined to move your platform.";
		return false;
	}

	public function LinkPlatforms()
	{
		SetNextFloor( m_currentFloor + 1 );
		SetPrevFloor( m_currentFloor - 1 );
	}

	protected function OnQuestMoveToFloor( evt : QuestMoveToFloor ) : EntityNotificationType
	{
		var floorIndx : Int32;
		var prop : array< DeviceActionProperty >;
		prop = evt.GetProperties();
		DeviceActionPropertyFunctions.GetProperty_Int( prop[ 0 ], floorIndx );
		SetDestination( floorIndx );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuestMoveToNextFloor( evt : QuestMoveToNextFloor ) : EntityNotificationType
	{
		evt.floor = GetFloorNode( m_nextFloor );
		SetDestination( m_nextFloor );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnQuestMoveToPrevFloor( evt : QuestMoveToPrevFloor ) : EntityNotificationType
	{
		evt.floor = GetFloorNode( m_prevFloor );
		SetDestination( m_prevFloor );
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected constexpr function OnQuestPause( evt : QuestPause ) : EntityNotificationType
	{
		m_paused = true;
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected constexpr function OnQuestResume( evt : QuestResume ) : EntityNotificationType
	{
		m_paused = false;
		evt.pauseTime = m_pausingTime;
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnArrivedAt( evt : ArrivedAt ) : EntityNotificationType
	{
		SetCurrentFloor( m_destinationFloor );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public override function GetActions( out actions : array< DeviceAction >, context : GetActionsContext ) : Bool
	{
		var result : Bool;
		if( !( super.GetActions( actions, context ) ) )
		{
			result = false;
		}
		else if( QuestMoveToNextFloor.IsDefaultConditionMet( this, context ) )
		{
			actions.PushBack( ActionToggleON() );
			actions.PushBack( ActionMoveToNextFloor() );
			actions.PushBack( ActionMoveToPrevFloor() );
			result = true;
		}
		return result;
	}

	public override function GetQuestActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		super.GetQuestActions( outActions, context );
		outActions.PushBack( ActionQuestMoveToFloor() );
		outActions.PushBack( ActionMoveToNextFloor() );
		outActions.PushBack( ActionMoveToPrevFloor() );
		outActions.PushBack( ActionQuestPause() );
		outActions.PushBack( ActionQuestResume() );
	}

	public override function ActionToggleON() : ToggleON
	{
		var action : ToggleON;
		action = new ToggleON;
		action.clearanceLevel = DefaultActionsParametersHolder.GetToggleOnClearance();
		action.SetUp( this );
		action.SetProperties( m_deviceState );
		action.AddDeviceName( m_deviceName );
		action.CreateActionWidgetPackage();
		return action;
	}

	public function ActionQuestMoveToFloor() : QuestMoveToFloor
	{
		var action : QuestMoveToFloor;
		action = new QuestMoveToFloor;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties( -9999 );
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public function ActionMoveToNextFloor() : QuestMoveToNextFloor
	{
		var action : QuestMoveToNextFloor;
		action = new QuestMoveToNextFloor;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.CreateActionWidgetPackage();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public function ActionMoveToPrevFloor() : QuestMoveToPrevFloor
	{
		var action : QuestMoveToPrevFloor;
		action = new QuestMoveToPrevFloor;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.CreateActionWidgetPackage();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public function ActionQuestPause() : QuestPause
	{
		var action : QuestPause;
		action = new QuestPause;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public function ActionQuestResume() : QuestResume
	{
		var action : QuestResume;
		action = new QuestResume;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

}

