UI/Puzzle/LockPanelInspector.cs
using Opium;
using Sandbox.UI;
using Sandbox.UI.Construct;
using static CombinationLockInteractor;
using static LockPanelInspector.Lock;

public class LockPanelInspector : Panel
{
	public LockType LockType { get; set; }
	public string StartingCombination { get; set; } = "1234";
	public CombinationLockInteractor CombinationLockInteractor { get; set; }
	public CombinationLockUI CombinationLockUI { get; set; }

	private SceneWorld World;
	private ScenePanel ScenePanel;
	private Vector3 camoffset;
	protected override void OnParametersSet()
	{
		base.OnParametersSet();

		Rebuild();
	}

	public void Rebuild()
	{
		ScenePanel?.Delete( true );
		ScenePanel = null;

		World?.Delete();
		World = null;

		World = new();

		CreatePadLock( LockType );
		PadLockModel.SetMaterialGroup( "view" );
		PadLockModel.Attributes.SetCombo( "D_VERTEX_SNAP", 0 );

		camoffset = Vector3.Backward * 8;
		camoffset += Vector3.Left * 2.5f + Vector3.Down * 3f;
		var camrot = Rotation.From( 0, 0, -90 );
		camrot *= Rotation.From( 15, -20, 0 );

		ScenePanel = Add.ScenePanel( World, camoffset, camrot, 75 );


		new SceneDirectionalLight( World, Rotation.From( 50, 40, 0 ), Color.White * 1.0f + Color.Cyan * 0.05f );

		ScenePanel.Style.Width = Length.Percent( 100 );
		ScenePanel.Style.Height = Length.Percent( 100 );
	}

	public override void Tick()
	{
		base.Tick();

		if ( CombinationLockInteractor.Type == LockType.PadlockA )
		{
			if ( Input.Pressed( "left" ) ) PreviousDial();
			if ( Input.Pressed( "right" ) ) NextDial();
			if ( Input.Pressed( "forward" ) ) DecreaseDial();
			if ( Input.Pressed( "backward" ) ) IncreaseDial();
		}
		else if ( CombinationLockInteractor.Type == LockType.PadlockB )
		{
			if ( Input.Pressed( "left" ) ) DecreaseDial();
			if ( Input.Pressed( "right" ) ) IncreaseDial();
			if ( Input.Pressed( "forward" ) ) PreviousDial();
			if ( Input.Pressed( "backward" ) ) NextDial();
		}
		else if ( CombinationLockInteractor.Type == LockType.PadLockC )
		{
			if ( Input.Pressed( "left" ) ) PreviousDial();
			if ( Input.Pressed( "right" ) ) NextDial();
			if ( Input.Pressed( "forward" ) ) IncreaseDial();
			if ( Input.Pressed( "backward" ) ) DecreaseDial();
		}

		if ( Input.Pressed( "use" ) )
		{
			if ( CombinationLockInteractor.CurrentCombination == CombinationLockInteractor.Solution )
			{
				CombinationLockUI.Solved = true;
				CombinationLockUI.Submit();
			}
		}

		UpdateDialSelection();
	}
	public Dial Dials;
	SceneModel PadLockModel { get; set; }
	void IncreaseDial()
	{
		var currentDial = GetCurrentDial();
		currentDial.CurrentNumber = (currentDial.CurrentNumber + 1) % 10;
		currentDial.UpdateRotation();

		CombinationLockInteractor.Edited = true;
		Sound.Play( "combolock_dial_01" );
	}
	void DecreaseDial()
	{
		var currentDial = GetCurrentDial();
		currentDial.CurrentNumber = (currentDial.CurrentNumber - 1 + 10) % 10;
		currentDial.UpdateRotation();

		CombinationLockInteractor.Edited = true;
		Sound.Play( "combolock_dial_01" );
	}
	void PreviousDial()
	{
		GetCurrentDial().Selected = false;

		activeDialIndex = (activeDialIndex - 1 + dials.Count) % dials.Count;
		GetCurrentDial().Selected = true;
	}
	void NextDial()
	{
		GetCurrentDial().Selected = false;

		activeDialIndex = (activeDialIndex + 1) % dials.Count;
		GetCurrentDial().Selected = true;
	}
	void UpdateDialSelection()
	{
		foreach ( var dial in dials )
		{
			dial.SetSelectedMaterial();
		}
		if ( lockType != LockType.PadLockC )
		{
			CombinationLockInteractor.CurrentCombination = $"{dial1.CurrentNumber}{dial2.CurrentNumber}{dial3.CurrentNumber}{dial4.CurrentNumber}";
		}
		else
		{
			CombinationLockInteractor.CurrentCombination = $"{dial1.CurrentNumber}{dial2.CurrentNumber}{dial3.CurrentNumber}{dial4.CurrentNumber}{dial5.CurrentNumber}";
		}
	}
	Dial GetCurrentDial()
	{
		return dials[activeDialIndex];
	}

	Dial dial1;
	Dial dial2;
	Dial dial3;
	Dial dial4;
	Dial dial5;
	List<Dial> dials = new List<Dial> { };

	LockType lockType { get; set; }

	int activeDialIndex = 0;
	void CreatePadLock( LockType type )
	{
		lockType = type;

		if ( type == LockType.PadlockA )
		{
			var offset = new Vector3( 0, 0, 0 );
			//Create Padlock A
			PadLockModel = new SceneModel( World, "models/interact/padlocks/padlock_a.vmdl", Transform.Zero );
			PadLockModel.SetMaterialGroup( "view" );
			PadLockModel.Position = offset;
			PadLockModel.Rotation = Rotation.From( 0, 180, 90f );
			PadLockModel.SetBodyGroup( "wheels", 1 );

			var selectedDial = Material.Load( "models/interact/padlocks/padlock_a_selected.vmat" );

			dial1 = new Lock.Dial();
			dial1.Model = new SceneModel( World, "models/interact/padlocks/padlock_a_wheel.vmdl", Transform.Zero );
			dial1.Number = 1;
			dial1.Model.SetMaterialGroup( "view" );
			dial1.Model.Position = new Vector3( 0, 0, 0 );
			dial1.Selected = true;
			dial1.SelectedMaterial = selectedDial;
			dials.Add( dial1 );
			dial1.UpdateRotation();
			dial1.Type = type;

			dial2 = new Lock.Dial();
			dial2.Model = new SceneModel( World, "models/interact/padlocks/padlock_a_wheel.vmdl", Transform.Zero );
			dial2.Number = 2;
			dial2.Model.SetMaterialGroup( "view" );
			dial2.Model.Position = new Vector3( 0, 0, 0.615f );
			dial2.Selected = false;
			dial2.SelectedMaterial = selectedDial;
			dials.Add( dial2 );
			dial2.UpdateRotation();
			dial2.Type = type;

			dial3 = new Lock.Dial();
			dial3.Model = new SceneModel( World, "models/interact/padlocks/padlock_a_wheel.vmdl", Transform.Zero );
			dial3.Number = 3;
			dial3.Model.SetMaterialGroup( "view" );
			dial3.Model.Position = new Vector3( 0, 0, 1.23f );
			dial3.Selected = false;
			dial3.SelectedMaterial = selectedDial;
			dials.Add( dial3 );
			dial3.UpdateRotation();
			dial3.Type = type;

			dial4 = new Lock.Dial();
			dial4.Model = new SceneModel( World, "models/interact/padlocks/padlock_a_wheel.vmdl", Transform.Zero );
			dial4.Number = 4;
			dial4.Model.SetMaterialGroup( "view" );
			dial4.Model.Position = new Vector3( 0, 0, 1.845f );
			dial4.Selected = false;
			dial4.SelectedMaterial = selectedDial;
			dials.Add( dial4 );
			dial4.UpdateRotation();
			dial4.Type = type;

			if ( StartingCombination.Length == 4 && StartingCombination.All( char.IsDigit ) )
			{
				// Parse each character to set the CurrentNumber for each dial
				dial1.CurrentNumber = int.Parse( StartingCombination[0].ToString() );
				dial2.CurrentNumber = int.Parse( StartingCombination[1].ToString() );
				dial3.CurrentNumber = int.Parse( StartingCombination[2].ToString() );
				dial4.CurrentNumber = int.Parse( StartingCombination[3].ToString() );

				// Make sure to call UpdateRotation() for each dial here if necessary
				dial1.UpdateRotation();
				dial2.UpdateRotation();
				dial3.UpdateRotation();
				dial4.UpdateRotation();
			}
		}
		else if ( type == LockType.PadlockB )
		{
			//Create Padlock B
			PadLockModel = new SceneModel( World, "models/interact/padlocks/padlock_b.vmdl", Transform.Zero );
			PadLockModel.SetMaterialGroup( "view" );
			PadLockModel.Rotation = Rotation.From( 0, 180, 90f );
			PadLockModel.SetBodyGroup( "wheels", 1 );

			var selectedDial = Material.Load( "models/interact/padlocks/padlock_b_selected.vmat" );

			dial1 = new Lock.Dial();
			dial1.Model = new SceneModel( World, "models/interact/padlocks/padlock_b_wheel.vmdl", Transform.Zero );
			dial1.Number = 1;
			dial1.Model.SetMaterialGroup( "view" );
			dial1.Model.Position = new Vector3( 0, 0, 0 );
			dial1.Selected = true;
			dial1.SelectedMaterial = selectedDial;
			dials.Add( dial1 );
			dial1.Type = type;
			dial1.UpdateRotation();

			dial2 = new Lock.Dial();
			dial2.Model = new SceneModel( World, "models/interact/padlocks/padlock_b_wheel.vmdl", Transform.Zero );
			dial2.Number = 2;
			dial2.Model.SetMaterialGroup( "view" );
			dial2.Model.Position = new Vector3( 0, -0.666f, 0 );
			dial2.Selected = false;
			dial2.SelectedMaterial = selectedDial;
			dials.Add( dial2 );
			dial2.Type = type;
			dial2.UpdateRotation();

			dial3 = new Lock.Dial();
			dial3.Model = new SceneModel( World, "models/interact/padlocks/padlock_b_wheel.vmdl", Transform.Zero );
			dial3.Number = 3;
			dial3.Model.SetMaterialGroup( "view" );
			dial3.Model.Position = new Vector3( 0, -1.332f, 0 );
			dial3.Selected = false;
			dial3.SelectedMaterial = selectedDial;
			dials.Add( dial3 );
			dial3.Type = type;
			dial3.UpdateRotation();

			dial4 = new Lock.Dial();
			dial4.Model = new SceneModel( World, "models/interact/padlocks/padlock_b_wheel.vmdl", Transform.Zero );
			dial4.Number = 4;
			dial4.Model.SetMaterialGroup( "view" );
			dial4.Model.Position = new Vector3( 0, -2f, 0 );
			dial4.Selected = false;
			dial4.SelectedMaterial = selectedDial;
			dials.Add( dial4 );
			dial4.Type = type;
			dial4.UpdateRotation();

			if ( StartingCombination.Length == 4 && StartingCombination.All( char.IsDigit ) )
			{
				// Parse each character to set the CurrentNumber for each dial
				dial1.CurrentNumber = int.Parse( StartingCombination[0].ToString() );
				dial2.CurrentNumber = int.Parse( StartingCombination[1].ToString() );
				dial3.CurrentNumber = int.Parse( StartingCombination[2].ToString() );
				dial4.CurrentNumber = int.Parse( StartingCombination[3].ToString() );

				// Make sure to call UpdateRotation() for each dial here if necessary
				dial1.UpdateRotation();
				dial2.UpdateRotation();
				dial3.UpdateRotation();
				dial4.UpdateRotation();
			}
		}
		else if ( type == LockType.PadLockC )
		{
			PadLockModel = new SceneModel( World, "models/interact/padlocks/padlock_c.vmdl", Transform.Zero );
			PadLockModel.SetMaterialGroup( "view" );
			PadLockModel.Rotation = Rotation.From( 0, 180, 90f );
			PadLockModel.SetBodyGroup( "wheels", 1 );

			var selectedDial = Material.Load( "models/interact/padlocks/padlock_c_selected.vmat" );

			dial1 = new Lock.Dial();
			dial1.Model = new SceneModel( World, "models/interact/padlocks/padlock_c_wheel.vmdl", Transform.Zero );
			dial1.Number = 1;
			dial1.Model.SetMaterialGroup( "view" );
			dial1.Model.Position = new Vector3( 0, 0, -0.69f );
			dial1.Selected = true;
			dial1.SelectedMaterial = selectedDial;
			dials.Add( dial1 );
			dial1.Type = type;
			dial1.UpdateRotation();

			dial2 = new Lock.Dial();
			dial2.Model = new SceneModel( World, "models/interact/padlocks/padlock_c_wheel.vmdl", Transform.Zero );
			dial2.Number = 2;
			dial2.Model.SetMaterialGroup( "view" );
			dial2.Model.Position = new Vector3( 0, 0, -0.265f );
			dial2.Selected = false;
			dial2.SelectedMaterial = selectedDial;
			dials.Add( dial2 );
			dial2.Type = type;
			dial2.UpdateRotation();

			dial3 = new Lock.Dial();
			dial3.Model = new SceneModel( World, "models/interact/padlocks/padlock_c_wheel.vmdl", Transform.Zero );
			dial3.Number = 3;
			dial3.Model.SetMaterialGroup( "view" );
			dial3.Model.Position = new Vector3( 0, 0, 0.1575f );
			dial3.Selected = false;
			dial3.SelectedMaterial = selectedDial;
			dial3.Type = type;
			dials.Add( dial3 );
			dial3.UpdateRotation();

			dial4 = new Lock.Dial();
			dial4.Model = new SceneModel( World, "models/interact/padlocks/padlock_c_wheel.vmdl", Transform.Zero );
			dial4.Number = 4;
			dial4.Model.SetMaterialGroup( "view" );
			dial4.Model.Position = new Vector3( 0, 0, 0.58f );
			dial4.Selected = false;
			dial4.SelectedMaterial = selectedDial;
			dial4.Type = type;
			dials.Add( dial4 );
			dial4.UpdateRotation();

			dial5 = new Lock.Dial();
			dial5.Model = new SceneModel( World, "models/interact/padlocks/padlock_c_wheel.vmdl", Transform.Zero );
			dial5.Number = 5;
			dial5.Model.SetMaterialGroup( "view" );
			dial5.Model.Position = new Vector3( 0, 0, 1.0f );
			dial5.Selected = false;
			dial5.SelectedMaterial = selectedDial;
			dial5.Type = type;
			dials.Add( dial5 );
			dial5.UpdateRotation();

			if ( StartingCombination.Length == 5 && StartingCombination.All( char.IsDigit ) )
			{
				// Parse each character to set the CurrentNumber for each dial
				dial1.CurrentNumber = int.Parse( StartingCombination[0].ToString() );
				dial2.CurrentNumber = int.Parse( StartingCombination[1].ToString() );
				dial3.CurrentNumber = int.Parse( StartingCombination[2].ToString() );
				dial4.CurrentNumber = int.Parse( StartingCombination[3].ToString() );
				dial5.CurrentNumber = int.Parse( StartingCombination[4].ToString() );

				// Make sure to call UpdateRotation() for each dial here if necessary
				dial1.UpdateRotation();
				dial2.UpdateRotation();
				dial3.UpdateRotation();
				dial4.UpdateRotation();
				dial5.UpdateRotation();
			}
		}
	}

	public class Lock
	{
		public class Dial
		{
			public int Number { get; set; }
			public int CurrentNumber { get; set; }
			public SceneModel Model { get; set; }
			public bool Selected { get; set; } = false;
			public Material SelectedMaterial { get; set; }
			public Material DefaultMaterial { get; set; }
			public LockType Type { get; set; }
			public void SetSelectedMaterial()
			{
				if ( Selected )
				{
					Model.SetMaterialGroup( "selected" );
				}
				else
					Model.SetMaterialGroup( "view" );
			}

			public void UpdateRotation()
			{
				float rotation = -36 * CurrentNumber;
				if ( Type == LockType.PadlockA )
				{
					Model.Rotation = Rotation.From( 180, rotation, 270 );
				}
				else if ( Type == LockType.PadlockB )
				{
					Model.Rotation = Rotation.From( -rotation, 180, 90 );
				}
				else if ( Type == LockType.PadLockC )
				{
					Model.Rotation = Rotation.From( 180, -rotation, 270 );
				}
			}
		}
	}
}