Editor/ChitChat/ActionWidgets/SpeakActionInspectorWidget.cs
using Sandbox;
using Editor;

namespace ChitChat.Editor;

public class SpeakActionInspectorWidget : Widget
{
	private SerializedObject _serializedObject;

	private readonly Layout _content;

	public SpeakActionInspectorWidget(SerializedObject speakActionSO, Widget parent) : base(parent)
	{
		Name = "SpeakAction";

		SetSerializedObject(speakActionSO);

		Layout = Layout.Column();

		_content = Layout;

		Rebuild();
	}

	public void SetSerializedObject(SerializedObject speakActionSO)
	{
		if (!speakActionSO.TypeName.Equals(nameof(SpeakAction)))
		{
			Log.Error("SerializedObject is not a " + nameof(SpeakAction) + "!");
			return;
		}

		_serializedObject = speakActionSO;
	}

	public void Rebuild()
	{
		_content.Clear(true);

		_content.Add(new TextSeperatorWidget("Character", 65, this));

		SpeakActionCharacterSettingsWidget characterSettings = new SpeakActionCharacterSettingsWidget(_serializedObject, this);
		_content.Add(characterSettings);

		_content.Add(new TextSeperatorWidget("Dialogue Text", 85, this));

		SpeakActionTextControlWidget textWidget = new SpeakActionTextControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.Text)));
		textWidget.FixedHeight = 200;
		_content.Add(textWidget);

		_content.Add(new TextSeperatorWidget("Settings", 55, this));

		//Input settings
		SpeakActionInputSettingsWidget inputSettings = new SpeakActionInputSettingsWidget(_serializedObject, this);
		_content.Add(inputSettings);

		_content.AddSeparator(2, Color.Transparent);

		//Write settings
		SpeakActionWriteSettingsWidget writeSettings = new SpeakActionWriteSettingsWidget(_serializedObject, this);
		_content.Add(writeSettings);

		_content.Add(new TextSeperatorWidget("Audio", 45, this));

		//Sound Settings
		SpeakActionAudioSettingsWidget audioSettings = new SpeakActionAudioSettingsWidget(_serializedObject, this);
		_content.Add(audioSettings);
	}

	private sealed class SpeakActionCharacterSettingsWidget : Widget
	{
		private CharacterPictureDropdown _pictureDropdown;
		private Label _characterNameLabel;
		private TextureWidget _characterPicturePreview;

		private DialogueCharacter _character;
		private SerializedObject _serializedObject;

		private Widget _characterPictureSettings;

		/// <summary>
		/// Property is assumed to be DialogueData.
		/// </summary>
		public SpeakActionCharacterSettingsWidget(SerializedObject so, Widget parent) : base(parent)
		{
			_serializedObject = so;
			SerializedProperty character = _serializedObject.GetProperty(nameof(SpeakAction.Character));
			_character = character.GetValue<DialogueCharacter>();

			Layout = Layout.Row();
			Layout.Alignment = TextFlag.LeftTop;
			VerticalSizeMode = SizeMode.CanShrink;

			//Character Preview
			Widget characterPreviewContainer = new Widget(this);
			characterPreviewContainer.MaximumWidth = 300;
			characterPreviewContainer.Layout = Layout.Column();
			characterPreviewContainer.Layout.Alignment = TextFlag.LeftTop;
			Layout.Add(characterPreviewContainer);

			int first = _serializedObject.GetProperty(nameof(SpeakAction.CharacterIndex)).GetValue<int>();

			_characterPicturePreview = new TextureWidget()
			{
				Parent = this,
				FixedSize = new Vector2(300, 300),
				RetainAspectRatio = true,
				Visible = _character.IsValid()
			};
			if (_character.IsValid() && _character.CharacterPictures?.Count > 0 && first < _character.CharacterPictures.Count)
			{
				if (first < 0)
					first = 0;
				_characterPicturePreview.Texture = _character.CharacterPictures[first].Picture;
			}
			else
				_characterPicturePreview.Texture = null;

			characterPreviewContainer.Layout.Add(_characterPicturePreview);

			ControlWidget characterControlWidget = ControlWidget.Create(_serializedObject.GetProperty(nameof(SpeakAction.Character)));
			characterControlWidget.MaximumWidth = 300;
			characterControlWidget.MaximumHeight = 30;
			characterPreviewContainer.Layout.AddSeparator(2, Color.Transparent);
			characterPreviewContainer.Layout.Add(characterControlWidget);

			//Character Info
			_characterPictureSettings = new Widget(this);
			_characterPictureSettings.Layout = Layout.Column();
			_characterPictureSettings.Layout.Margin = new Sandbox.UI.Margin(5, 0, 0, 0);
			_characterPictureSettings.Layout.Alignment = TextFlag.LeftTop;
			_characterPictureSettings.Visible = (_character.IsValid());
			Layout.Add(_characterPictureSettings);

			_characterNameLabel = new Label(_character.IsValid() ? _character.Name : string.Empty, this);
			_characterNameLabel.SetStyles("font-size: 23px;");
			_characterNameLabel.MinimumHeight = 30;
			_characterNameLabel.ToolTip = "";
			_characterPictureSettings.Layout.Add(_characterNameLabel);

			_characterPictureSettings.Layout.AddSeparator(2, Color.Transparent);

			Label picDropdownLabel = new Label("Picture")
			{
				ContentMargins = new Sandbox.UI.Margin(2, 0, 0, 0),
			};
			_pictureDropdown = new CharacterPictureDropdown(character, first, this);
			_pictureDropdown.onItemSelected += OnItemSelected;
			_pictureDropdown.MinimumHeight = 30;
			_pictureDropdown.MinimumWidth = 300;
			_characterPictureSettings.Layout.Add(picDropdownLabel);
			_characterPictureSettings.Layout.Add(_pictureDropdown);

			_characterPictureSettings.Layout.AddSeparator(2, Color.Transparent);

			Label picPosLabel = new Label("Picture Position")
			{
				ContentMargins = new Sandbox.UI.Margin(2, 0, 0, 0),
			};
			EnumControlWidget picturePosWidget = new EnumControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.PicturePosition)));
			picturePosWidget.MaximumWidth = 300;
			_characterPictureSettings.Layout.Add(picPosLabel);
			_characterPictureSettings.Layout.Add(picturePosWidget);

			_serializedObject.OnPropertyChanged += OnPropertyChanged;
		}

		public override void OnDestroyed()
		{
			_serializedObject.OnPropertyChanged -= OnPropertyChanged;
			_pictureDropdown.onItemSelected -= OnItemSelected;
		}

		public void UpdateUI()
		{
			int first = _serializedObject.GetProperty(nameof(SpeakAction.CharacterIndex)).GetValue<int>();
			SerializedProperty characterProp = _serializedObject.GetProperty(nameof(SpeakAction.Character));
			_character = characterProp.GetValue<DialogueCharacter>();

			if (_character.IsValid())
			{
				if (first > _character.CharacterPictures?.Count - 1 || first < 0)
				{
					_serializedObject.GetProperty(nameof(SpeakAction.CharacterIndex)).SetValue<int>(0);
					first = 0;
				}
			}

			//Update name label
			if (_character.IsValid())
				_characterNameLabel.Text = _character?.Name;
			else
				_characterNameLabel.Text = string.Empty;

			//Update picture dropdown
			if (_character.IsValid())
				_pictureDropdown.UpdateProperty(characterProp, first);
			_characterPictureSettings.Visible = (_character != null);

			//Update preview texture
			if (_character.IsValid() && _character.CharacterPictures?.Count > 0 && first < _character.CharacterPictures.Count)
				_characterPicturePreview.Texture = _character.CharacterPictures[first].Picture;
			else
				_characterPicturePreview.Texture = null;

			_characterPicturePreview.Visible = _character.IsValid();
		}

		private void OnItemSelected(int index)
		{
			_serializedObject.GetProperty(nameof(SpeakAction.CharacterIndex)).SetValue<int>(index);

			UpdateUI();
		}

		private void OnPropertyChanged(SerializedProperty property)
		{
			if (property.IsValid() && property.PropertyType.Equals(typeof(DialogueCharacter)))
			{
				UpdateUI();
			}
		}
	}

	private sealed class SpeakActionInputSettingsWidget : Widget
	{
		private Layout _content;
		private Widget _waitForInputContainer;

		private SerializedObject _serializedObject;

		public SpeakActionInputSettingsWidget(SerializedObject so, Widget parent) : base(parent)
		{
			Name = "SpeakActionInputSettings";
			_serializedObject = so;
			_serializedObject.OnPropertyChanged += OnPropertyChanged;

			Layout = Layout.Column();
			_content = Layout;

			Rebuild();
		}

		public void Rebuild()
		{
			Layout inputTypeLayout = Layout.Row();
			inputTypeLayout.Alignment = TextFlag.LeftCenter;
			Label inputTypeLabel = new Label("Input State")
			{
				ContentMargins = new Sandbox.UI.Margin(5, 0, 5, 0),
				ToolTip = "How the dialogue system should handle input.",
				FixedWidth = 125
			};
			EnumControlWidget inputTypeWidget = new EnumControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.DialogueInputState)));
			inputTypeWidget.MaximumWidth = 375;
			inputTypeLayout.Add(inputTypeLabel);
			inputTypeLayout.Add(inputTypeWidget);
			inputTypeLayout.Margin = new Sandbox.UI.Margin(0, 1, 0, 1);
			_content.Add(inputTypeLayout);

			_waitForInputContainer = new Widget(this);
			_waitForInputContainer.Layout = Layout.Row();
			_waitForInputContainer.Layout.Alignment = TextFlag.LeftCenter;
			Label waitInputDelayLabel = new Label("Wait For Input Delay")
			{
				ContentMargins = new Sandbox.UI.Margin(5, 0, 5, 0),
				ToolTip = "How long it should wait before playing.",
				FixedWidth = 125
			};
			FloatControlWidget waitInputDelayWidget = new FloatControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.WaitForInputDelay)));
			waitInputDelayWidget.MaximumWidth = 375;
			_waitForInputContainer.Layout.Add(waitInputDelayLabel);
			_waitForInputContainer.Layout.Add(waitInputDelayWidget);
			_waitForInputContainer.Visible = _serializedObject.GetProperty(nameof(SpeakAction.DialogueInputState)).GetValue<DialogueInputState>() == DialogueInputState.WaitForSeconds;
			_waitForInputContainer.ContentMargins = new Sandbox.UI.Margin(0, 1, 0, 1);
			_content.Add(_waitForInputContainer);
		}

		private void OnPropertyChanged(SerializedProperty prop)
		{
			if (prop.PropertyType.Equals(typeof(DialogueInputState)))
			{
				if (prop.GetValue<DialogueInputState>() == DialogueInputState.WaitForSeconds)
					_waitForInputContainer.Visible = true;
				else
					_waitForInputContainer.Visible = false;
			}
		}

		public override void OnDestroyed()
		{
			_serializedObject.OnPropertyChanged -= OnPropertyChanged;
		}
	}

	private sealed class SpeakActionWriteSettingsWidget : Widget
	{
		private Layout _content;
		private Widget _writeSpeedContainer;

		private SerializedObject _serializedObject;

		public SpeakActionWriteSettingsWidget(SerializedObject so, Widget parent) : base(parent)
		{
			Name = "SpeakActionWriteSettings";
			_serializedObject = so;
			_serializedObject.OnPropertyChanged += OnPropertyChanged;

			Layout = Layout.Column();
			_content = Layout;

			Rebuild();
		}

		public void Rebuild()
		{
			Layout writingEffectLayout = Layout.Row();
			writingEffectLayout.Alignment = TextFlag.LeftCenter;
			Label writingEffectLabel = new Label("Use Writing effect")
			{
				ContentMargins = new Sandbox.UI.Margin(5, 0, 5, 0),
				ToolTip = "If it should use the writing effect.",
				FixedWidth = 125
			};
			BoolControlWidget writingEffectWidget = new BoolControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.UseWritingEffect)));
			writingEffectLayout.Add(writingEffectLabel);
			writingEffectLayout.Add(writingEffectWidget);
			writingEffectLayout.Margin = new Sandbox.UI.Margin(0, 1, 0, 1);
			_content.Add(writingEffectLayout);

			_writeSpeedContainer = new Widget(this);
			_writeSpeedContainer.Layout = Layout.Row();
			_writeSpeedContainer.Layout.Alignment = TextFlag.LeftCenter;
			Label writeSpeedLabel = new Label("Write speed")
			{
				ContentMargins = new Sandbox.UI.Margin(5, 0, 5, 0),
				ToolTip = "How fast the text effect types.",
				FixedWidth = 125
			};
			FloatControlWidget writeSpeedWidget = new FloatControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.WriteSpeed)));
			writeSpeedWidget.MaximumWidth = 375;
			_writeSpeedContainer.Layout.Add(writeSpeedLabel);
			_writeSpeedContainer.Layout.Add(writeSpeedWidget);
			_writeSpeedContainer.Visible = _serializedObject.GetProperty(nameof(SpeakAction.UseWritingEffect)).GetValue<bool>();
			_writeSpeedContainer.ContentMargins = new Sandbox.UI.Margin(0, 1, 0, 1);
			_content.Add(_writeSpeedContainer);
		}

		private void OnPropertyChanged(SerializedProperty prop)
		{
			if (prop.Name.Equals(nameof(SpeakAction.UseWritingEffect)))
			{
				_writeSpeedContainer.Visible = prop.GetValue<bool>();
			}
		}

		public override void OnDestroyed()
		{
			if (_serializedObject != null)
				_serializedObject.OnPropertyChanged -= OnPropertyChanged;
		}
	}

	private sealed class SpeakActionAudioSettingsWidget : Widget
	{
		private readonly Layout _content;

		private Widget _audioSettings;
		private Widget _audioDelayContainer;

		private SerializedObject _serializedObject;

		public SpeakActionAudioSettingsWidget(SerializedObject so, Widget parent) : base(parent)
		{
			Name = "SpeakActionAudioSettings";
			
			_serializedObject = so;
			_serializedObject.OnPropertyChanged += OnPropertyChanged;

			Layout = Layout.Column();
			_content = Layout;

			Rebuild();
		}

		public void Rebuild()
		{
			//Audio Type
			Layout audioTypeLayout = Layout.Row();
			audioTypeLayout.Alignment = TextFlag.LeftCenter;
			Label audioTypeLabel = new Label("Audio Type")
			{
				ContentMargins = new Sandbox.UI.Margin(5, 0, 5, 0),
				ToolTip = "How the audio should be played.",
				FixedWidth = 100
			};
			EnumControlWidget audioTypeWidget = new EnumControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.AudioType)));
			audioTypeWidget.MaximumWidth = 400;
			audioTypeLayout.Add(audioTypeLabel);
			audioTypeLayout.Add(audioTypeWidget);
			audioTypeLayout.Margin = new Sandbox.UI.Margin(0, 1, 0, 1);
			_content.Add(audioTypeLayout);

			_audioSettings = new Widget(this);
			_audioSettings.Layout = Layout.Column();

			//Sound Event
			Layout soundEventLayout = Layout.Row();
			soundEventLayout.Alignment = TextFlag.LeftCenter;
			Label soundEventLabel = new Label("Sound Event")
			{
				ContentMargins = new Sandbox.UI.Margin(5, 0, 5, 0),
				ToolTip = "The audio that should be played.",
				FixedWidth = 100
			};
			ControlWidget soundEventWidget = ControlWidget.Create(_serializedObject.GetProperty(nameof(SpeakAction.SoundEvent)));
			soundEventWidget.MaximumWidth = 400;
			soundEventLayout.Add(soundEventLabel);
			soundEventLayout.Add(soundEventWidget);
			soundEventLayout.Margin = new Sandbox.UI.Margin(0, 1, 0, 1);
			_audioSettings.Layout.Add(soundEventLayout);

			//Use audio delay
			Layout useDelayLayout = Layout.Row();
			useDelayLayout.Alignment = TextFlag.LeftCenter;
			Label delayAudioLabel = new Label("Use Audio Delay")
			{
				ContentMargins = new Sandbox.UI.Margin(5, 0, 5, 0),
				ToolTip = "If the audi should be delayed.",
				FixedWidth = 100
			};
			BoolControlWidget delayAudioWidget = new BoolControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.UseAudioDelay)));
			useDelayLayout.Add(delayAudioLabel);
			useDelayLayout.Add(delayAudioWidget);
			useDelayLayout.Margin = new Sandbox.UI.Margin(0, 1, 0, 1);
			_audioSettings.Layout.Add(useDelayLayout);

			//Delay
			_audioDelayContainer = new Widget(this);
			_audioDelayContainer.Layout = Layout.Row();
			_audioDelayContainer.Layout.Alignment = TextFlag.LeftCenter;
			Label delayLabel = new Label("Audio Delay")
			{
				ContentMargins = new Sandbox.UI.Margin(5, 0, 5, 0),
				ToolTip = "How long the audio should delay in seconds.",
				FixedWidth = 100
			};
			RangedFloatControlWidget delayWidget = new RangedFloatControlWidget(_serializedObject.GetProperty(nameof(SpeakAction.AudioDelay)));
			delayWidget.MaximumWidth = 400;
			_audioDelayContainer.Layout.Add(delayLabel);
			_audioDelayContainer.Layout.Add(delayWidget);
			_audioDelayContainer.Visible = _serializedObject.GetProperty(nameof(SpeakAction.UseAudioDelay)).GetValue<bool>();
			_audioDelayContainer.ContentMargins = new Sandbox.UI.Margin(0, 1, 0, 1);
			_audioSettings.Layout.Add(_audioDelayContainer);

			_audioSettings.Visible = _serializedObject.GetProperty(nameof(SpeakAction.AudioType)).GetValue<DialogueAudioType>() != DialogueAudioType.None;
			_content.Add(_audioSettings);
		}

		private void OnPropertyChanged(SerializedProperty prop)
		{
			if (prop.Name.Equals(nameof(SpeakAction.UseAudioDelay)))
			{
				_audioDelayContainer.Visible = prop.GetValue<bool>();
			}
			else if (prop.Name.Equals(nameof(SpeakAction.AudioType)))
			{
				_audioSettings.Visible = prop.GetValue<DialogueAudioType>() != DialogueAudioType.None;
			}
		}

		public override void OnDestroyed()
		{
			if (_serializedObject != null)
				_serializedObject.OnPropertyChanged -= OnPropertyChanged;
		}
	}

	private sealed class SpeakActionTextControlWidget : TextAreaControlWidget
	{
		public SpeakActionTextControlWidget(SerializedProperty property) : base(property)
		{
			TextEdit.TranslucentBackground = true;
			TextEdit.FixedHeight = 200;
			TextEdit.HorizontalSizeMode = SizeMode.CanGrow;
		}

		public override bool SupportsMultiEdit => false;
	}
}