struct SFactToChange
{
	instanceeditable var factName : CName;
	instanceeditable var factValue : Int32;
	instanceeditable var operationType : EMathOperationType;
	default operationType = EMathOperationType.Set;
}

class CPOMissionDevice extends GameObject
{
	protected instanceeditable var m_compatibleDeviceName : CName;
	protected instanceeditable var m_blockAfterOperation : Bool;
	default m_blockAfterOperation = true;
	protected instanceeditable var m_factToUnblock : CName;
	protected var m_isBlocked : Bool;
	default m_isBlocked = false;
	private var m_factUnblockCallbackID : Uint32;
	default m_factUnblockCallbackID = 0;

	protected event OnGameAttached()
	{
		super.OnGameAttached();
		RegisterFactsListener();
	}

	protected event OnDetach()
	{
		UnregisterFactsListener();
	}

	protected virtual function RegisterFactsListener()
	{
		if( m_factToUnblock != '' )
		{
			m_factUnblockCallbackID = GameInstance.GetQuestsSystem( GetGame() ).RegisterEntity( m_factToUnblock, GetEntityID() );
			m_isBlocked = GameInstance.GetQuestsSystem( GetGame() ).GetFact( m_factToUnblock ) == 0;
		}
	}

	protected virtual function UnregisterFactsListener()
	{
		if( m_factToUnblock != '' )
		{
			GameInstance.GetQuestsSystem( GetGame() ).UnregisterEntity( m_factToUnblock, m_factUnblockCallbackID );
		}
	}

	protected event OnEnabledFactChangeTrigerred( evt : FactChangedEvent )
	{
		var factName : CName;
		factName = evt.GetFactName();
		if( factName == m_factToUnblock )
		{
			m_isBlocked = GameInstance.GetQuestsSystem( GetGame() ).GetFact( m_factToUnblock ) == 0;
		}
	}

	public const function IsBlocked() : Bool
	{
		return m_isBlocked;
	}

	public const function GetCompatibleDeviceName() : CName
	{
		return m_compatibleDeviceName;
	}

	protected function SetFacts( const facts : ref< array< SFactToChange > > )
	{
		var i : Int32;
		for( i = 0; i < facts.Size(); i += 1 )
		{
			SetFact( facts[ i ].factName, facts[ i ].factValue, facts[ i ].operationType );
		}
	}

	protected function SetFact( factName : CName, factValue : Int32, factOperationType : EMathOperationType )
	{
		var newFactCount : Int32;
		if( IsNameValid( factName ) )
		{
			if( factOperationType == EMathOperationType.Set )
			{
				GameInstance.GetQuestsSystem( GetGame() ).SetFact( factName, factValue );
			}
			else
			{
				newFactCount = GameInstance.GetQuestsSystem( GetGame() ).GetFact( factName ) + factValue;
				GameInstance.GetQuestsSystem( GetGame() ).SetFact( factName, newFactCount );
			}
		}
	}

}

class CPOMissionDataAccessPoint extends CPOMissionDevice
{
	default m_blockAfterOperation = true;
	default m_isBlocked = false;
	default m_factUnblockCallbackID = 0;
	protected instanceeditable var m_hasDataToDownload : Bool;
	default m_hasDataToDownload = true;
	protected instanceeditable var m_damagesPresetName : CName;
	default m_damagesPresetName = 'CPODataRaceParams';
	protected const instanceeditable var m_factsOnDownload : array< SFactToChange >;
	protected const instanceeditable var m_factsOnUpload : array< SFactToChange >;
	protected instanceeditable var m_ownerDecidesOnTransfer : Bool;
	default m_ownerDecidesOnTransfer = false;

	public const function HasDataToDownload() : Bool
	{
		return m_hasDataToDownload;
	}

	protected event OnInteraction( choiceEvent : InteractionChoiceEvent )
	{
		var choice : String;
		var missionDataTransferred : CPOMissionDataTransferred;
		choice = choiceEvent.choice.choiceMetaData.tweakDBName;
		if( choice == "DownloadCPOMissionData" )
		{
			if( m_blockAfterOperation )
			{
				m_isBlocked = true;
			}
			ChatterHelper.PlayCpoServerSyncVoiceOver( choiceEvent.activator, 'cpo_got_data' );
			m_hasDataToDownload = false;
			SetFacts( m_factsOnDownload );
			missionDataTransferred = new CPOMissionDataTransferred;
			missionDataTransferred.dataDownloaded = true;
			missionDataTransferred.compatibleDeviceName = m_compatibleDeviceName;
			missionDataTransferred.ownerDecidesOnTransfer = m_ownerDecidesOnTransfer;
			if( IsDamagePresetValid( m_damagesPresetName ) )
			{
				missionDataTransferred.dataDamagesPresetName = m_damagesPresetName;
			}
			else
			{
				missionDataTransferred.dataDamagesPresetName = 'CPODataRaceParams';
			}
			choiceEvent.activator.QueueEvent( missionDataTransferred );
		}
		else if( choice == "UploadCPOMissionData" )
		{
			if( m_blockAfterOperation )
			{
				m_isBlocked = true;
			}
			m_hasDataToDownload = true;
			SetFacts( m_factsOnUpload );
			missionDataTransferred = new CPOMissionDataTransferred;
			missionDataTransferred.dataDownloaded = false;
			choiceEvent.activator.QueueEvent( missionDataTransferred );
		}
	}

	protected function IsDamagePresetValid( presetName : CName ) : Bool
	{
		var armorPresetTweakDBID : TweakDBID;
		var healthPresetTweakDBID : TweakDBID;
		if( presetName != '' )
		{
			armorPresetTweakDBID = TDBID.Create( ( "player." + NameToString( presetName ) ) + ".armorDPS" );
			healthPresetTweakDBID = TDBID.Create( ( "player." + NameToString( presetName ) ) + ".healthDPS" );
			if( TDBID.IsValid( armorPresetTweakDBID ) && TDBID.IsValid( healthPresetTweakDBID ) )
			{
				return true;
			}
		}
		return false;
	}

}

import class MultiplayerGiveChoiceTokenEvent extends Event
{
	import var compatibleDeviceName : CName;
	import var timeout : Uint32;
	private var m_tokenAlreadyGiven : Bool;
	default m_tokenAlreadyGiven = false;

	public import function RandomizePlayer( player : GameObject ) : GameObject;

	public function GiveChoiceToken( player : PlayerPuppet )
	{
		var transferDataEvent : CPOMissionDataTransferred;
		var clearDataEvent : CPOMissionDataTransferred;
		var playerToGiveData : GameObject;
		if( m_tokenAlreadyGiven && !( player.HasCPOMissionData() ) )
		{
			return;
		}
		if( m_tokenAlreadyGiven )
		{
			playerToGiveData = RandomizePlayer( player );
			clearDataEvent = new CPOMissionDataTransferred;
			clearDataEvent.dataDownloaded = false;
			player.QueueEvent( clearDataEvent );
		}
		else
		{
			m_tokenAlreadyGiven = true;
			playerToGiveData = player;
		}
		if( playerToGiveData )
		{
			transferDataEvent = new CPOMissionDataTransferred;
			transferDataEvent.dataDownloaded = true;
			transferDataEvent.compatibleDeviceName = compatibleDeviceName;
			transferDataEvent.ownerDecidesOnTransfer = true;
			transferDataEvent.dataDamagesPresetName = '';
			transferDataEvent.isChoiceToken = true;
			transferDataEvent.choiceTokenTimeout = timeout;
			playerToGiveData.QueueEvent( transferDataEvent );
		}
	}

	public static function CreateEvent( compatibleDeviceName : CName, timeout : Uint32 ) : MultiplayerGiveChoiceTokenEvent
	{
		var evt : MultiplayerGiveChoiceTokenEvent;
		evt = new MultiplayerGiveChoiceTokenEvent;
		evt.compatibleDeviceName = compatibleDeviceName;
		evt.timeout = timeout;
		evt.m_tokenAlreadyGiven = true;
		return evt;
	}

	public static function CreateDelayedEvent( player : GameObject, compatibleDeviceName : CName, timeout : Uint32 ) : DelayID
	{
		var evt : MultiplayerGiveChoiceTokenEvent;
		evt = CreateEvent( compatibleDeviceName, timeout );
		return GameInstance.GetDelaySystem( player.GetGame() ).DelayEvent( player, evt, ( ( Float )( timeout ) ) );
	}

}

