import enum ConfigVarType : Uint8
{
	Bool,
	Int,
	Float,
	Name,
	IntList,
	FloatList,
	StringList,
	NameList,
}

import enum ConfigVarUpdatePolicy : Uint8
{
	Disabled,
	Immediately,
	ConfirmationRequired,
	RestartRequired,
	LoadLastCheckpointRequired,
}

import enum UserSettingsLoadStatus : Uint8
{
	NotLoaded,
	InternalError,
	FileIsMissing,
	FileIsCorrupted,
	Loaded,
	ImportedFromOldVersion,
}

import enum ConfigChangeReason : Int8
{
	Invalid,
	Accepted,
	Rejected,
	NeedsConfirmation,
	NeedsRestart,
}

import enum ConfigNotificationType : Uint8
{
	RestartRequiredConfirmed,
	RestartRequiredRejected,
	ChangesApplied,
	ChangesRejected,
	ChangesLoadLastCheckpointApplied,
	ChangesLoadLastCheckpointRejected,
	Saved,
	ErrorSaving,
	Loaded,
	LoadCanceled,
	LoadInternalError,
	Refresh,
}

import function IsLanguageVoicePackInstalled( language : CName ) : Bool;
import function RequestInstallLanguagePackDialog( language : CName );

importonly class ConfigVarListener extends IScriptable
{
	public import virtual function OnVarModified( groupPath : CName, varName : CName, varType : ConfigVarType, reason : ConfigChangeReason );
	public import function Register( groupPath : CName ) : Bool;
}

importonly class ConfigNotificationListener extends IScriptable
{
	public import virtual function OnNotify( status : ConfigNotificationType );
	public import function Register() : Bool;
	public import function Unregister() : Bool;
}

importonly class UserSettings extends IScriptable
{
	public import function HasGroup( groupPath : CName ) : Bool;
	public import function HasVar( groupPath : CName, varName : CName ) : Bool;
	public import function GetRootGroup() : ConfigGroup;
	public import function GetGroup( groupPath : CName ) : ConfigGroup;
	public import function GetVar( groupPath : CName, varName : CName ) : ConfigVar;
	public import function NeedsConfirmation() : Bool;
	public import function ConfirmChanges();
	public import function RejectChanges();
	public import function NeedsRestartToApply() : Bool;
	public import function RejectRestartToApply();
	public import function NeedsLoadLastCheckpoint() : Bool;
	public import function RejectLoadLastCheckpointChanges();
	public import function WasModifiedSinceLastSave() : Bool;
	public import function RequestNeedsRestartDialog();
	public import function RequestConfirmationDialog();
	public import function RequestRejectConfirmationDialog();
	public import function RequestLoadLastCheckpointDialog();
	public import function RequestRestoreDefaultDialog( isPreGame : Bool, onlyVisible : Bool, optional groupPath : CName );
	public import function RequestStartReplayTutorialDialog( settingsMain : IScriptable );
	public import function RestoreVarToDefault( groupPath : CName, varName : CName );
	public import function GetUserSettingsLoadStatus() : UserSettingsLoadStatus;
	public import function GetMenuIndex() : Int32;
	public import function SetMenuIndex( index : Int32 );
}

importonly class ConfigGroup extends IScriptable
{
	public import function GetParentPath() : CName;
	public import function GetPath() : CName;
	public import function GetName() : CName;
	public import function GetDisplayName() : CName;
	public import function GetGroups( isPreGame : Bool ) : array< ConfigGroup >;
	public import function GetOrder() : Int32;
	public import function GetVar( varName : CName ) : ConfigVar;
	public import function GetVars( isPreGame : Bool ) : array< ConfigVar >;
	public import function HasGroups() : Bool;
	public import function HasVars( isPreGame : Bool ) : Bool;
	public import function IsEmpty( isPreGame : Bool ) : Bool;
	public import function HasVar( varName : CName ) : Bool;
}

importonly class ConfigVar extends IScriptable
{
	public import function GetName() : CName;
	public import function GetGroupPath() : CName;
	public import function GetGroup() : ConfigGroup;
	public import function GetDisplayName() : CName;
	public import function GetDisplayNameKey( index : Int32 ) : CName;
	public import function GetDisplayNameKeysSize() : Int32;
	public import function GetDescription() : CName;
	public import function GetWarning() : CName;
	public import function GetType() : ConfigVarType;
	public import function GetUpdatePolicy() : ConfigVarUpdatePolicy;
	public import function IsInPreGame() : Bool;
	public import function IsInGame() : Bool;
	public import function IsVisible() : Bool;
	public import function IsDisabled() : Bool;
	public import function IsSubOption() : Bool;
	public import function IsPlatformSpecific() : Bool;
	public import function IsDynamic() : Bool;
	public import function IsDynamicInitialized() : Bool;
	public import function ListHasDisplayValues() : Bool;
	public import function GetOrder() : Int32;
	public import function WasModifiedSinceLastSave() : Bool;
	public import function HasRequestedValue() : Bool;
	public import function RestoreDefault() : Bool;
	public import function SetVisible( isVisible : Bool );
	public import function SetEnabled( isEnabled : Bool );
}

import class ConfigVarBool extends ConfigVar
{
	public import function SetValue( value : Bool );
	public import function GetValue() : Bool;
	public import function GetDefaultValue() : Bool;

	public function Toggle()
	{
		SetValue( !( GetValue() ) );
	}

}

importonly class ConfigVarInt extends ConfigVar
{
	public import function SetValue( value : Int32 );
	public import function GetValue() : Int32;
	public import function GetDefaultValue() : Int32;
	public import function GetMinValue() : Int32;
	public import function GetMaxValue() : Int32;
	public import function GetStepValue() : Int32;
}

importonly class ConfigVarFloat extends ConfigVar
{
	public import function SetValue( value : Float );
	public import function GetValue() : Float;
	public import function GetDefaultValue() : Float;
	public import function GetMinValue() : Float;
	public import function GetMaxValue() : Float;
	public import function GetStepValue() : Float;
}

importonly class ConfigVarName extends ConfigVar
{
	public import function SetValue( value : CName );
	public import function GetValue() : CName;
	public import function GetDefaultValue() : CName;
}

importonly class ConfigVarListInt extends ConfigVar
{
	public import function GetValueFor( index : Int32 ) : Int32;
	public import function GetValue() : Int32;
	public import function GetDefaultValue() : Int32;
	public import function GetValues() : array< Int32 >;
	public import function GetIndexFor( value : Int32 ) : Int32;
	public import function GetIndex() : Int32;
	public import function GetDefaultIndex() : Int32;
	public import function SetIndex( index : Int32 );
	public import function GetDisplayValue( index : Int32 ) : CName;
}

importonly class ConfigVarListFloat extends ConfigVar
{
	public import function GetValueFor( index : Int32 ) : Float;
	public import function GetValue() : Float;
	public import function GetDefaultValue() : Float;
	public import function GetValues() : array< Float >;
	public import function GetIndexFor( value : Float ) : Int32;
	public import function GetIndex() : Int32;
	public import function GetDefaultIndex() : Int32;
	public import function SetIndex( index : Int32 );
	public import function GetDisplayValue( index : Int32 ) : CName;
}

importonly class ConfigVarListString extends ConfigVar
{
	public import function GetValueFor( index : Int32 ) : String;
	public import function GetValue() : String;
	public import function GetDefaultValue() : String;
	public import function GetValues() : array< String >;
	public import function GetIndexFor( value : String ) : Int32;
	public import function GetIndex() : Int32;
	public import function GetDefaultIndex() : Int32;
	public import function SetIndex( index : Int32 );
	public import function GetDisplayValue( index : Int32 ) : CName;
	public import function SetValues( values : array< String > );
}

importonly class ConfigVarListName extends ConfigVar
{
	public import function GetValueFor( index : Int32 ) : CName;
	public import function GetValue() : CName;
	public import function GetDefaultValue() : CName;
	public import function GetValues() : array< CName >;
	public import function GetIndexFor( value : CName ) : Int32;
	public import function GetIndex() : Int32;
	public import function GetDefaultIndex() : Int32;
	public import function SetIndex( index : Int32 );
	public import function GetDisplayValue( index : Int32 ) : CName;
}

class GameplaySettingsSystem extends ScriptableSystem
{
	private persistent var m_gameplaySettingsListener : GameplaySettingsListener;
	private persistent var m_wasEverJohnny : Bool;

	private export function OnPlayerAttach( request : PlayerAttachRequest )
	{
		var player : PlayerPuppet;
		player = ( ( PlayerPuppet )( request.owner ) );
		if( player )
		{
			m_gameplaySettingsListener = new GameplaySettingsListener;
			m_gameplaySettingsListener.Initialize( player );
		}
	}

	private export override function OnRestored( saveVersion : Int32, gameVersion : Int32 )
	{
		var player : PlayerPuppet;
		player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( GetGameInstance() ).GetLocalPlayerControlledGameObject() ) );
		if( player )
		{
			m_gameplaySettingsListener = new GameplaySettingsListener;
			m_gameplaySettingsListener.Initialize( player );
		}
	}

	private function OnPlayerDetach( request : PlayerDetachRequest ) {}

	public static function GetGameplaySettingsSystemInstance( owner : GameObject ) : GameplaySettingsSystem
	{
		return ( ( GameplaySettingsSystem )( GameInstance.GetScriptableSystemsContainer( owner.GetGame() ).Get( 'GameplaySettingsSystem' ) ) );
	}

	public static function GetAdditiveCameraMovementsSetting( owner : GameObject ) : Float
	{
		return GetGameplaySettingsSystemInstance( owner ).m_gameplaySettingsListener.m_additiveCameraMovements;
	}

	public static function GetIsFastForwardByLine( owner : GameObject ) : Bool
	{
		return GetGameplaySettingsSystemInstance( owner ).m_gameplaySettingsListener.m_isFastForwardByLine;
	}

	public static function GetMovementDodgeEnabled( owner : GameObject ) : Bool
	{
		return GetGameplaySettingsSystemInstance( owner ).m_gameplaySettingsListener.m_movementDodgeEnabled;
	}

	public static function GetVehicleCombatHoldToShootEnabled( owner : GameObject ) : Bool
	{
		return GetGameplaySettingsSystemInstance( owner ).m_gameplaySettingsListener.m_vehicleCombatHoldToShootEnabled;
	}

	public static function WasEverJohnny( owner : GameObject ) : Bool
	{
		return GetGameplaySettingsSystemInstance( owner ).m_wasEverJohnny;
	}

	public static function SetWasEverJohnny( owner : GameObject, value : Bool )
	{
		GetGameplaySettingsSystemInstance( owner ).SetWasEverJohnny( value );
	}

	public const function GetIsFastForwardByLine() : Bool
	{
		return m_gameplaySettingsListener.m_isFastForwardByLine;
	}

	public const function GetIsInputHintEnabled() : Bool
	{
		return m_gameplaySettingsListener.m_InputHintsEnabled;
	}

	private function SetWasEverJohnny( value : Bool )
	{
		m_wasEverJohnny = value;
	}

}

class GameplaySettingsListener extends ConfigVarListener
{
	private var m_player : weak< PlayerPuppet >;
	private var m_userSettings : UserSettings;
	private var m_diffSettingsGroup : ConfigGroup;
	private var m_miscSettingsGroup : ConfigGroup;
	private var m_controlsGroup : ConfigGroup;
	private var m_vehicleControlsGroup : ConfigGroup;
	private var m_hudGroup : ConfigGroup;
	var m_additiveCameraMovements : Float;
	var m_isFastForwardByLine : Bool;
	var m_InputHintsEnabled : Bool;
	var m_movementDodgeEnabled : Bool;
	var m_vehicleCombatHoldToShootEnabled : Bool;
	private var m_difficultyPath : CName;
	default m_difficultyPath = '/accessibility/difficulty';
	private var m_miscPath : CName;
	default m_miscPath = '/gameplay/misc';
	private var m_controlsPath : CName;
	default m_controlsPath = '/controls';
	private var m_vehicleControlsPath : CName;
	default m_vehicleControlsPath = '/controls/vehicle';
	private var m_hudPath : CName;
	default m_hudPath = '/interface/hud';
	private var m_additiveCameraOptionName : CName;
	default m_additiveCameraOptionName = 'AdditiveCameraMovements';
	private var m_fastForwardOptionName : CName;
	default m_fastForwardOptionName = 'FastForward';
	private var m_inputHintsOptionName : CName;
	default m_inputHintsOptionName = 'input_hints';
	private var m_movementDodgeOptionName : CName;
	default m_movementDodgeOptionName = 'MovementDodge';
	private var m_vehicleCombatHoldToShootOptionName : CName;
	default m_vehicleCombatHoldToShootOptionName = 'VehicleCombatHoldToShoot';

	public function Initialize( player : weak< PlayerPuppet > )
	{
		m_player = player;
		m_userSettings = GameInstance.GetSettingsSystem( m_player.GetGame() );
		m_diffSettingsGroup = m_userSettings.GetGroup( m_difficultyPath );
		m_miscSettingsGroup = m_userSettings.GetGroup( m_miscPath );
		m_controlsGroup = m_userSettings.GetGroup( m_controlsPath );
		m_vehicleControlsGroup = m_userSettings.GetGroup( m_vehicleControlsPath );
		m_hudGroup = m_userSettings.GetGroup( m_hudPath );
		Register( m_difficultyPath );
		Register( m_miscPath );
		Register( m_controlsPath );
		Register( m_vehicleControlsPath );
		Register( m_hudPath );
		UpdateInputHintsEnabled();
		RefreshAdditiveCameraMovementsSetting();
		UpdateFFSetting();
		UpdateMovementDodgeSettings();
		UpdateVehicleCombatHoldToShootSettings();
		if( m_player )
		{
			m_player.OnAdditiveCameraMovementsSettingChanged();
			RestoreJohnnyRelatedState();
		}
	}

	public export override function OnVarModified( groupPath : CName, varName : CName, varType : ConfigVarType, reason : ConfigChangeReason )
	{
		if( reason != ConfigChangeReason.Accepted )
		{
			return;
		}
		switch( varName )
		{
			case m_inputHintsOptionName:
				UpdateInputHintsEnabled();
			break;
			case m_additiveCameraOptionName:
				RefreshAdditiveCameraMovementsSetting();
			m_player.OnAdditiveCameraMovementsSettingChanged();
			break;
			case m_fastForwardOptionName:
				UpdateFFSetting();
			break;
			case m_movementDodgeOptionName:
				UpdateMovementDodgeSettings();
			break;
			case m_vehicleCombatHoldToShootOptionName:
				UpdateVehicleCombatHoldToShootSettings();
			break;
			default:
				break;
		}
	}

	private function UpdateInputHintsEnabled()
	{
		var settingsVar : ConfigVarBool;
		if( m_hudGroup )
		{
			settingsVar = ( ( ConfigVarBool )( m_hudGroup.GetVar( m_inputHintsOptionName ) ) );
			if( settingsVar )
			{
				m_InputHintsEnabled = settingsVar.GetValue();
			}
		}
	}

	private function RefreshAdditiveCameraMovementsSetting()
	{
		var additiveCameraMovementsVar : ConfigVarListFloat;
		if( m_diffSettingsGroup )
		{
			additiveCameraMovementsVar = ( ( ConfigVarListFloat )( m_diffSettingsGroup.GetVar( m_additiveCameraOptionName ) ) );
			if( additiveCameraMovementsVar )
			{
				m_additiveCameraMovements = additiveCameraMovementsVar.GetValue();
			}
		}
	}

	private function RestoreJohnnyRelatedState()
	{
		var johnnyHUDVar : ConfigVar;
		if( GameplaySettingsSystem.WasEverJohnny( m_player ) )
		{
			johnnyHUDVar = m_userSettings.GetVar( '/interface/hud', 'johnny_hud' );
			johnnyHUDVar.SetVisible( true );
		}
	}

	private function UpdateFFSetting()
	{
		var FFSettingsVar : ConfigVarListString;
		if( m_miscSettingsGroup )
		{
			FFSettingsVar = ( ( ConfigVarListString )( m_miscSettingsGroup.GetVar( m_fastForwardOptionName ) ) );
			m_isFastForwardByLine = FFSettingsVar.GetIndex() == 0;
		}
	}

	private function UpdateMovementDodgeSettings()
	{
		var controlsSettingsVar : ConfigVarBool;
		if( m_controlsGroup && m_controlsGroup.HasVar( m_movementDodgeOptionName ) )
		{
			controlsSettingsVar = ( ( ConfigVarBool )( m_controlsGroup.GetVar( m_movementDodgeOptionName ) ) );
			m_movementDodgeEnabled = controlsSettingsVar.GetValue();
		}
		else
		{
			m_movementDodgeEnabled = true;
		}
	}

	private function UpdateVehicleCombatHoldToShootSettings()
	{
		var configVar : ConfigVarBool;
		if( m_vehicleControlsGroup && m_vehicleControlsGroup.HasVar( m_vehicleCombatHoldToShootOptionName ) )
		{
			configVar = ( ( ConfigVarBool )( m_vehicleControlsGroup.GetVar( m_vehicleCombatHoldToShootOptionName ) ) );
			m_vehicleCombatHoldToShootEnabled = configVar.GetValue();
		}
		else
		{
			m_vehicleCombatHoldToShootEnabled = false;
		}
	}

}

