﻿using UnityEngine;
using UnityEditor;
using ExpressionParameters = VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionParameters;
using ExpressionParameter = VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionParameters.Parameter;
using UnityEngine.UI;

[CustomEditor(typeof(VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionParameters))]
public class VRCExpressionParametersEditor : Editor
{
	int selected = -1;
	GUIStyle boxNormal;
	GUIStyle boxSelected;

	void InitStyles()
	{
		//Normal
		if(boxNormal == null)
			boxNormal = new GUIStyle(GUI.skin.box);

		//Selected
		if(boxSelected == null)
		{
			boxSelected = new GUIStyle(GUI.skin.box);
			boxSelected.normal.background = MakeStyleBackground(new Color(0.0f, 0.5f, 1f, 0.5f));
		}
	}
	Texture2D MakeStyleBackground(Color color)
	{
		var texture = new Texture2D(1, 1);
		texture.SetPixel(0, 0, color);
		texture.Apply();
		return texture;
	}

	void SelectParam(int value)
	{
		selected = value;
		Repaint();
	}
	public void OnEnable()
	{
		//Init parameters
		var expressionParameters = target as ExpressionParameters;
		if (expressionParameters.parameters == null)
			InitExpressionParameters(true);

		SelectParam(-1);
	}
	public override void OnInspectorGUI()
	{
		InitStyles();

		serializedObject.Update();
		{
			EditorGUILayout.LabelField("Parameters");
			var parameters = serializedObject.FindProperty("parameters");

			//Controls
			EditorGUILayout.BeginHorizontal();
			{
				//Add
				if (GUILayout.Button("Add"))
					parameters.arraySize = parameters.arraySize + 1;

				EditorGUI.BeginDisabledGroup(selected < 0);
				{
					//Move Up
					if (GUILayout.Button("Up"))
					{
						if(selected > 0)
						{
							SwapParams(selected, selected - 1);
							selected = selected - 1;
							Repaint();
						}
					}

					//Move Down
					if (GUILayout.Button("Down"))
					{
						if (selected < parameters.arraySize-1)
						{
							SwapParams(selected, selected + 1);
							selected = selected + 1;
							Repaint();
						}
					}

					void SwapParams(int indexA, int indexB)
					{
						var script = (ExpressionParameters)target;
						var itemA = script.parameters[indexA];
						var itemB = script.parameters[indexB];
						script.parameters[indexA] = itemB;
						script.parameters[indexB] = itemA;

						serializedObject.Update();
					}

					//Delete
					if (GUILayout.Button("Delete"))
					{
						parameters.DeleteArrayElementAtIndex(selected);
						SelectParam(-1);
					}
				}
				EditorGUI.EndDisabledGroup();
			}
			EditorGUILayout.EndHorizontal();
			

			//Labels
			EditorGUILayout.BeginHorizontal();
			{
				EditorGUILayout.LabelField("    Name", GUILayout.MinWidth(100));
				EditorGUILayout.LabelField("    Type", GUILayout.Width(100));
				EditorGUILayout.LabelField("Default", GUILayout.Width(64));
				EditorGUILayout.LabelField("Saved", GUILayout.Width(64));
			}
			EditorGUILayout.EndHorizontal();

			//Parameters
			int count = parameters.arraySize;
			for(int paramIter=0; paramIter< parameters.arraySize; paramIter++)
			{
				DrawExpressionParameter(parameters, paramIter);
					
				/*var item = parameters.GetArrayElementAtIndex(paramIter);
				var name = item.FindPropertyRelative("name");
				var valueType = item.FindPropertyRelative("valueType");

				//Draw
				EditorGUI.indentLevel += 1;
				EditorGUILayout.BeginHorizontal();
				{
					EditorGUILayout.PropertyField(name, new GUIContent(""));
					EditorGUILayout.PropertyField(valueType, new GUIContent(""));
					if(GUILayout.Button("X", GUILayout.Width(32)))
					{
						parameters.DeleteArrayElementAtIndex(paramIter);
						paramIter -= 1;
					}
				}
				EditorGUILayout.EndHorizontal();
				EditorGUI.indentLevel -= 1;*/
			}

			//Cost
			int cost = (target as ExpressionParameters).CalcTotalCost();
			if(cost <= ExpressionParameters.MAX_PARAMETER_COST)
				EditorGUILayout.HelpBox($"Total Memory: {cost}/{ExpressionParameters.MAX_PARAMETER_COST}", MessageType.Info);
			else
				EditorGUILayout.HelpBox($"Total Memory: {cost}/{ExpressionParameters.MAX_PARAMETER_COST}\nParameters use too much memory.  Remove parameters or use bools which use less memory.", MessageType.Error);

			//Info
			EditorGUILayout.HelpBox("Only parameters defined here can be used by expression menus, sync between all playable layers and sync across the network to remote clients.", MessageType.Info);
			EditorGUILayout.HelpBox("The parameter name and type should match a parameter defined on one or more of your animation controllers.", MessageType.Info);
			EditorGUILayout.HelpBox("Parameters used by the default animation controllers (Optional)\nVRCEmote, Int\nVRCFaceBlendH, Float\nVRCFaceBlendV, Float", MessageType.Info);

			//Clear
			if (GUILayout.Button("Clear Parameters"))
			{
				if (EditorUtility.DisplayDialogComplex("Warning", "Are you sure you want to clear all expression parameters?", "Clear", "Cancel", "") == 0)
				{
					InitExpressionParameters(false);
				}
			}
			if (GUILayout.Button("Default Parameters"))
			{
				if (EditorUtility.DisplayDialogComplex("Warning", "Are you sure you want to reset all expression parameters to default?", "Reset", "Cancel", "") == 0)
				{
					InitExpressionParameters(true);
				}
			}
		}
		serializedObject.ApplyModifiedProperties();
	}
	void DrawExpressionParameter(SerializedProperty parameters, int index)
	{
		if (parameters.arraySize < index + 1)
			parameters.InsertArrayElementAtIndex(index);
		var item = parameters.GetArrayElementAtIndex(index);

		var name = item.FindPropertyRelative("name");
		var valueType = item.FindPropertyRelative("valueType");
		var defaultValue = item.FindPropertyRelative("defaultValue");
		var saved = item.FindPropertyRelative("saved");

		bool isSelected = selected == index;

		EditorGUI.indentLevel += 1;
		var rect = EditorGUILayout.BeginHorizontal(isSelected ? boxSelected : boxNormal);
		{
			EditorGUILayout.PropertyField(name, new GUIContent(""), GUILayout.MinWidth(100));
			EditorGUILayout.PropertyField(valueType, new GUIContent(""), GUILayout.Width(100));
			var type = (ExpressionParameters.ValueType)valueType.intValue;
			switch(type)
			{
				case ExpressionParameters.ValueType.Int:
					defaultValue.floatValue = Mathf.Clamp(EditorGUILayout.IntField((int)defaultValue.floatValue, GUILayout.Width(64)), 0, 255);
					break;
				case ExpressionParameters.ValueType.Float:
					defaultValue.floatValue = Mathf.Clamp(EditorGUILayout.FloatField(defaultValue.floatValue, GUILayout.Width(64)), -1f, 1f);
					break;
				case ExpressionParameters.ValueType.Bool:
					defaultValue.floatValue = EditorGUILayout.Toggle(defaultValue.floatValue != 0 ? true : false, GUILayout.Width(64)) ? 1f : 0f;
					break;
			}
			EditorGUILayout.PropertyField(saved, new GUIContent(""), GUILayout.Width(64));
		}
		EditorGUILayout.EndHorizontal();
		EditorGUI.indentLevel -= 1;

		//Select
		if(Event.current.type == EventType.MouseDown)
		{
			if(rect.Contains(Event.current.mousePosition))
			{
				SelectParam(index);
				Event.current.Use();
			}
		}
	}
	void InitExpressionParameters(bool populateWithDefault)
	{
		var expressionParameters = target as ExpressionParameters;
		serializedObject.Update();
		{
			if (populateWithDefault)
			{
				expressionParameters.parameters = new ExpressionParameter[3];

				expressionParameters.parameters[0] = new ExpressionParameter();
				expressionParameters.parameters[0].name = "VRCEmote";
				expressionParameters.parameters[0].valueType = ExpressionParameters.ValueType.Int;

				expressionParameters.parameters[1] = new ExpressionParameter();
				expressionParameters.parameters[1].name = "VRCFaceBlendH";
				expressionParameters.parameters[1].valueType = ExpressionParameters.ValueType.Float;

				expressionParameters.parameters[2] = new ExpressionParameter();
				expressionParameters.parameters[2].name = "VRCFaceBlendV";
				expressionParameters.parameters[2].valueType = ExpressionParameters.ValueType.Float;
			}
			else
			{
				//Empty
				expressionParameters.parameters = new ExpressionParameter[0];
			}
		}
		serializedObject.ApplyModifiedProperties();
	}
}