enum EDebuggerColor
{
	RED = 0,
	YELLOW = 1,
}

struct DebuggerProperties
{
	instanceeditable var m_exclusiveMode : Bool;
	hint m_exclusiveMode = "If this Device starts to be debugged no other device will be debugged until this streams out";
	instanceeditable var m_factActivated : CName;
	hint m_factActivated = "If this field is not empty debugger will work only if this fact is set to 1";
	instanceeditable var m_debugName : CName;
	hint m_debugName = "DEBUG Use with DeviceListeningMode() and/or ToggleListeningMode()";
	var m_layerIDs : array< Uint32 >;
}

class RegisterDebuggerCanditateEvent extends Event
{
	var m_device : weak< Device >;
}

class DeviceDebuggerComponent extends ScriptableComponent
{
	private var m_isActive : Bool;
	private var m_exclusiveModeTriggered : Bool;
	private var m_currentDeviceProperties : DebuggerProperties;
	private var m_debuggedDevice : weak< Device >;
	private var m_debuggerColor : EDebuggerColor;
	private var m_previousContext : String;
	default m_previousContext = "NONE";
	private var m_cachedContext : String;
	default m_cachedContext = "NONE";
	private var m_layerIDs : array< Uint32 >;

	protected event OnRegisterDebuggerCandidate( evt : RegisterDebuggerCanditateEvent )
	{
		if( m_exclusiveModeTriggered && m_debuggedDevice )
		{
			if( !( IsFinal() ) )
			{
			}
			return false;
		}
		if( evt.m_device == m_debuggedDevice )
		{
			if( !( IsFinal() ) )
			{
			}
			return false;
		}
		if( !( evt.m_device.ShouldInitiateDebug() ) )
		{
			if( !( IsFinal() ) )
			{
			}
			return false;
		}
		if( evt.m_device )
		{
			if( !( IsFinal() ) )
			{
			}
			m_debuggedDevice = evt.m_device;
			m_currentDeviceProperties = m_debuggedDevice.GetDebuggerProperties();
			m_exclusiveModeTriggered = m_currentDeviceProperties.m_exclusiveMode;
			ToggleDebuggerColor();
			return true;
		}
		if( !( IsFinal() ) )
		{
		}
		return false;
	}

	protected function OnUpdate( deltaTime : Float )
	{
		var i : Int32;
		for( i = 0; i < m_layerIDs.Size(); i += 1 )
		{
			GameInstance.GetDebugVisualizerSystem( GetOwner().GetGame() ).ClearLayer( m_layerIDs[ i ] );
		}
		if( !( m_debuggedDevice ) || !( IsFactValid() ) )
		{
			return;
		}
		PerformDebug();
	}

	private function PerformDebug()
	{
		var positionLayer : Vector4;
		var positionPreviousLayer : Vector4;
		var positionDevice : Vector4;
		var positionDistance : Vector4;
		var separatorPosition : Vector4;
		var statusPosition : Vector4;
		var dynamicPosition : Vector4;
		var separator : String;
		var textLayer : String;
		var textPreviousLayer : String;
		var textDevice : String;
		var textDistance : String;
		var textStatus : String;
		var textActions : array< String >;
		var actions : array< DeviceAction >;
		var distance : Float;
		var context : GetActionsContext;
		var verticalDistance : Float;
		var verticalStartingPos : Float;
		var verticalAxis : Float;
		var i : Int32;
		m_layerIDs.Clear();
		verticalDistance = 20.0;
		verticalStartingPos = 180.0;
		verticalAxis = verticalStartingPos;
		positionDevice = Vector4( 100.0, verticalAxis, 0.0, 0.0 );
		verticalAxis += verticalDistance;
		positionLayer = Vector4( 100.0, verticalAxis, 0.0, 0.0 );
		verticalAxis += verticalDistance;
		positionPreviousLayer = Vector4( 100.0, verticalAxis, 0.0, 0.0 );
		verticalAxis += verticalDistance;
		positionDistance = Vector4( 100.0, verticalAxis, 0.0, 0.0 );
		verticalAxis += verticalDistance;
		separatorPosition = Vector4( 100.0, verticalAxis, 0.0, 0.0 );
		verticalAxis += verticalDistance;
		statusPosition = Vector4( 100.0, verticalAxis, 0.0, 0.0 );
		verticalAxis += verticalDistance;
		separator = " ------------ ";
		distance = Vector4.Distance( m_debuggedDevice.GetWorldPosition(), GameInstance.GetPlayerSystem( GetOwner().GetGame() ).GetLocalPlayerMainGameObject().GetWorldPosition() );
		m_debuggedDevice.GetActionsDebug( context, this, actions );
		textDevice = "DEVICE: " + ( ( Device )( m_debuggedDevice ) ).GetDeviceName();
		textLayer = "CONTEXT: " + NameToString( context.interactionLayerTag );
		if( textLayer != m_cachedContext )
		{
			m_previousContext = m_cachedContext;
			m_cachedContext = textLayer;
		}
		textPreviousLayer = "PREV CONTEXT: " + m_previousContext;
		textDistance = "DISTANCE :" + FloatToString( distance );
		textStatus = "STATUS: " + m_debuggedDevice.GetDeviceStatusString();
		for( i = 0; i < actions.Size(); i += 1 )
		{
			textActions.PushBack( actions[ i ].GetCurrentDisplayString() );
		}
		AddDebugBit( positionDevice, textDevice, GetColor() );
		AddDebugBit( positionLayer, textLayer, GetColor() );
		AddDebugBit( positionPreviousLayer, textPreviousLayer, GetColor( true ) );
		AddDebugBit( positionDistance, textDistance, GetColor() );
		AddDebugBit( separatorPosition, separator, GetColor() );
		AddDebugBit( statusPosition, textStatus, GetColor() );
		for( i = 0; i < textActions.Size(); i += 1 )
		{
			dynamicPosition = Vector4( 100.0, verticalAxis, 0.0, 0.0 );
			AddDebugBit( dynamicPosition, textActions[ i ], GetColor() );
			verticalAxis += verticalDistance;
		}
		if( GetQuestsSystem().GetFact( 'disableLine' ) == 0 )
		{
			DrawDbgLine();
		}
	}

	private function DrawDbgLine()
	{
		var playerPos : Vector4;
		var devicePos : Vector4;
		playerPos = GetPlayerSystem().GetLocalPlayerMainGameObject().GetWorldPosition();
		playerPos.Z += 1.5;
		devicePos = m_debuggedDevice.GetWorldPosition();
		devicePos.Z += 1.5;
		m_layerIDs.PushBack( GetDebugVisualizerSystem().DrawLine3D( playerPos, devicePos, GetColor() ) );
	}

	private function IsFactValid() : Bool
	{
		return m_debuggedDevice.ShouldInitiateDebug();
	}

	private function AddDebugBit( position : Vector4, const text : ref< String >, color : Color )
	{
		m_layerIDs.PushBack( GameInstance.GetDebugVisualizerSystem( GetOwner().GetGame() ).DrawText( position, text, , color ) );
	}

	private function GetColor( optional reverse : Bool ) : Color
	{
		if( m_debuggerColor == EDebuggerColor.RED )
		{
			if( reverse )
			{
				return SColor.Yellow();
			}
			return SColor.Red();
		}
		else
		{
			if( reverse )
			{
				return SColor.Red();
			}
			return SColor.Yellow();
		}
	}

	private function ToggleDebuggerColor()
	{
		if( m_debuggerColor == EDebuggerColor.RED )
		{
			m_debuggerColor = EDebuggerColor.YELLOW;
		}
		else
		{
			m_debuggerColor = EDebuggerColor.RED;
		}
	}

}

struct DeviceHelper
{

	public static function IDMO( gameInstance : GameInstance ) : Bool
	{
		return DeviceHelper.IsDebugModeON( gameInstance );
	}

	public static function IsDebugModeON( gameInstance : GameInstance ) : Bool
	{
		var val : Int32;
		val = GameInstance.GetQuestsSystem( gameInstance ).GetFact( 'dbgDevices' );
		if( val > 0 )
		{
			return true;
		}
		return false;
	}

	public static function DebugLog( gameInstance : GameInstance, const message : ref< String > )
	{
		if( DeviceHelper.IsDebugModeON( gameInstance ) )
		{
			GameInstance.GetActivityLogSystem( gameInstance ).AddLog( message );
		}
	}

	public static function ExtractSpecificStateClass( const states : ref< array< PersistentState > >, desiredClassName : CName, extractedStates : array< PersistentState > ) : Bool
	{
		var i : Int32;
		var wasSuccessful : Bool;
		for( i = 0; i < states.Size(); i += 1 )
		{
			if( states[ i ].GetClassName() == desiredClassName )
			{
				extractedStates.PushBack( states[ i ] );
				wasSuccessful = true;
			}
		}
		return wasSuccessful;
	}

	public static function ConvertActionsArray( const puppetActions : ref< array< PuppetAction > > ) : array< DeviceAction >
	{
		var i : Int32;
		var deviceActions : array< DeviceAction >;
		for( i = 0; i < puppetActions.Size(); i += 1 )
		{
			deviceActions.PushBack( ( ( DeviceAction )( puppetActions[ i ] ) ) );
		}
		return deviceActions;
	}

	public static function ConvertActionIntoScriptableAction( const inActions : ref< array< DeviceAction > >, outActions : ref< array< ScriptableDeviceAction > > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < inActions.Size(); i += 1 )
		{
			outActions.PushBack( ( ( ScriptableDeviceAction )( inActions[ i ] ) ) );
		}
		if( inActions.Size() == outActions.Size() )
		{
			return true;
		}
		return false;
	}

	public static function PushActionsIntoInteractionChoice( choice : ref< InteractionChoice >, const actions : ref< array< DeviceAction > > )
	{
		var i : Int32;
		for( i = 0; i < actions.Size(); i += 1 )
		{
			choice.data.PushBack( ( ( DeviceAction )( actions[ i ] ) ) );
		}
	}

	public static function FindAction( actionName : CName, const actions : ref< array< DeviceAction > >, out foundAction : DeviceAction ) : Int32
	{
		var i : Int32;
		for( i = 0; i < actions.Size(); i += 1 )
		{
			if( actions[ i ].actionName == actionName )
			{
				foundAction = actions[ i ];
				return i;
			}
		}
		return -1;
	}

	public static function RemoveActionFromArray( actionName : CName, actions : ref< array< DeviceAction > > )
	{
		var index : Int32;
		var dummyAction : DeviceAction;
		index = FindAction( actionName, actions, dummyAction );
		if( index < 0 )
		{
			return;
		}
		else
		{
			actions.Erase( index );
		}
	}

	public static function FindStatusAction( const actions : ref< array< DeviceAction > >, out status : BaseDeviceStatus ) : Int32
	{
		var i : Int32;
		for( i = 0; i < actions.Size(); i += 1 )
		{
			if( ( ( BaseDeviceStatus )( actions[ i ] ) ) )
			{
				status = ( ( BaseDeviceStatus )( actions[ i ] ) );
				return i;
			}
		}
		return -1;
	}

	public static function RemoveStatusAction( actions : ref< array< DeviceAction > > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < actions.Size(); i += 1 )
		{
			if( ( ( BaseDeviceStatus )( actions[ i ] ) ) )
			{
				actions.Erase( i );
				return true;
			}
		}
		return false;
	}

}

function DebugDevices( gameInstance : GameInstance, shouldDebug : Bool )
{
	if( shouldDebug )
	{
		SetFactValue( gameInstance, 'dbgDevices', 1 );
		if( !( IsFinal() ) )
		{
		}
	}
	else
	{
		SetFactValue( gameInstance, 'dbgDevices', 0 );
		if( !( IsFinal() ) )
		{
		}
	}
}

