﻿using ABI.CCK.Components;
using ABI.CCK.Scripts.Editor;
using ABI.CCK.Scripts.Editor.Tools;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

namespace ABI.CCK.Scripts.Editor
{
    //[CustomEditor(typeof(CVRSpawnable), true)]
    public class CCK_CVRSpawnableEditor : UnityEditor.Editor
    {
        private CVRSpawnable _spawnable;
        
        private ReorderableList _syncedValueList;
        private CVRSpawnableValue _syncedValueEntry;
        
        private ReorderableList _subSyncList;
        private CVRSpawnableSubSync _subSyncEntry;

        private float _subSyncCosts;
        private bool _outOfBoundsError;

        #region Unity Events

        public void OnEnable()
        {
            // Occurs on recompile
            if (target == null)
                return; 
            
            _spawnable = (CVRSpawnable)target;
            _spawnable.Reset();
            
            if (_syncedValueList == null) 
                InitializeList();
            
            if (_subSyncList == null) 
                InitializeSubSyncList();
        }
        
        public override void OnInspectorGUI()
        {
            if (_spawnable == null)
                return;

            EditorGUI.BeginChangeCheck();
            
            _spawnable.spawnHeight = EditorGUILayout.FloatField("Spawn Height", _spawnable.spawnHeight);

            _spawnable.propPrivacy = (CVRSpawnable.PropPrivacy) EditorGUILayout.EnumPopup("Prop Usage", _spawnable.propPrivacy);
            
            GUILayout.BeginVertical("HelpBox");
            
            GUILayout.BeginHorizontal ();
            _spawnable.useAdditionalValues = EditorGUILayout.Toggle (_spawnable.useAdditionalValues, GUILayout.Width(16));
            EditorGUILayout.LabelField ("Enable Sync Values", GUILayout.Width(250));
            GUILayout.EndHorizontal ();

            if (_spawnable.useAdditionalValues)
            {
                UpdateSubSyncCosts();
                
                GUILayout.BeginHorizontal();
                GUILayout.BeginVertical("GroupBox");

                var SyncCost = _spawnable.syncValues.Count + _subSyncCosts;
                
                Rect _rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight);
                EditorGUI.ProgressBar(_rect, SyncCost / 40f, Mathf.CeilToInt(SyncCost) + " of 40 Synced Values used");
                EditorGUILayout.Space();

                if (_subSyncCosts > 0f)
                {
                    EditorGUILayout.HelpBox(_subSyncCosts.ToString("F2")+" Values are used for Sub Sync Transforms", MessageType.Info);
                    EditorGUILayout.Space();
                }
                
                _syncedValueList.displayAdd = SyncCost <= 39f;
                _syncedValueList.DoLayoutList();
                
                EditorGUILayout.Space();
                
                _subSyncList.displayAdd = SyncCost <= 39f;
                _subSyncList.DoLayoutList();
                
                GUILayout.EndVertical();
                GUILayout.EndHorizontal();
            }
            
            GUILayout.EndVertical();

            if (_outOfBoundsError)
            {
                EditorGUILayout.HelpBox("A Sub Sync Transform is out of bounds by default. This object will snap to its bounds, when it is being synced.", MessageType.Error);
                _outOfBoundsError = false;
            }

            if (EditorGUI.EndChangeCheck())
                EditorUtility.SetDirty(_spawnable);
        }

        #endregion

        #region ReorderableList SyncedValue

        private void InitializeList()
        {
            _syncedValueList = new ReorderableList(_spawnable.syncValues, typeof(CVRAdvancedSettingsEntry), true, true, true, true)
                {
                    drawHeaderCallback = OnDrawHeaderSyncedValue,
                    drawElementCallback = OnDrawElementSyncedValue,
                    elementHeightCallback = OnHeightElementSyncedValue,
                    onAddCallback = OnAddSyncedValue,
                    onChangedCallback = OnChangedSyncedValue
                };
        }
        
        private void OnChangedSyncedValue(ReorderableList list)
        {
            //EditorUtility.SetDirty(target);
        }

        private void OnAddSyncedValue(ReorderableList list)
        {
            if (_spawnable.syncValues.Count >= 40) return;
            _spawnable.syncValues.Add(new CVRSpawnableValue());
            Repaint();
        }

        private float OnHeightElementSyncedValue(int index)
        {
            return EditorGUIUtility.singleLineHeight * 7.5f;
        }

        private void OnDrawElementSyncedValue(Rect rect, int index, bool isactive, bool isfocused)
        {
            if (index > _spawnable.syncValues.Count) return;
            _syncedValueEntry = _spawnable.syncValues[index];
            
            rect = new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight);
            
            float spacing = EditorGUIUtility.singleLineHeight * 1.25f;
            float originalLabelWidth = EditorGUIUtility.labelWidth;
            EditorGUIUtility.labelWidth = 120;

            _syncedValueEntry.name = EditorGUI.TextField(rect, "Name", _syncedValueEntry.name);
            rect.y += spacing;
            _syncedValueEntry.startValue = EditorGUI.FloatField(rect, "Start Value", _syncedValueEntry.startValue);
            rect.y += spacing;
            _syncedValueEntry.updatedBy = (CVRSpawnableValue.UpdatedBy) EditorGUI.EnumPopup(rect, "Update Type", _syncedValueEntry.updatedBy);
            rect.y += spacing;
            _syncedValueEntry.updateMethod = (CVRSpawnableValue.UpdateMethod) EditorGUI.EnumPopup(rect, "Update Method", _syncedValueEntry.updateMethod);
            rect.y += spacing;
            _syncedValueEntry.animator = (Animator)EditorGUI.ObjectField(rect, "Connected Animator", _syncedValueEntry.animator, typeof(Animator), true);
            rect.y += spacing;
            
            _syncedValueEntry.animatorParameterName = EditorGUIExtensions.AdvancedDropdownInput(rect, _syncedValueEntry.animatorParameterName,
                CVRCommon.GetParametersFromAnimatorAsString(_syncedValueEntry.animator), "Animator Parameter","No Parameters");

            EditorGUIUtility.labelWidth = originalLabelWidth;
        }
        
        private void OnDrawHeaderSyncedValue(Rect rect)
        {
            Rect _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);
            GUI.Label(_rect, "Values");
            EditorGUIExtensions.UtilityMenu(_rect, _syncedValueList);
        }

        #endregion

        #region ReorderableList SubSync

        private void InitializeSubSyncList()
        {
            _subSyncList = new ReorderableList(_spawnable.subSyncs, typeof(CVRSpawnableSubSync), false, true, true, true)
                {
                    drawHeaderCallback = OnDrawHeaderSubSync,
                    drawElementCallback = OnDrawElementSubSync,
                    elementHeightCallback = OnHeightElementSubSync,
                    onAddCallback = OnAddSubSync,
                    onChangedCallback = OnChangedSubSync
                };
        }

        private void OnDrawHeaderSubSync(Rect rect)
        {
            Rect _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);
            GUI.Label(_rect, "Sub Sync Transforms");
            EditorGUIExtensions.UtilityMenu(_rect, _subSyncList);
        }

        private void OnDrawElementSubSync(Rect rect, int index, bool isactive, bool isfocused)
        {
            if (index > _spawnable.subSyncs.Count) return;
            _subSyncEntry = _spawnable.subSyncs[index];
    
            rect = new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight);
            
            float spacing = EditorGUIUtility.singleLineHeight * 1.25f;
            float originalLabelWidth = EditorGUIUtility.labelWidth;
            EditorGUIUtility.labelWidth = 120;

            _subSyncEntry.transform = (Transform)EditorGUI.ObjectField(rect, "Transform", _subSyncEntry.transform, typeof(Transform), true);
            rect.y += spacing;
            _subSyncEntry.syncedValues = (CVRSpawnableSubSync.SyncFlags) EditorGUI.EnumFlagsField(rect, "Synced Properties", _subSyncEntry.syncedValues);
            rect.y += spacing;
            _subSyncEntry.precision = (CVRSpawnableSubSync.SyncPrecision) EditorGUI.EnumPopup(rect, "Sync Precision", _subSyncEntry.precision);
            rect.y += spacing;

            if (_subSyncEntry.precision != CVRSpawnableSubSync.SyncPrecision.Full)
            {
                _subSyncEntry.syncBoundary = EditorGUI.FloatField(rect, "Sync Boundary", _subSyncEntry.syncBoundary);
                rect.y += spacing;
                if (_subSyncEntry.transform != null)
                {
                    Vector3 localPosition = _subSyncEntry.transform.localPosition;
                    _outOfBoundsError = Mathf.Abs(localPosition.x) > _subSyncEntry.syncBoundary ||
                                       Mathf.Abs(localPosition.y) > _subSyncEntry.syncBoundary ||
                                       Mathf.Abs(localPosition.z) > _subSyncEntry.syncBoundary;
                }
            }

            EditorGUIUtility.labelWidth = originalLabelWidth;
        }

        private float OnHeightElementSubSync(int index)
        {
            if (_spawnable.subSyncs[index].precision == CVRSpawnableSubSync.SyncPrecision.Full) 
                return EditorGUIUtility.singleLineHeight * 3.75f;
            return EditorGUIUtility.singleLineHeight * 5f;
        }

        private void OnAddSubSync(ReorderableList list)
        {
            if (_spawnable.subSyncs.Count >= 40) return;
            _spawnable.subSyncs.Add(new CVRSpawnableSubSync());
            Repaint();
        }

        private void OnChangedSubSync(ReorderableList list)
        {
            //EditorUtility.SetDirty(target);
        }

        #endregion
        
        #region Private Methods

        private void UpdateSubSyncCosts()
        {
            _subSyncCosts = 0f;
            
            foreach (CVRSpawnableSubSync subSync in _spawnable.subSyncs)
            {
                if (subSync.syncedValues.HasFlag(CVRSpawnableSubSync.SyncFlags.TransformX))
                    _subSyncCosts += (int) subSync.precision / 4f;
                if (subSync.syncedValues.HasFlag(CVRSpawnableSubSync.SyncFlags.TransformY))
                    _subSyncCosts += (int) subSync.precision / 4f;
                if (subSync.syncedValues.HasFlag(CVRSpawnableSubSync.SyncFlags.TransformZ))
                    _subSyncCosts += (int) subSync.precision / 4f;
                if (subSync.syncedValues.HasFlag(CVRSpawnableSubSync.SyncFlags.RotationX))
                    _subSyncCosts += (int) subSync.precision / 4f;
                if (subSync.syncedValues.HasFlag(CVRSpawnableSubSync.SyncFlags.RotationY))
                    _subSyncCosts += (int) subSync.precision / 4f;
                if (subSync.syncedValues.HasFlag(CVRSpawnableSubSync.SyncFlags.RotationZ))
                    _subSyncCosts += (int) subSync.precision / 4f;
            }
        }

        #endregion
    }
}