// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.

using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Input;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;

namespace FlaxEditor.Surface.Elements
{
    /// <summary>
    /// The handler for the input box default value editing. Used to display the default to the UI.
    /// </summary>
    [HideInEditor]
    public interface IDefaultValueEditor
    {
        /// <summary>
        /// Checks if the handles supports the given type.
        /// </summary>
        /// <param name="box">The input box that uses this editor.</param>
        /// <param name="type">The type to check.</param>
        /// <returns>True if can create UI for this type editing, otherwise false.</returns>
        bool CanUse(InputBox box, ref ScriptType type);

        /// <summary>
        /// Creates the UI for the value editing.
        /// </summary>
        /// <param name="box">The input box that uses this editor.</param>
        /// <param name="bounds">The control bounds (control can be smaller but cannot be bigger).</param>
        /// <returns>The root control of the created UI</returns>
        Control Create(InputBox box, ref Rectangle bounds);

        /// <summary>
        /// Returns true if given control is valid root editor for the value.
        /// </summary>
        /// <param name="box">The input box that uses this editor.</param>
        /// <param name="control">The root control of the editor UI.</param>
        /// <returns><c>true</c> if the specified control is valid; otherwise, <c>false</c>.</returns>
        bool IsValid(InputBox box, Control control);

        /// <summary>
        /// Updates the default value editor UI value.
        /// </summary>
        /// <param name="box">The input box that uses this editor.</param>
        /// <param name="control">The root control of the editor UI.</param>
        void UpdateDefaultValue(InputBox box, Control control);

        /// <summary>
        /// Updates the UI after box attributes change.
        /// </summary>
        /// <param name="box">The input box that uses this editor.</param>
        /// <param name="attributes">The custom attributes collection.</param>
        /// <param name="control">The root control of the editor UI.</param>
        void UpdateAttributes(InputBox box, object[] attributes, Control control);
    }

    class BooleanDefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(bool);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = BoolValue.Get(box.ParentNode, box.Archetype);
            var control = new CheckBox(bounds.X, bounds.Y, value, bounds.Height)
            {
                Parent = box.Parent,
                Tag = box,
            };
            control.StateChanged += OnCheckboxStateChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is CheckBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is CheckBox checkBox)
            {
                checkBox.Checked = BoolValue.Get(box.ParentNode, box.Archetype);
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private void OnCheckboxStateChanged(CheckBox control)
        {
            var box = (InputBox)control.Tag;
            box.ParentNode.SetValue(box.Archetype.ValueIndex, control.Checked);
        }
    }

    class IntegerDefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(int) || type.Type == typeof(uint);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = IntegerValue.Get(box.ParentNode, box.Archetype);
            var control = new IntValueBox(value, bounds.X, bounds.Y, 40, box.CurrentType.Type == typeof(uint) ? 0 : int.MinValue, int.MaxValue, 0.01f)
            {
                Height = bounds.Height,
                Parent = box.Parent,
                Tag = box,
            };
            control.BoxValueChanged += OnIntValueBoxChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is IntValueBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is IntValueBox intValue)
            {
                intValue.Value = IntegerValue.Get(box.ParentNode, box.Archetype);
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private void OnIntValueBoxChanged(ValueBox<int> control)
        {
            var box = (InputBox)control.Tag;
            IntegerValue.Set(box.ParentNode, box.Archetype, control.Value);
        }
    }

    class FloatingDefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(float) || type.Type == typeof(double);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = FloatValue.Get(box.ParentNode, box.Archetype);
            var control = new FloatValueBox(value, bounds.X, bounds.Y, 40, float.MinValue, float.MaxValue, 0.01f)
            {
                Height = bounds.Height,
                Parent = box.Parent,
                Tag = box,
            };
            control.BoxValueChanged += OnFloatValueBoxChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is FloatValueBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is FloatValueBox floatValue)
            {
                floatValue.Value = FloatValue.Get(box.ParentNode, box.Archetype);
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private void OnFloatValueBoxChanged(ValueBox<float> control)
        {
            var box = (InputBox)control.Tag;
            FloatValue.Set(box.ParentNode, box.Archetype, control.Value);
        }
    }

    class StringDefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(string) && (box.Attributes == null || box.Attributes.All(x => x.GetType() != typeof(TypeReferenceAttribute)));
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = box.ParentNode.Values[box.Archetype.ValueIndex] as string;
            var control = new TextBox(false, bounds.X, bounds.Y, 40)
            {
                Text = value,
                Height = bounds.Height,
                Parent = box.Parent,
                Tag = box,
            };
            control.TextBoxEditEnd += OnTextBoxTextChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is TextBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is TextBox textBox)
            {
                textBox.Text = box.ParentNode.Values[box.Archetype.ValueIndex] as string;
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private void OnTextBoxTextChanged(TextBoxBase control)
        {
            var box = (InputBox)control.Tag;
            box.ParentNode.SetValue(box.Archetype.ValueIndex, control.Text);
        }
    }

    class Vector2DefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(Vector2);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = GetValue(box);
            var control = new ContainerControl(bounds.X, bounds.Y, 22 * 2 - 2, bounds.Height)
            {
                ClipChildren = false,
                AutoFocus = false,
                Parent = box.Parent,
                Tag = box,
            };
            var floatX = new FloatValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatX.BoxValueChanged += OnVector2ValueChanged;
            var floatY = new FloatValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatY.BoxValueChanged += OnVector2ValueChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is ContainerControl vec2
                   && vec2.ChildrenCount == 2
                   && vec2.Children[0] is FloatValueBox
                   && vec2.Children[1] is FloatValueBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is ContainerControl vec2
                && vec2.Tag as Type == typeof(Vector2)
                && vec2.ChildrenCount == 2
                && vec2.Children[0] is FloatValueBox x
                && vec2.Children[1] is FloatValueBox y)
            {
                var value = GetValue(box);
                x.Value = value.X;
                y.Value = value.Y;
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private Vector2 GetValue(InputBox box)
        {
            var value = Vector2.Zero;
            var v = box.ParentNode.Values[box.Archetype.ValueIndex];
            if (v is Vector2 vec2)
                value = vec2;
            else if (v is Vector3 vec3)
                value = new Vector2(vec3);
            else if (v is Vector4 vec4)
                value = new Vector2(vec4);
            else if (v is Color col)
                value = new Vector2(col.R, col.G);
            else if (v is float f)
                value = new Vector2(f);
            else if (v is int i)
                value = new Vector2(i);
            return value;
        }

        private void OnVector2ValueChanged(ValueBox<float> valueBox)
        {
            var control = valueBox.Parent;
            var box = (InputBox)control.Tag;
            var x = ((FloatValueBox)control.Children[0]).Value;
            var y = ((FloatValueBox)control.Children[1]).Value;
            box.ParentNode.SetValue(box.Archetype.ValueIndex, new Vector2(x, y));
        }
    }

    class Vector3DefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(Vector3);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = GetValue(box);
            var control = new ContainerControl(bounds.X, bounds.Y, 22 * 3 - 2, bounds.Height)
            {
                ClipChildren = false,
                AutoFocus = false,
                Parent = box.Parent,
                Tag = box,
            };
            var floatX = new FloatValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatX.BoxValueChanged += OnVector3ValueChanged;
            var floatY = new FloatValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatY.BoxValueChanged += OnVector3ValueChanged;
            var floatZ = new FloatValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatZ.BoxValueChanged += OnVector3ValueChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is ContainerControl vec3
                   && vec3.ChildrenCount == 3
                   && vec3.Children[0] is FloatValueBox
                   && vec3.Children[1] is FloatValueBox
                   && vec3.Children[2] is FloatValueBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is ContainerControl vec3
                && vec3.ChildrenCount == 3
                && vec3.Children[0] is FloatValueBox x
                && vec3.Children[1] is FloatValueBox y
                && vec3.Children[2] is FloatValueBox z)
            {
                var value = GetValue(box);
                x.Value = value.X;
                y.Value = value.Y;
                z.Value = value.Z;
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private Vector3 GetValue(InputBox box)
        {
            var value = Vector3.Zero;
            var v = box.ParentNode.Values[box.Archetype.ValueIndex];
            if (v is Vector2 vec2)
                value = new Vector3(vec2, 0.0f);
            else if (v is Vector3 vec3)
                value = vec3;
            else if (v is Vector4 vec4)
                value = new Vector3(vec4);
            else if (v is Color col)
                value = col;
            else if (v is float f)
                value = new Vector3(f);
            else if (v is int i)
                value = new Vector3(i);
            return value;
        }

        private void OnVector3ValueChanged(ValueBox<float> valueBox)
        {
            var control = valueBox.Parent;
            var box = (InputBox)control.Tag;
            var x = ((FloatValueBox)control.Children[0]).Value;
            var y = ((FloatValueBox)control.Children[1]).Value;
            var z = ((FloatValueBox)control.Children[2]).Value;
            box.ParentNode.SetValue(box.Archetype.ValueIndex, new Vector3(x, y, z));
        }
    }

    class Vector4DefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(Vector4);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = GetValue(box);
            var control = new ContainerControl(bounds.X, bounds.Y, 22 * 4 - 2, bounds.Height)
            {
                ClipChildren = false,
                AutoFocus = false,
                Parent = box.Parent,
                Tag = box,
            };
            var floatX = new FloatValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatX.BoxValueChanged += OnVector4ValueChanged;
            var floatY = new FloatValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatY.BoxValueChanged += OnVector4ValueChanged;
            var floatZ = new FloatValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatZ.BoxValueChanged += OnVector4ValueChanged;
            var floatW = new FloatValueBox(value.W, 66, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatW.BoxValueChanged += OnVector4ValueChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is ContainerControl vec4
                   && vec4.ChildrenCount == 4
                   && vec4.Children[0] is FloatValueBox
                   && vec4.Children[1] is FloatValueBox
                   && vec4.Children[2] is FloatValueBox
                   && vec4.Children[3] is FloatValueBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is ContainerControl vec4
                && vec4.ChildrenCount == 4
                && vec4.Children[0] is FloatValueBox x
                && vec4.Children[1] is FloatValueBox y
                && vec4.Children[2] is FloatValueBox z
                && vec4.Children[3] is FloatValueBox w)
            {
                var value = GetValue(box);
                x.Value = value.X;
                y.Value = value.Y;
                z.Value = value.Z;
                w.Value = value.W;
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private Vector4 GetValue(InputBox box)
        {
            var value = Vector4.Zero;
            var v = box.ParentNode.Values[box.Archetype.ValueIndex];
            if (v is Vector2 vec2)
                value = new Vector4(vec2, 0.0f, 0.0f);
            else if (v is Vector3 vec3)
                value = new Vector4(vec3, 0.0f);
            else if (v is Vector4 vec4)
                value = vec4;
            else if (v is Color col)
                value = col;
            else if (v is float f)
                value = new Vector4(f);
            else if (v is int i)
                value = new Vector4(i);
            return value;
        }

        private void OnVector4ValueChanged(ValueBox<float> valueBox)
        {
            var control = valueBox.Parent;
            var box = (InputBox)control.Tag;
            var x = ((FloatValueBox)control.Children[0]).Value;
            var y = ((FloatValueBox)control.Children[1]).Value;
            var z = ((FloatValueBox)control.Children[2]).Value;
            var w = ((FloatValueBox)control.Children[3]).Value;
            box.ParentNode.SetValue(box.Archetype.ValueIndex, new Vector4(x, y, z, w));
        }
    }

    class QuaternionDefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(Quaternion);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = GetValue(box).EulerAngles;
            var control = new ContainerControl(bounds.X, bounds.Y, 22 * 3 - 2, bounds.Height)
            {
                ClipChildren = false,
                AutoFocus = false,
                Parent = box.Parent,
                Tag = box,
            };
            var floatX = new FloatValueBox(value.X, 0, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatX.BoxValueChanged += OnQuaternionValueChanged;
            var floatY = new FloatValueBox(value.Y, 22, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatY.BoxValueChanged += OnQuaternionValueChanged;
            var floatZ = new FloatValueBox(value.Z, 44, 0, 20, float.MinValue, float.MaxValue, 0.0f)
            {
                Height = bounds.Height,
                Parent = control,
            };
            floatZ.BoxValueChanged += OnQuaternionValueChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is ContainerControl vec3
                   && vec3.ChildrenCount == 3
                   && vec3.Children[0] is FloatValueBox
                   && vec3.Children[1] is FloatValueBox
                   && vec3.Children[2] is FloatValueBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is ContainerControl quat
                && quat.ChildrenCount == 3
                && quat.Children[0] is FloatValueBox x
                && quat.Children[1] is FloatValueBox y
                && quat.Children[2] is FloatValueBox z)
            {
                var value = GetValue(box).EulerAngles;
                x.Value = value.X;
                y.Value = value.Y;
                z.Value = value.Z;
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private Quaternion GetValue(InputBox box)
        {
            var value = Quaternion.Identity;
            var v = box.ParentNode.Values[box.Archetype.ValueIndex];
            if (v is Quaternion quat)
                value = quat;
            else if (v is Transform transform)
                value = transform.Orientation;
            return value;
        }

        private void OnQuaternionValueChanged(ValueBox<float> valueBox)
        {
            var control = valueBox.Parent;
            var box = (InputBox)control.Tag;
            var x = ((FloatValueBox)control.Children[0]).Value;
            var y = ((FloatValueBox)control.Children[1]).Value;
            var z = ((FloatValueBox)control.Children[2]).Value;
            box.ParentNode.SetValue(box.Archetype.ValueIndex, Quaternion.Euler(x, y, z));
        }
    }

    class ColorDefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.Type == typeof(Color);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = GetValue(box);
            var control = new ColorValueBox(value, bounds.X, bounds.Y)
            {
                Height = bounds.Height,
                Parent = box.Parent,
                Tag = box,
            };
            control.ColorValueChanged += OnColorValueChanged;
            return control;
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is ColorValueBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is ColorValueBox colorValueBox)
            {
                colorValueBox.Value = GetValue(box);
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private Vector4 GetValue(InputBox box)
        {
            var value = Color.Black;
            var v = box.ParentNode.Values[box.Archetype.ValueIndex];
            if (v is Vector2 vec2)
                value = new Color(vec2.X, vec2.Y, 0.0f, 1.0f);
            else if (v is Vector3 vec3)
                value = new Color(vec3.X, vec3.Y, vec3.Z, 1.0f);
            else if (v is Vector4 vec4)
                value = new Color(vec4.X, vec4.Y, vec4.Z, vec4.W);
            else if (v is Color col)
                value = col;
            else if (v is float f)
                value = new Color(f);
            else if (v is int i)
                value = new Color(i);
            return value;
        }

        private void OnColorValueChanged(ColorValueBox control)
        {
            var box = (InputBox)control.Tag;
            box.ParentNode.SetValue(box.Archetype.ValueIndex, control.Value);
        }
    }

    class EnumDefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            return type.IsEnum;
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = GetValue(box);
            var control = new EnumComboBox(box.CurrentType.Type)
            {
                Location = new Vector2(bounds.X, bounds.Y),
                Size = new Vector2(60.0f, bounds.Height),
                EnumTypeValue = value ?? box.CurrentType.CreateInstance(),
                Parent = box.Parent,
                Tag = box,
            };
            control.EnumValueChanged += OnEnumValueChanged;
            return control;
        }

        private void OnEnumValueChanged(EnumComboBox control)
        {
            var box = (InputBox)control.Tag;
            box.ParentNode.SetValue(box.Archetype.ValueIndex, control.EnumTypeValue);
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is EnumComboBox;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is EnumComboBox enumComboBox)
            {
                enumComboBox.EnumTypeValue = GetValue(box);
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
        }

        private object GetValue(InputBox box)
        {
            var value = box.CurrentType.CreateInstance();
            var v = box.ParentNode.Values[box.Archetype.ValueIndex];
            if (v != null && v.GetType().IsEnum)
                value = v;
            return value;
        }
    }

    class TypeDefaultValueEditor : IDefaultValueEditor
    {
        public bool CanUse(InputBox box, ref ScriptType type)
        {
            if (type.Type == typeof(string) && box.Attributes != null && box.Attributes.Any(x => x.GetType() == typeof(TypeReferenceAttribute)))
                return true;
            return type.Type == typeof(Type) || type.Type == typeof(ScriptType);
        }

        public Control Create(InputBox box, ref Rectangle bounds)
        {
            var value = GetValue(box);
            var control = new TypePickerControl
            {
                Location = new Vector2(bounds.X, bounds.Y),
                Size = new Vector2(60.0f, bounds.Height),
                ValueTypeName = value,
                Parent = box.Parent,
                Tag = box,
            };
            if (box.CurrentType.Type == typeof(Type))
                control.CheckValid = type => type.Type != null;
            control.TypePickerValueChanged += OnTypeValueChanged;
            return control;
        }

        private void OnTypeValueChanged(TypePickerControl control)
        {
            var box = (InputBox)control.Tag;
            var v = box.ParentNode.Values[box.Archetype.ValueIndex];
            if (v is string)
                box.ParentNode.SetValue(box.Archetype.ValueIndex, control.ValueTypeName);
            else if (v is Type)
                box.ParentNode.SetValue(box.Archetype.ValueIndex, TypeUtils.GetType(control.Value));
            else
                box.ParentNode.SetValue(box.Archetype.ValueIndex, control.Value);
        }

        public bool IsValid(InputBox box, Control control)
        {
            return control is TypePickerControl;
        }

        public void UpdateDefaultValue(InputBox box, Control control)
        {
            if (control is TypePickerControl typePickerControl)
            {
                typePickerControl.ValueTypeName = GetValue(box);
            }
        }

        public void UpdateAttributes(InputBox box, object[] attributes, Control control)
        {
            var typeReference = (TypeReferenceAttribute)attributes.FirstOrDefault(x => x.GetType() == typeof(TypeReferenceAttribute));
            var type = typeReference != null ? TypeUtils.GetType(typeReference.TypeName) : ScriptType.Null;
            ((TypePickerControl)control).Type = type ? type : new ScriptType(typeof(object));
        }

        private string GetValue(InputBox box)
        {
            var v = box.ParentNode.Values[box.Archetype.ValueIndex];
            if (v is Type asType)
                return asType.FullName;
            if (v is ScriptType asScriptType)
                return asScriptType.TypeName;
            if (v is string asString)
                return asString;
            return string.Empty;
        }
    }

    /// <summary>
    /// Visject Surface input box element.
    /// </summary>
    /// <seealso cref="FlaxEditor.Surface.Elements.Box" />
    [HideInEditor]
    public class InputBox : Box
    {
        private Control _defaultValueEditor;
        private IDefaultValueEditor _editor;

        /// <summary>
        /// The handlers for the input box default value editing. Used to display the default to the UI.
        /// </summary>
        public static readonly List<IDefaultValueEditor> DefaultValueEditors = new List<IDefaultValueEditor>()
        {
            new BooleanDefaultValueEditor(),
            new IntegerDefaultValueEditor(),
            new FloatingDefaultValueEditor(),
            new StringDefaultValueEditor(),
            new Vector2DefaultValueEditor(),
            new Vector3DefaultValueEditor(),
            new Vector4DefaultValueEditor(),
            new QuaternionDefaultValueEditor(),
            new ColorDefaultValueEditor(),
            new EnumDefaultValueEditor(),
            new TypeDefaultValueEditor(),
        };

        /// <summary>
        /// Gets the control that is used to edit default value (optional).
        /// </summary>
        public Control DefaultValueEditor => _defaultValueEditor;

        /// <inheritdoc />
        public InputBox(SurfaceNode parentNode, NodeElementArchetype archetype)
        : base(parentNode, archetype, archetype.Position)
        {
            // Check if use inlined default value editor
            if (Archetype.ValueIndex != -1)
            {
                ParentNode.ValuesChanged += UpdateDefaultValue;
            }
        }

        /// <summary>
        /// Updates the default value editor UI value.
        /// </summary>
        public void UpdateDefaultValue()
        {
            var currentType = CurrentType.Type;
            if (_defaultValueEditor != null && currentType != null)
            {
                _editor.UpdateDefaultValue(this, _defaultValueEditor);
            }
        }

        /// <inheritdoc />
        public override bool IsOutput => false;

        /// <inheritdoc />
        protected override void OnLocationChanged()
        {
            base.OnLocationChanged();

            if (_defaultValueEditor != null)
            {
                _defaultValueEditor.Location = new Vector2(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y);
            }
        }

        /// <inheritdoc />
        public override void Draw()
        {
            base.Draw();

            // Box
            DrawBox();

            // Draw text
            var style = Style.Current;
            var rect = new Rectangle(Width + 4, 0, 1410, Height);
            Render2D.DrawText(style.FontSmall, Text, rect, Enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center);
        }

        /// <inheritdoc />
        protected override void OnCurrentTypeChanged()
        {
            base.OnCurrentTypeChanged();

            if (_defaultValueEditor != null && !_editor.IsValid(this, _defaultValueEditor))
            {
                _defaultValueEditor.Dispose();
                _defaultValueEditor = null;
                _editor = null;
            }

            if (Connections.Count == 0)
            {
                CreateDefaultEditor();
            }
        }

        /// <inheritdoc />
        public override void OnConnectionsChanged()
        {
            bool showEditor = Connections.Count == 0 && Archetype.ValueIndex != -1;
            if (showEditor)
            {
                CreateDefaultEditor();
            }

            if (_defaultValueEditor != null)
            {
                _defaultValueEditor.Enabled = showEditor;
                _defaultValueEditor.Visible = showEditor;
            }
        }

        /// <inheritdoc />
        protected override void OnAttributesChanged()
        {
            OnCurrentTypeChanged();
            _editor?.UpdateAttributes(this, _attributes ?? Utils.GetEmptyArray<object>(), _defaultValueEditor);
        }

        /// <inheritdoc />
        public override void OnSurfaceCanEditChanged(bool canEdit)
        {
            base.OnSurfaceCanEditChanged(canEdit);

            if (_defaultValueEditor != null)
            {
                _defaultValueEditor.Enabled = canEdit;
            }
        }

        /// <summary>
        /// Creates the default value editor control.
        /// </summary>
        private void CreateDefaultEditor()
        {
            if (_defaultValueEditor != null || Archetype.ValueIndex == -1)
                return;

            for (int i = 0; i < DefaultValueEditors.Count; i++)
            {
                if (DefaultValueEditors[i].CanUse(this, ref _currentType))
                {
                    var bounds = new Rectangle(X + Width + 8 + Style.Current.FontSmall.MeasureText(Text).X, Y, 90, Height);
                    _editor = DefaultValueEditors[i];
                    _defaultValueEditor = _editor.Create(this, ref bounds);
                    if (_attributes != null)
                        _editor.UpdateAttributes(this, _attributes, _defaultValueEditor);
                    break;
                }
            }
        }
    }
}
