﻿#if VRC_SDK_VRCSDK3 && UNITY_EDITOR

using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using VRC.SDKBase;
using VRC.SDKBase.Editor.Versioning;
using VRCPickup = VRC.SDK3.Components.VRCPickup;

namespace VRC.SDK3.Editor
{
    [CanEditMultipleObjects]
    [CustomEditor(typeof(VRCPickup))]
    public class VRCPickupEditor3 : VRCInspectorBase
    {
        private SerializedProperty propVersion;
        private SerializedProperty propMomentumTransferMethod;
        private SerializedProperty propDisallowTheft;
        private SerializedProperty propExactGun;
        private SerializedProperty propExactGrip;
        private SerializedProperty propAllowManipulationWhenEquipped;
        private SerializedProperty propOrientation;
        private SerializedProperty propAutoHold;
        private SerializedProperty propInteractionText;
        private SerializedProperty propUseText;
        private SerializedProperty propThrowVelocityBoostMinSpeed;
        private SerializedProperty propThrowVelocityBoostScale;
        private SerializedProperty propOutlineRenderers;
        private SerializedProperty propPickupable;
        private SerializedProperty propProximity;

        private PropertyField fieldMomentumTransferMethod;
        private PropertyField fieldDisallowTheft;
        private PropertyField fieldExactGun;
        private PropertyField fieldExactGrip;
        private PropertyField fieldAllowManipulationWhenEquipped;
        private PropertyField fieldOrientation;
        private PropertyField fieldAutoHold;
        private PropertyField fieldInteractionText;
        private PropertyField fieldUseText;
        private PropertyField fieldThrowVelocityBoostMinSpeed;
        private PropertyField fieldThrowVelocityBoostScale;
        private PropertyField fieldOutlineRenderers;
        private PropertyField fieldPickupable;
        private PropertyField fieldProximity;
        
        private ComponentVersionUI<VRCPickup.Version> versionUI;

        
        private void OnEnable()
        {
            propVersion = serializedObject.FindProperty(nameof(VRCPickup.version));
            propMomentumTransferMethod = serializedObject.FindProperty(nameof(VRCPickup.MomentumTransferMethod));
            propDisallowTheft = serializedObject.FindProperty(nameof(VRCPickup.DisallowTheft));
            propExactGun = serializedObject.FindProperty(nameof(VRCPickup.ExactGun));
            propExactGrip = serializedObject.FindProperty(nameof(VRCPickup.ExactGrip));
            propAllowManipulationWhenEquipped = serializedObject.FindProperty(nameof(VRCPickup.allowManipulationWhenEquipped));
            propOrientation = serializedObject.FindProperty(nameof(VRCPickup.orientation));
            propAutoHold = serializedObject.FindProperty(nameof(VRCPickup.AutoHold));
            propInteractionText = serializedObject.FindProperty(nameof(VRCPickup.InteractionText));
            propUseText = serializedObject.FindProperty(nameof(VRCPickup.UseText));
            propThrowVelocityBoostMinSpeed = serializedObject.FindProperty(nameof(VRCPickup.ThrowVelocityBoostMinSpeed));
            propThrowVelocityBoostScale = serializedObject.FindProperty(nameof(VRCPickup.ThrowVelocityBoostScale));
            propOutlineRenderers = serializedObject.FindProperty(nameof(VRCPickup.OutlineRenderers));
            propPickupable = serializedObject.FindProperty(nameof(VRCPickup.pickupable));
            propProximity = serializedObject.FindProperty(nameof(VRCPickup.proximity));
        }

        #region Inspector GUI Construction (Common Pattern for All Custom Editors)
        
        public override void BuildInspectorGUI()
        {
            base.BuildInspectorGUI();
            var pickup = (VRCPickup)target;

            // Add version system
            var migrator = new VRCPickupVersionMigrator(propAutoHold, propOrientation, propInteractionText, OnVersionChanged);
            versionUI = AddVersionSystem(propVersion, migrator, 
                "https://creators.vrchat.com/worlds/components/vrc_pickup#versions");

            // Guide new creators towards adding a collider
            // != null is specific to the Unity lifetime check, as this may be invoked with a destroyed object when exiting play mode...
            if (pickup != null && !pickup.TryGetComponent<Collider>(out _))
            {
                // Prompt to add since Collider is abstract so we can't use RequireComponent
                Root.Add(new HelpBox("This pickup has no collider on it, so it won't be usable. You can add a collider using the Add Component button below.", HelpBoxMessageType.Warning));
            }

            // Create AutoHold enum field (shown in V1.0, hidden in V1.1+ when toggle is used)
            fieldAutoHold = AddField(propAutoHold);
            fieldAutoHold.RegisterValueChangeCallback(AutoHoldCallback);

            fieldUseText = AddFieldTooltip(propUseText,
                "Text to display describing action for clicking button, when this pickup is already being held.");
            fieldInteractionText = AddFieldTooltip(propInteractionText, 
                "Text displayed when user hovers over the pickup.");
            fieldProximity = AddField(propProximity);
            
            fieldOrientation = AddField(propOrientation);
            fieldOrientation.RegisterValueChangeCallback(OrientationCallback);
            
            fieldExactGun = AddField(propExactGun);
            fieldExactGrip = AddField(propExactGrip);
            fieldPickupable = AddField(propPickupable);
            fieldDisallowTheft = AddField(propDisallowTheft);
            fieldAllowManipulationWhenEquipped = AddField(propAllowManipulationWhenEquipped);
            fieldMomentumTransferMethod = AddField(propMomentumTransferMethod);
            fieldThrowVelocityBoostMinSpeed = AddField(propThrowVelocityBoostMinSpeed);
            fieldThrowVelocityBoostScale = AddField(propThrowVelocityBoostScale);
            fieldOutlineRenderers = AddField(propOutlineRenderers);
            
            // Now that all fields are created, we can update their visibility based on version
            SetupAutoHoldField(pickup.version);
            RefreshAllFieldVisibility(pickup.version);
        }

        // Creates the toggle field for modern AutoHold UI (V1.1+)
        private void SetupAutoHoldField(VRCPickup.Version version)
        {
            if (IsModernVersion(version))
            {
                var autoHold = (VRC_Pickup.AutoHoldMode)propAutoHold.enumValueIndex;

                // Kick out legacy auto-hold values for this version.
                if (autoHold == VRC_Pickup.AutoHoldMode.AutoDetect || autoHold == VRC_Pickup.AutoHoldMode.Sometimes)
                {
                    var orientation = (VRC_Pickup.PickupOrientation)propOrientation.enumValueIndex;
                    bool willBeAutoHold = VRC_Pickup.IsGlobalAutoHoldPickup(autoHold, orientation);
                    propAutoHold.enumValueIndex = willBeAutoHold ? (int)VRC_Pickup.AutoHoldMode.Yes : (int)VRC_Pickup.AutoHoldMode.No;
                    serializedObject.ApplyModifiedProperties();
                }
            }

            var container = fieldAutoHold.parent;
            
            if (!fieldAutoHold.ClassListContains("auto-hold-enum-field"))
                fieldAutoHold.AddToClassList("auto-hold-enum-field");
        
            var toggleField = container.Query().Name("autoHoldToggle").First() as Toggle;
            if (toggleField == null)
            {
                toggleField = new Toggle("Auto Hold")
                {
                    name = "autoHoldToggle",
                    tooltip = "When enabled, the pickup will stay in hand when trigger is released"
                };
                toggleField.AddToClassList("auto-hold-toggle-field");
        
                toggleField.RegisterValueChangedCallback(evt => {
                    serializedObject.Update();
                    propAutoHold.enumValueIndex = evt.newValue ?
                        (int)VRC_Pickup.AutoHoldMode.Yes :
                        (int)VRC_Pickup.AutoHoldMode.No;
                    serializedObject.ApplyModifiedProperties();
                    RefreshUseTextVisibility();
                });
        
                container.Insert(container.IndexOf(fieldAutoHold) + 1, toggleField);
            }
        }
        
        // Switches between enum dropdown (V1.0) and toggle (V1.1+) for AutoHold field
        private void UpdateAutoHoldFieldVisibility(VRCPickup.Version version)
        {
            var container = fieldAutoHold.parent;
            var toggleField = container.Query().Name("autoHoldToggle").First() as Toggle;
        
            if (toggleField == null) return;
        
            if (IsModernVersion(version))
            {
                fieldAutoHold.style.display = DisplayStyle.None;
                toggleField.style.display = DisplayStyle.Flex;
                UpdateToggleFromEnumValue(toggleField);
            }
            else
            {
                fieldAutoHold.style.display = DisplayStyle.Flex;
                toggleField.style.display = DisplayStyle.None;
            }
        }
        
        // Syncs toggle state with underlying enum value without triggering callbacks
        private void UpdateToggleFromEnumValue(Toggle toggle)
        {
            bool treatAsAutoHold = propAutoHold.enumValueIndex
                is (int)VRC_Pickup.AutoHoldMode.Yes
                or (int)VRC_Pickup.AutoHoldMode.Sometimes;
            toggle.SetValueWithoutNotify(treatAsAutoHold);
        }
        
        #endregion

        #region VRC Pickup-Specific Helper Methods
        
        // Checks if version uses modern UI (toggle instead of enum dropdown)
        private bool IsModernVersion(VRCPickup.Version version)
        {
            return version > VRCPickup.Version.Version_1_0;
        }

        private void AutoHoldCallback(SerializedPropertyChangeEvent evt)
        {
            RefreshUseTextVisibility();
        }
        
        private void RefreshUseTextVisibility()
        {
            bool canDisplayUseText = propAutoHold.enumValueIndex
                is (int)VRC_Pickup.AutoHoldMode.Yes
                or (int)VRC_Pickup.AutoHoldMode.Sometimes
                or (int)VRC_Pickup.AutoHoldMode.AutoDetect; // Use text can appear if gun or grip transforms are assigned.
            
            fieldUseText.SetVisible(canDisplayUseText);
            
            // Update version upgrade info visibility
            versionUI?.RefreshUpgradeInfo();
        }

        private void OrientationCallback(SerializedPropertyChangeEvent evt) => OrientationChanged();

        // Shows/hides ExactGun and ExactGrip fields based on pickup orientation
        private void OrientationChanged()
        {
            switch ((VRC_Pickup.PickupOrientation)propOrientation.enumValueIndex)
            {
                case VRC_Pickup.PickupOrientation.Any:
                    fieldExactGun.SetVisible(true);
                    fieldExactGrip.SetVisible(true);
                    break;
                case VRC_Pickup.PickupOrientation.Grip:
                    fieldExactGun.SetVisible(false);
                    fieldExactGrip.SetVisible(true);
                    break;
                case VRC_Pickup.PickupOrientation.Gun:
                    fieldExactGun.SetVisible(true);
                    fieldExactGrip.SetVisible(false);
                    break;
            }
            
            // Update version upgrade info since orientation affects AutoDetect migration
            versionUI?.RefreshUpgradeInfo();
        }
        
        #endregion
        
        #region Version-Dependent UI Management (Pattern for Versioned Components)
        
        // Refreshes all field visibility based on current version and field states
        private void RefreshAllFieldVisibility(VRCPickup.Version version)
        {
            versionUI?.RefreshUpgradeInfo();
            UpdateAutoHoldFieldVisibility(version);
            OrientationChanged();
            RefreshUseTextVisibility();
        }
        
        // Called when version changes to update UI elements
        private void OnVersionChanged(VRCPickup.Version newVersion)
        {
            RefreshAllFieldVisibility(newVersion);
        }
        
        #endregion
    }
}
#endif