using System;
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using ABI.CCK.Components;
using UnityEditor.Profiling;
using UnityEditorInternal;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using AnimatorController = UnityEditor.Animations.AnimatorController;
using AnimatorControllerParameterType = UnityEngine.AnimatorControllerParameterType;

namespace ABI.CCK.Scripts.Editor
{
    [CustomEditor(typeof(ABI.CCK.Components.CVRAvatar))]
    public class CCK_CVRAvatarEditor : UnityEditor.Editor
    {
        private static string[] _visemeNames = new[] {"sil", "PP", "FF", "TH", "DD", "kk", "CH", "SS", "nn", "RR", "aa", "E", "ih", "oh", "ou"};
        public static string[] coreParameters = {"MovementX", "MovementY", "Grounded", "Emote", "GestureLeft", 
            "GestureRight", "Toggle", "Sitting", "Crouching", "CancelEmote", "Prone", "Flying"};
        
        private CVRAvatar _avatar;
        private int _blink;
        
        private List<string> _blendShapeNames = null;
        
        private ReorderableList reorderableList;
        private CVRAdvancedSettingsEntry entity = null;
        private int syncedValues = 0;
        private List<string> animatorParameters = new List<string>();
        private bool definitionContainsError = false;

        private void InitializeList()
        {
            if (_avatar.avatarSettings == null) return;

            reorderableList = new ReorderableList(_avatar.avatarSettings.settings, typeof(CVRAdvancedSettingsEntry),
                                                  true, true, true, true);
            reorderableList.drawHeaderCallback = OnDrawHeader;
            reorderableList.drawElementCallback = OnDrawElement;
            reorderableList.elementHeightCallback = OnHeightElement;
            reorderableList.onAddCallback = OnAdd;
            reorderableList.onChangedCallback = OnChanged; 
        }

        public override void OnInspectorGUI()
        {
            if (_avatar == null) _avatar = (CVRAvatar) target;

            GetBlendShapeNames();
            
            EditorGUILayout.LabelField("General avatar settings", EditorStyles.boldLabel);
            _avatar.viewPosition = EditorGUILayout.Vector3Field("View Position", _avatar.viewPosition);
            EditorGUILayout.HelpBox(CCKLocalizationProvider.GetLocalizedText("ABI_UI_AVATAR_INFO_VIEWPOINT"), MessageType.Info);
            _avatar.voicePosition = EditorGUILayout.Vector3Field("Voice Position", _avatar.voicePosition);
            _avatar.voiceParent = (CVRAvatar.CVRAvatarVoiceParent) EditorGUILayout.EnumPopup("Voice Parent", _avatar.voiceParent);
            EditorGUILayout.HelpBox(CCKLocalizationProvider.GetLocalizedText("ABI_UI_AVATAR_INFO_VOICE_POSITION"), MessageType.Info);
            
            EditorGUILayout.LabelField("");
            EditorGUILayout.LabelField("Avatar customization", EditorStyles.boldLabel);
            _avatar.overrides = (AnimatorOverrideController)EditorGUILayout.ObjectField("Animation Overrides", _avatar.overrides, typeof(AnimatorOverrideController), true, null);
            EditorGUILayout.HelpBox(CCKLocalizationProvider.GetLocalizedText("ABI_UI_AVATAR_INFO_OVERRIDE_CONTROLLER"), MessageType.Info);
            
            EditorGUILayout.LabelField("");
            EditorGUILayout.LabelField("Blinking and Visemes", EditorStyles.boldLabel);
            _avatar.bodyMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Face Mesh", _avatar.bodyMesh, typeof(SkinnedMeshRenderer), true);

            _avatar.useEyeMovement = EditorGUILayout.Toggle("Use Eye Movement", _avatar.useEyeMovement);
            EditorGUILayout.HelpBox(CCKLocalizationProvider.GetLocalizedText("ABI_UI_AVATAR_INFO_EYE_MOVEMENT"), MessageType.Info);
            _avatar.useBlinkBlendshapes = EditorGUILayout.Toggle("Use Blink Blendshapes", _avatar.useBlinkBlendshapes);
            EditorGUILayout.HelpBox(CCKLocalizationProvider.GetLocalizedText("ABI_UI_AVATAR_INFO_BLinking"), MessageType.Info);
            for (int i = 0; i < 4; i++)
            {
                int current = 0;
                for (int j = 0; j < _blendShapeNames.Count; ++j)
                    if (_avatar.blinkBlendshape[i] == _blendShapeNames[j])
                        current = j;
                
                int blink = EditorGUILayout.Popup("Blink " + (i + 1), current, _blendShapeNames.ToArray());
                _avatar.blinkBlendshape[i] = _blendShapeNames[blink];
            }
            
            _avatar.useVisemeLipsync = EditorGUILayout.Toggle("Use Viseme Blendshapes", _avatar.useVisemeLipsync);

            if (_avatar.visemeBlendshapes == null || _avatar.visemeBlendshapes.Length != _visemeNames.Length)
                _avatar.visemeBlendshapes = new string[_visemeNames.Length];
            
            for (int i = 0; i < _visemeNames.Length; i++)
            {
                int current = 0;
                for (int j = 0; j < _blendShapeNames.Count; ++j)
                    if (_avatar.visemeBlendshapes[i] == _blendShapeNames[j])
                        current = j;
                
                int viseme = EditorGUILayout.Popup("Viseme: " + _visemeNames[i], current, _blendShapeNames.ToArray());
                _avatar.visemeBlendshapes[i] = _blendShapeNames[viseme];
            }

            if (GUILayout.Button("Auto select Visemes"))
            {
                FindVisemes();
                _avatar.useVisemeLipsync = true;
            }

            EditorGUILayout.HelpBox(CCKLocalizationProvider.GetLocalizedText("ABI_UI_AVATAR_INFO_EYE_VISEMES"), MessageType.Info);

            
            //Advanced Avatar Settings
            GUILayout.BeginVertical("HelpBox");
            
            GUILayout.BeginHorizontal();
            _avatar.avatarUsesAdvancedSettings = EditorGUILayout.Toggle(_avatar.avatarUsesAdvancedSettings, GUILayout.Width(16));
            EditorGUILayout.LabelField("Enable Advanced Settings", GUILayout.Width(250));
            GUILayout.EndHorizontal();
            
            if (_avatar.avatarUsesAdvancedSettings)
            {
                GUILayout.BeginHorizontal();
                GUILayout.BeginVertical("GroupBox");

                if (_avatar.avatarSettings == null || !_avatar.avatarSettings.initialized)
                {
                    CreateAvatarSettings(_avatar);
                }

                _avatar.avatarSettings.baseController = (RuntimeAnimatorController) EditorGUILayout.ObjectField("Base Animator",
                    _avatar.avatarSettings.baseController, typeof(RuntimeAnimatorController));
                if (_avatar.avatarSettings.baseController is AnimatorOverrideController)
                    _avatar.avatarSettings.baseController = null;
                
                EditorGUILayout.HelpBox("This is the Base Animator that is extended for the creation of your Advanced Avatar Settings. "+
                                        "If you do not want to extend a specific Animator Controller, make sure that the Default Avatar Animator "+
                                        "From the Directory \"ABI.CCK/Animations\" is used here.", MessageType.Info);
                
                _avatar.avatarSettings.baseOverrideController = (RuntimeAnimatorController) EditorGUILayout.ObjectField("Override Controller",
                    _avatar.avatarSettings.baseOverrideController, typeof(RuntimeAnimatorController));
                if (_avatar.avatarSettings.baseOverrideController is AnimatorController)
                    _avatar.avatarSettings.baseOverrideController = null;
                
                EditorGUILayout.HelpBox("You can Put your previous Override Controller here in order to put your overrides in "+
                                        "the newly created Override Controller.", MessageType.Info);
                
                UpdateSyncCount();
                Rect _rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight);
                EditorGUI.ProgressBar(_rect, syncedValues / 100f, syncedValues + " of 100 Synced Parameter Slots used");
                
                if (reorderableList == null) InitializeList();
                reorderableList.DoLayoutList();

                if (!definitionContainsError)
                {
                    if (GUILayout.Button("CreateAnimator")) CreateAnimator();
                }

                if (_avatar.avatarSettings.overrides != null && _avatar.avatarSettings.overrides != _avatar.overrides)
                {
                    if (GUILayout.Button("Attach created Override to Avatar"))
                    {
                        _avatar.overrides = _avatar.avatarSettings.overrides;
                    }
                }
                
                GUILayout.EndVertical();
                GUILayout.EndHorizontal();
            }

            GUILayout.EndVertical();

            if (GUI.changed)
            {
                EditorUtility.SetDirty(target);
            }
        }

        private void UpdateSyncCount()
        {
            syncedValues = 0;

            if (_avatar.avatarSettings == null) return;
            
            animatorParameters.Clear();
            definitionContainsError = false;

            if (_avatar.avatarSettings.baseController != null)
            {
                var animator = (AnimatorController) _avatar.avatarSettings.baseController;

                foreach (var parameter in animator.parameters)
                {
                    if (parameter.type == AnimatorControllerParameterType.Float && parameter.name.Length > 0 &&
                        !coreParameters.Contains(parameter.name) && parameter.name.Substring(0, 1) != "#")
                    {
                        syncedValues += 1;
                        animatorParameters.Add(parameter.name);
                    }
                }
            }

            foreach (var entry in _avatar.avatarSettings.settings)
            {
                if (entry == null) continue;
                if (entry.name == null) continue;
                if (entry.name.Length == 0) continue;
                if (entry.name.Substring(0, 1) == "#") continue;

                switch (entry.type)
                {
                    case CVRAdvancedSettingsEntry.SettingsType.GameObjectToggle:
                        syncedValues += 1;
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.GameObjectDropdown:
                        syncedValues += 1;
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.MaterialColor:
                        syncedValues += 3;
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.Joystick2D:
                        syncedValues += 2;
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.Joystick3D:
                        syncedValues += 3;
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.InputVector2:
                        syncedValues += 2;
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.InputVector3:
                        syncedValues += 3;
                        break;
                    default:
                        syncedValues += 1;
                        break;
                }
            }
        }

        private void CreateAnimator()
        {
            if (_avatar.avatarSettings.baseController == null)
            {
                EditorUtility.DisplayDialog("Animator Error", "The Base Animator was not selected. No new Animator Controller was created.", "OK");
                return;
            }
            
            if (_avatar.avatarSettings.animator != null)
            {
                if (!EditorUtility.DisplayDialog("Animator already created",
                    "There is Animator already created for this avatar.", "Override", "Cancel"))
                {
                    return;
                }
            }
            
            string pathToCurrentFolder = "Assets/AdvancedSettings.Generated";
            if (!AssetDatabase.IsValidFolder(pathToCurrentFolder)) AssetDatabase.CreateFolder("Assets", "AdvancedSettings.Generated");

            var folderPath = pathToCurrentFolder + "/" + _avatar.name + "_AAS";
            if (!AssetDatabase.IsValidFolder(folderPath)) AssetDatabase.CreateFolder(pathToCurrentFolder, _avatar.name + "_AAS");
            var animatorPath = pathToCurrentFolder + "/" + _avatar.name + "_AAS/" + _avatar.name + "_aas.controller";
            AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(_avatar.avatarSettings.baseController.GetInstanceID()), animatorPath);
            
            _avatar.avatarSettings.animator = AssetDatabase.LoadAssetAtPath<AnimatorController>(animatorPath);

            foreach (var entry in _avatar.avatarSettings.settings)
            {
                entry.setting.SetupAnimator(ref _avatar.avatarSettings.animator, entry.machineName, folderPath);
            }

            if (_avatar.avatarSettings.baseOverrideController != null)
            {
                var overridePath = pathToCurrentFolder + "/" + _avatar.name + "_AAS/" + _avatar.name + "_aas_overrides.overrideController";
                AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(_avatar.avatarSettings.baseOverrideController.GetInstanceID()), overridePath);
                _avatar.avatarSettings.overrides = AssetDatabase.LoadAssetAtPath<AnimatorOverrideController>(overridePath);
                _avatar.avatarSettings.overrides.runtimeAnimatorController = _avatar.avatarSettings.animator;
            }
            else
            {
                _avatar.avatarSettings.overrides = new AnimatorOverrideController(_avatar.avatarSettings.animator);
                AssetDatabase.CreateAsset(_avatar.avatarSettings.overrides, pathToCurrentFolder + "/" + _avatar.name + "_AAS/" + _avatar.name + "_aas_overrides.overrideController");
            }
            
            AssetDatabase.SaveAssets();
        }
        
        private float OnHeightElement(int index)
        {
            if (index > _avatar.avatarSettings.settings.Count) return EditorGUIUtility.singleLineHeight * 1f;
            entity = _avatar.avatarSettings.settings[index];

            // When collapsed only return one line height
            if (!entity.setting.isCollapsed) return EditorGUIUtility.singleLineHeight * 1.25f;

            switch (entity.type) 
            {
                case CVRAdvancedSettingsEntry.SettingsType.GameObjectToggle:
                {
                    var gameObjectToggle = (CVRAdvancesAvatarSettingGameObjectToggle) entity.setting;
                    if (gameObjectToggle == null || gameObjectToggle.gameObjectTargets == null)
                        return EditorGUIUtility.singleLineHeight * 9f;
                    float height = 9.25f;
                    if (gameObjectToggle.useAnimationClip) 
                    {
                        height -= 1.25f;
                    } 
                    else 
                    {
                        foreach (var target in gameObjectToggle.gameObjectTargets) 
                        {
                            if (!target.isCollapsed) 
                            {
                                height += 1.25f;
                            } 
                            else 
                            {
                                height += 3.75f;
                            }
                        }
                        if (gameObjectToggle.gameObjectTargets.Count == 0) 
                        {
                            height += 1f;
                        }
                    }
                    return EditorGUIUtility.singleLineHeight * height;
                }
                case CVRAdvancedSettingsEntry.SettingsType.GameObjectDropdown:
                {
                    
                    var gameObjectDropdown = (CVRAdvancesAvatarSettingGameObjectDropdown) entity.setting;
                    if (gameObjectDropdown == null || gameObjectDropdown.options == null)
                        return EditorGUIUtility.singleLineHeight * 8f;
                    float height = 7;
                    foreach (var option in gameObjectDropdown.options) 
                    {
                        height += 1;
                        if (option.isCollapsed) 
                        {
                            height += 4;
                            if (option.useAnimationClip) 
                            {
                                height -= 1.5f;
                            } 
                            else 
                            {
                                if (option.gameObjectTargets.Count != 0) 
                                {
                                    foreach (var target in option.gameObjectTargets) 
                                    {
                                        if (!target.isCollapsed) 
                                        {
                                            height += 1;
                                        } 
                                        else 
                                        {
                                            height += 3;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    return EditorGUIUtility.singleLineHeight * height * 1.25f;
                }
                case CVRAdvancedSettingsEntry.SettingsType.MaterialColor: 
                {
                    var materialColor = (CVRAdvancedAvatarSettingMaterialColor) entity.setting;
                    if (materialColor == null || materialColor.materialColorTargets == null)
                        return EditorGUIUtility.singleLineHeight * 8f;
                    float height = 8f;
                    foreach (var option in materialColor.materialColorTargets) 
                    {
                        if (!option.isCollapsed) 
                        {
                            height += 1.25f;
                        } 
                        else 
                        {
                            height += 3.75f;
                        }
                    }
                    if (materialColor.materialColorTargets.Count == 0) 
                    {
                        height += 1f;
                    }
                    return EditorGUIUtility.singleLineHeight * height;
                }
                case CVRAdvancedSettingsEntry.SettingsType.Slider: 
                {
                    var slider = (CVRAdvancesAvatarSettingSlider) entity.setting;
                    if (slider == null || slider.materialPropertyTargets == null)
                        return EditorGUIUtility.singleLineHeight * 8f;
                    float height = 11.25f;
                    foreach (var option in slider.materialPropertyTargets) 
                    {
                        if (!option.isCollapsed)
                        {
                            height += 1.25f;
                        } 
                        else 
                        {
                            height += 5 * 1.25f;
                        }
                    }
                    if (slider.materialPropertyTargets.Count == 0) 
                    {
                        height += 1.25f;
                    }
                    return EditorGUIUtility.singleLineHeight * height;
                }
                case CVRAdvancedSettingsEntry.SettingsType.Joystick2D:
                case CVRAdvancedSettingsEntry.SettingsType.Joystick3D:
                    return EditorGUIUtility.singleLineHeight * 11.25f;
                    break;
            }

            return EditorGUIUtility.singleLineHeight * 8.75f;
        }

        private void OnDrawHeader(Rect rect)
        {
            Rect _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);

            GUI.Label(_rect, "Inputs");
        }

        private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
        {
            if (index > _avatar.avatarSettings.settings.Count) return;
            entity = _avatar.avatarSettings.settings[index];
            rect.y += 2;
            rect.x += 12;
            rect.width -= 12;
            Rect _rect = new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight);

            entity.setting.isCollapsed = EditorGUI.Foldout(_rect, entity.setting.isCollapsed, "Name", true);
            _rect.x += 100;
            _rect.width = rect.width - 100;
            entity.name = EditorGUI.TextField(_rect, entity.name);

            // when collapsed skip rest of UI drawing
            if (!entity.setting.isCollapsed) return;

            if (entity.name != null)
            {
                entity.machineName = Regex.Replace(entity.name, "[^a-zA-Z0-9#]", "");
            }

            rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
            _rect = new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight);

            EditorGUI.LabelField(_rect, "Parameter");
            _rect.x += 100;
            _rect.width = rect.width - 100;

            switch (entity.type)
            {
                case CVRAdvancedSettingsEntry.SettingsType.GameObjectToggle:
                    EditorGUI.LabelField(_rect, entity.machineName);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.GameObjectDropdown:
                    EditorGUI.LabelField(_rect, entity.machineName);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.MaterialColor:
                    EditorGUI.LabelField(_rect, $"{entity.machineName}-r, {entity.machineName}-g, {entity.machineName}-b");
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.Slider:
                    EditorGUI.LabelField(_rect, entity.machineName);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.Joystick2D:
                    EditorGUI.LabelField(_rect, $"{entity.machineName}-x, {entity.machineName}-y");
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.Joystick3D:
                    EditorGUI.LabelField(_rect, $"{entity.machineName}-x, {entity.machineName}-y, {entity.machineName}-z");
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.InputSingle:
                    EditorGUI.LabelField(_rect, entity.machineName);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.InputVector2:
                    EditorGUI.LabelField(_rect, $"{entity.machineName}-x, {entity.machineName}-y");
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.InputVector3:
                    EditorGUI.LabelField(_rect, $"{entity.machineName}-x, {entity.machineName}-y, {entity.machineName}-z");
                    break;
            }

            rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
            _rect = new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight);

            EditorGUI.LabelField(_rect, "Type");
            _rect.x += 100;
            _rect.width = rect.width - 100;
            var type = (CVRAdvancedSettingsEntry.SettingsType) EditorGUI.EnumPopup(_rect, entity.type);

            if (type != entity.type)
            {
                entity.type = type;
                
                switch (type)
                {
                    case CVRAdvancedSettingsEntry.SettingsType.GameObjectToggle:
                        entity.setting = new CVRAdvancesAvatarSettingGameObjectToggle();
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.GameObjectDropdown:
                        entity.setting = new CVRAdvancesAvatarSettingGameObjectDropdown();
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.MaterialColor:
                        entity.setting = new CVRAdvancedAvatarSettingMaterialColor();
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.Slider:
                        entity.setting = new CVRAdvancesAvatarSettingSlider();
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.Joystick2D:
                        entity.setting = new CVRAdvancesAvatarSettingJoystick2D();
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.Joystick3D:
                        entity.setting = new CVRAdvancesAvatarSettingJoystick3D();
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.InputSingle:
                        entity.setting = new CVRAdvancesAvatarSettingInputSingle();
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.InputVector2:
                        entity.setting = new CVRAdvancesAvatarSettingInputVector2();
                        break;
                    case CVRAdvancedSettingsEntry.SettingsType.InputVector3:
                        entity.setting = new CVRAdvancesAvatarSettingInputVector3();
                        break;
                }
            }

            rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
            _rect = new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight);

            switch (entity.type)
            {
                case CVRAdvancedSettingsEntry.SettingsType.GameObjectToggle:
                    if (animatorParameters.Contains(entity.machineName))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }
                    animatorParameters.Add(entity.machineName);
                    
                    var gameObjectToggle = (CVRAdvancesAvatarSettingGameObjectToggle) entity.setting;
                    
                    // Default State
                    EditorGUI.LabelField(_rect, "Default");
                    _rect.x += 100;
                    _rect.width = rect.width - 100;
                    gameObjectToggle.defaultValue = EditorGUI.Toggle(_rect, gameObjectToggle.defaultValue);

                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight);

                    // Use Animation Clip
                    EditorGUI.LabelField(_rect, "Use Animation");
                    _rect.x += 100;
                    _rect.width = rect.width - 100;
                    gameObjectToggle.useAnimationClip = EditorGUI.Toggle(_rect, gameObjectToggle.useAnimationClip);

                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    if (gameObjectToggle.useAnimationClip)
                    {
                        _rect = new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight);

                        // Animation Clip Slot
                        EditorGUI.LabelField(_rect, "Clip");
                        _rect.x += 100;
                        _rect.width = rect.width - 100;
                        gameObjectToggle.animationClip = (AnimationClip)EditorGUI.ObjectField(_rect, gameObjectToggle.animationClip, typeof(AnimationClip), true);
                    }
                    else
                    {
                        var gameObjectList = gameObjectToggle.GetReorderableList(_avatar);
                        gameObjectList.DoList(new Rect(rect.x, rect.y, rect.width, 20f));
                    }
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.GameObjectDropdown:
                    if (animatorParameters.Contains(entity.machineName)) {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" was already defined.",
                                          MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    animatorParameters.Add(entity.machineName);
                    
                    var gameObjectDropdown = (CVRAdvancesAvatarSettingGameObjectDropdown) entity.setting;

                    EditorGUI.LabelField(_rect, "Default");
                    _rect.x += 100;
                    _rect.width = rect.width - 100;
                    gameObjectDropdown.defaultValue = EditorGUI.Popup(_rect, gameObjectDropdown.defaultValue, gameObjectDropdown.getOptionsList());
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    
                    var options = gameObjectDropdown.GetReorderableList(_avatar);
                    options.DoList(new Rect(rect.x, rect.y, rect.width, 20f));
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.MaterialColor:
                    if (animatorParameters.Contains(entity.machineName + "-r"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-r) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    if (animatorParameters.Contains(entity.machineName + "-g"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-g) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    if (animatorParameters.Contains(entity.machineName + "-b"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-b) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    animatorParameters.Add(entity.machineName + "-r");
                    animatorParameters.Add(entity.machineName + "-g");
                    animatorParameters.Add(entity.machineName + "-b");
                    
                    var materialColor = (CVRAdvancedAvatarSettingMaterialColor) entity.setting;
                    
                    EditorGUI.LabelField(_rect, "Default");
                    _rect.x += 100;
                    _rect.width = rect.width - 100;
                    materialColor.defaultValue = EditorGUI.ColorField(_rect, new GUIContent(), materialColor.defaultValue, true, false, false);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    
                    var materialColorList = materialColor.GetReorderableList(_avatar);
                    materialColorList.DoList(new Rect(rect.x, rect.y, rect.width, 20f));
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.Slider:
                    if (animatorParameters.Contains(entity.machineName))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    animatorParameters.Add(entity.machineName);
                    
                    var slider = (CVRAdvancesAvatarSettingSlider) entity.setting;

                    EditorGUI.LabelField(_rect, "Default");
                    _rect.x += 100;
                    _rect.width = rect.width - 100;
                    slider.defaultValue = EditorGUI.Slider(_rect, slider.defaultValue, 0f, 1f);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    
                    var materialPropertyList = slider.GetReorderableList(_avatar);
                    materialPropertyList.DoList(new Rect(rect.x, rect.y, rect.width, 20f));
                    
                    foreach (var target in slider.materialPropertyTargets)
                    {
                        rect.y += EditorGUIUtility.singleLineHeight * 1.25f * (!target.isCollapsed ? 1 : 5);
                    }
                    rect.y += EditorGUIUtility.singleLineHeight * (3f + (slider.materialPropertyTargets.Count == 0 ? 1.25f : 0));
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    
                    EditorGUI.HelpBox(_rect, "The Setup Utility will help you create a slider for Material properties "+
                                             "If you want to bind other properties you can edit the animation files generated "+
                                             "by the System after the animator was created.", MessageType.Info);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.Joystick2D:
                    if (animatorParameters.Contains(entity.machineName + "-x"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-x) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    if (animatorParameters.Contains(entity.machineName + "-y"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-y) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    animatorParameters.Add(entity.machineName + "-x");
                    animatorParameters.Add(entity.machineName + "-y");

                    var joystick = (CVRAdvancesAvatarSettingJoystick2D) entity.setting;

                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    joystick.defaultValue = EditorGUI.Vector2Field(_rect, "Default", joystick.defaultValue);

                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    joystick.rangeMin = EditorGUI.Vector2Field(_rect, "Range Min", joystick.rangeMin);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    joystick.rangeMax = EditorGUI.Vector2Field(_rect, "Range Max", joystick.rangeMax);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    
                    EditorGUI.HelpBox(_rect, "This Settings does not provide a Setup Utility. "+
                                             "But it will create the necessary Animator Layers, Parameters and Animations. "+
                                             "So you can edit them to your liking after the animator was created.", MessageType.Info);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.Joystick3D:
                    if (animatorParameters.Contains(entity.machineName + "-x"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-x) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    if (animatorParameters.Contains(entity.machineName + "-y"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-y) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    if (animatorParameters.Contains(entity.machineName + "-z"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-z) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    animatorParameters.Add(entity.machineName + "-x");
                    animatorParameters.Add(entity.machineName + "-y");
                    animatorParameters.Add(entity.machineName + "-z");

                    var joystick3D = (CVRAdvancesAvatarSettingJoystick3D) entity.setting;
                    
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    joystick3D.defaultValue = EditorGUI.Vector3Field(_rect, "Default", joystick3D.defaultValue);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    joystick3D.rangeMin = EditorGUI.Vector2Field(_rect, "Range Min", joystick3D.rangeMin);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    joystick3D.rangeMax = EditorGUI.Vector2Field(_rect, "Range Max", joystick3D.rangeMax);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    
                    EditorGUI.HelpBox(_rect, "This Settings does not provide a Setup Utility. "+
                                             "But it will create the necessary Animator Layers, Parameters and Animations. "+
                                             "So you can edit them to your liking after the animator was created.", MessageType.Info);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.InputSingle:
                    if (animatorParameters.Contains(entity.machineName))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    animatorParameters.Add(entity.machineName);
                    
                    var inputSingle = (CVRAdvancesAvatarSettingInputSingle) entity.setting;

                    EditorGUI.LabelField(_rect, "Default");
                    _rect.x += 100;
                    _rect.width = rect.width - 100;
                    inputSingle.defaultValue = EditorGUI.FloatField(_rect, inputSingle.defaultValue);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    
                    EditorGUI.HelpBox(_rect, "This Settings does not provide a Setup Utility. "+
                                             "But it will create the necessary Animator Layers, Parameters and Animations. "+
                                             "So you can edit them to your liking after the animator was created.", MessageType.Info);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.InputVector2:
                    if (animatorParameters.Contains(entity.machineName + "-x"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-x) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    if (animatorParameters.Contains(entity.machineName + "-y"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-y) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    animatorParameters.Add(entity.machineName + "-x");
                    animatorParameters.Add(entity.machineName + "-y");
                    
                    var inputVector2 = (CVRAdvancesAvatarSettingInputVector2) entity.setting;
                    
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    inputVector2.defaultValue = EditorGUI.Vector2Field(_rect, "Default", inputVector2.defaultValue);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    
                    EditorGUI.HelpBox(_rect, "This Settings does not provide a Setup Utility. "+
                                             "But it will create the necessary Animator Layers, Parameters and Animations. "+
                                             "So you can edit them to your liking after the animator was created.", MessageType.Info);
                    break;
                case CVRAdvancedSettingsEntry.SettingsType.InputVector3:
                    if (animatorParameters.Contains(entity.machineName + "-x"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-x) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    if (animatorParameters.Contains(entity.machineName + "-y"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-y) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    if (animatorParameters.Contains(entity.machineName + "-z"))
                    {
                        _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 2f);
                        EditorGUI.HelpBox(_rect, $"The Parameter \"{entity.machineName}\" ({entity.machineName}-z) was already defined.", MessageType.Error);
                        definitionContainsError = true;
                        return;
                    }

                    animatorParameters.Add(entity.machineName + "-x");
                    animatorParameters.Add(entity.machineName + "-y");
                    animatorParameters.Add(entity.machineName + "-z");
                    
                    var inputVector3 = (CVRAdvancesAvatarSettingInputVector3) entity.setting;
                    
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    inputVector3.defaultValue = EditorGUI.Vector3Field(_rect, "Default", inputVector3.defaultValue);
                    
                    rect.y += EditorGUIUtility.singleLineHeight * 1.25f;
                    _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight * 3);
                    
                    EditorGUI.HelpBox(_rect, "This Settings does not provide a Setup Utility. "+
                                             "But it will create the necessary Animator Layers, Parameters and Animations. "+
                                             "So you can edit them to your liking after the animator was created.", MessageType.Info);
                    break;
            }
        }
        
        private void OnAdd(ReorderableList list)
        {
            _avatar.avatarSettings.settings.Add(new CVRAdvancedSettingsEntry());
            Repaint();
        }
   
        private void OnChanged(ReorderableList list)
        {
            //EditorUtility.SetDirty(target);
        }

        private static void CreateAvatarSettings(CVRAvatar avatar)
        {
            string[] guids = AssetDatabase.FindAssets("AvatarAnimator t:animatorController", null);

            if (guids.Length < 1)
            {
                Debug.LogError("No Animator controller with the name \"AvatarAnimator\" was found. Please make sure that you CCK is installed properly.");
                return;
            }
            
            Type projectWindowUtilType = typeof(ProjectWindowUtil);
            MethodInfo getActiveFolderPath = projectWindowUtilType.GetMethod("GetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic);
            object obj = getActiveFolderPath.Invoke(null, new object[0]);
            string pathToCurrentFolder = obj.ToString();
            
            avatar.avatarSettings = new CVRAdvancedAvatarSettings();
            avatar.avatarSettings.baseController = AssetDatabase.LoadAssetAtPath<AnimatorController>(AssetDatabase.GUIDToAssetPath(guids[0]));
            avatar.avatarSettings.settings = new List<CVRAdvancedSettingsEntry>();
            avatar.avatarSettings.initialized = true;
        }

        protected virtual void OnSceneGUI()
        {
            if (_avatar == null) _avatar = (CVRAvatar) target;
            if (_avatar != null)
            {
                var avatarTransform = _avatar.transform;
                var scale = avatarTransform.localScale;
                var inverseScale = new Vector3(1 / scale.x, 1 / scale.y, 1 / scale.z);

                //View Position
                GUIStyle style = new GUIStyle();
                style.normal.textColor = Color.green;
                style.fontSize = 20;
                Handles.BeginGUI();
                Vector3 pos = avatarTransform.TransformPoint(Vector3.Scale(_avatar.viewPosition, inverseScale));
                Vector2 pos2D = HandleUtility.WorldToGUIPoint(pos);
                GUI.Label(new Rect(pos2D.x + 20, pos2D.y - 10, 100, 20), "View Position", style);
                Handles.EndGUI();

                EditorGUI.BeginChangeCheck();
                Vector3 viewPos = Handles.PositionHandle(pos, avatarTransform.rotation);
                if (EditorGUI.EndChangeCheck())
                {
                    Undo.RecordObject(_avatar, "CVR View Position Change");
                    _avatar.viewPosition = Vector3.Scale(avatarTransform.InverseTransformPoint(viewPos), scale);
                }

                //Voice Position
                style.normal.textColor = Color.red;
                Handles.BeginGUI();
                pos = avatarTransform.TransformPoint(Vector3.Scale(_avatar.voicePosition, inverseScale));
                pos2D = HandleUtility.WorldToGUIPoint(pos);
                GUI.Label(new Rect(pos2D.x + 20, pos2D.y - 10, 100, 20), "Voice Position", style);
                Handles.EndGUI();

                EditorGUI.BeginChangeCheck();
                Vector3 voicePos = Handles.PositionHandle(pos, avatarTransform.rotation);
                if (EditorGUI.EndChangeCheck())
                {
                    Undo.RecordObject(_avatar, "CVR Voice Position Change");
                    _avatar.voicePosition = Vector3.Scale(avatarTransform.InverseTransformPoint(voicePos), scale);
                }
            }
        }
        
        void GetBlendShapeNames()
        {
            if (_avatar.bodyMesh != null)
            {
                _blendShapeNames = new List<string>();
                _blendShapeNames.Add("-none-");
                for (int i = 0; i < _avatar.bodyMesh.sharedMesh.blendShapeCount; ++i)
                    _blendShapeNames.Add(_avatar.bodyMesh.sharedMesh.GetBlendShapeName(i));
            }
            else
            {
                _blendShapeNames = new List<string>();
                _blendShapeNames.Add("-none-");
            }
        }

        void FindVisemes()
        {
            for (int i = 0; i < _visemeNames.Length; i++)
            {
                for (int j = 0; j < _blendShapeNames.Count; ++j)
                {
                    if (_blendShapeNames[j].ToLower().Contains("v_" + _visemeNames[i].ToLower()))
                    {
                        _avatar.visemeBlendshapes[i] = _blendShapeNames[j];
                    }
                }
            }
        }
    }
}