using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;

namespace WaterSystem.Data
{
    [CustomEditor(typeof(WaterSurfaceData))]
    public class WaterSurfaceDataEditor : Editor
    {
        [SerializeField]
        ReorderableList waveList;

        private void OnValidate()
        {
            var init = serializedObject.FindProperty("_init");
            if(init?.boolValue == false)
                Setup();

            var standardHeight = EditorGUIUtility.singleLineHeight;
            var standardLine = standardHeight + EditorGUIUtility.standardVerticalSpacing;
            //Reorderable list stuff////////////////////////////////////////////////
            waveList = new ReorderableList(serializedObject, serializedObject.FindProperty("_waves"), true, true, true, true);

			//Single entry GUI
            waveList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
			{
                var element = waveList.serializedProperty.GetArrayElementAtIndex(index);
                rect.y += 2;

                // Swell height
                var preWidth = EditorGUIUtility.labelWidth;
                EditorGUIUtility.labelWidth = rect.width * 0.2f;
                Rect ampRect = new Rect(rect.x, rect.y + standardLine, rect.width * 0.5f, standardHeight);
                var waveAmp = element.FindPropertyRelative("amplitude");
                waveAmp.floatValue = EditorGUI.Slider(ampRect, "Swell Height", waveAmp.floatValue, 0.1f, 30f);
                // Wavelength
                Rect lengthRect = new Rect(rect.x + ampRect.width, rect.y + standardLine, rect.width * 0.5f, standardHeight);
                var waveLen = element.FindPropertyRelative("wavelength");
                waveLen.floatValue = EditorGUI.Slider(lengthRect, "Wavelength", waveLen.floatValue, 1.0f, 200f);
                EditorGUIUtility.labelWidth = preWidth;
                // Directional controls
                Rect dirToggleRect = new Rect(rect.x, rect.y + 2 + standardLine * 2, rect.width * 0.5f, standardHeight);
                Rect omniToggleRect = new Rect(rect.x + rect.width * 0.5f, dirToggleRect.y, rect.width * 0.5f, standardHeight);
                Rect containerRect = new Rect(rect.x, dirToggleRect.y + 1, rect.width, standardLine * 3.2f);
                // Direction/origin
                var waveType = element.FindPropertyRelative("onmiDir");
                var wTypeBool = (int)waveType.floatValue == 1 ? true : false;
                GUI.Box(containerRect, "", EditorStyles.helpBox );
                wTypeBool = !GUI.Toggle(dirToggleRect, !wTypeBool, "Directional", EditorStyles.miniButtonLeft);
                wTypeBool = GUI.Toggle(omniToggleRect, wTypeBool, "Omni-directional", EditorStyles.miniButtonRight);
                waveType.floatValue = wTypeBool ? 1 : 0;

                Rect dirRect = new Rect(rect.x + 4, dirToggleRect.y + standardLine, rect.width - 8, standardHeight);
                Rect buttonRect = new Rect(rect.x + 4, dirRect.y + standardLine + 2, rect.width - 8, standardHeight);
                // Directional
                if(!wTypeBool)
                {
                    var waveDir = element.FindPropertyRelative("direction");
                    waveDir.floatValue = EditorGUI.Slider(dirRect, "Facing Direction", waveDir.floatValue, -180.0f, 180.0f);
                    if(GUI.Button(buttonRect, "Align with Scene Camera"))
                        waveDir.floatValue = CameraRelativeDirection();
                }
                else
                {// Omni-Directional
                    EditorGUIUtility.wideMode = true;
                    //var perWidth = EditorGUIUtility.labelWidth;
                    //EditorGUIUtility.labelWidth = 20f;
                    var waveOrig = element.FindPropertyRelative("origin");
                    waveOrig.vector2Value = EditorGUI.Vector2Field(dirRect, "Point of Origin", waveOrig.vector2Value);
                    if(GUI.Button(buttonRect, "Project Origin from Scene Camera"))
                        waveOrig.vector2Value = CameraRelativeOrigin(waveOrig.vector2Value);
                    //EditorGUIUtility.labelWidth = perWidth;
                }
            };

            // Check can remove to make sure at least one wave remains
            waveList.onCanRemoveCallback = (ReorderableList l) =>
            {
                return l.count > 1;
            };

            // Check on remove to give a warning incase removing by accident
            waveList.onRemoveCallback = (ReorderableList l) =>
            {
                if (EditorUtility.DisplayDialog("Warning!", "Are you sure you want to delete the wave?", "Yes", "No"))
                {
                    ReorderableList.defaultBehaviours.DoRemoveButton(l);
                }
            };
            // When adding, check if under 10, if so add a new random wave
            waveList.onAddCallback = (ReorderableList l) =>
            {
                var index = l.serializedProperty.arraySize;
                if (index < 10)
                {
                    l.serializedProperty.arraySize++;
                    l.index = index;
                    var element = l.serializedProperty.GetArrayElementAtIndex(index);
                    element.FindPropertyRelative("amplitude").floatValue = Random.Range(0.25f, 1.5f);
                    element.FindPropertyRelative("direction").floatValue = Random.Range(-180f, 180f);
                    element.FindPropertyRelative("wavelength").floatValue = Random.Range(2f, 8f);
                }
                else
                {
                    EditorUtility.DisplayDialog("Warning!", "You have reached the limit of 10 waves for this Water.", "Close");
                }
            };
            //Draw header
            waveList.drawHeaderCallback = (Rect rect) =>
            {
                EditorGUI.LabelField(rect, "Wave List");
            };
            //Do height of list entry
            waveList.elementHeightCallback = (index) =>
            {
                var elementHeight = standardLine * 6f;
                return elementHeight;
            };
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();
            
            EditorGUILayout.LabelField("Visual Settings", EditorStyles.boldLabel);
            EditorGUI.indentLevel += 1;
            // Max visibility - slider 3-300
            var maxDepth = serializedObject.FindProperty("_waterMaxVisibility");
            EditorGUILayout.Slider(maxDepth, 3, 300, new GUIContent("Maximum Visibility", maxDepthTT));
            // Colouring settings
            DoSmallHeader("Coloring Controls");
            // Absorbstion Ramp
            var absorpRamp = serializedObject.FindProperty("_absorptionRamp");
            EditorGUILayout.PropertyField(absorpRamp, new GUIContent("Absorption Color", absorpRampTT), true, null);
            // Scatter Ramp
            var scatterRamp = serializedObject.FindProperty("_scatterRamp");
            EditorGUILayout.PropertyField(scatterRamp, new GUIContent("Scattering Color", scatterRampTT), true, null);
            // Foam Ramps
            DoSmallHeader("Surface Foam");
            var foamSettings = serializedObject.FindProperty("_foamSettings");
            var foamType = foamSettings.FindPropertyRelative("foamType");
            foamType.intValue = GUILayout.Toolbar(foamType.intValue, foamTypeOptions);

            EditorGUILayout.Space();

            switch (foamType.intValue)
            {
			case 0: //// Auto ////
				{
					EditorGUILayout.HelpBox("Automatic will distribute the foam suitable for an average swell", MessageType.Info);
				}
				break;
			case 1: //// Simple ////
				{
					EditorGUILayout.BeginHorizontal();
					DoInlineLabel("Foam Profile", foamCurveTT, 50f);
					var basicFoam = foamSettings.FindPropertyRelative("basicFoam");
					basicFoam.animationCurveValue = EditorGUILayout.CurveField(basicFoam.animationCurveValue, Color.white, new Rect(Vector2.zero, Vector2.one));
					EditorGUILayout.EndHorizontal();
				}
				break;
			case 2: //// Simple ////
				{
					EditorGUILayout.BeginHorizontal();
					DoInlineLabel("Foam Profiles", foam3curvesTT, 50f);
					var liteFoam = foamSettings.FindPropertyRelative("liteFoam");
					liteFoam.animationCurveValue = EditorGUILayout.CurveField(liteFoam.animationCurveValue, new Color(0.5f, 0.75f, 1f, 1f), new Rect(Vector2.zero, Vector2.one));
					var mediumFoam = foamSettings.FindPropertyRelative("mediumFoam");
					mediumFoam.animationCurveValue = EditorGUILayout.CurveField(mediumFoam.animationCurveValue, new Color(0f, 0.5f, 1f, 1f), new Rect(Vector2.zero, Vector2.one));
					var denseFoam = foamSettings.FindPropertyRelative("denseFoam");
					denseFoam.animationCurveValue = EditorGUILayout.CurveField(denseFoam.animationCurveValue, Color.blue, new Rect(Vector2.zero, Vector2.one));
					EditorGUILayout.EndHorizontal();
				}
				break;
            }

            EditorGUI.indentLevel -= 1;
            EditorGUILayout.LabelField("Wave Settings", EditorStyles.boldLabel);
            EditorGUI.indentLevel += 1;
            // Wave type - Automatic / Customized
            //wavesType = EditorGUILayout.Popup("System Type", wavesType, wavesTypeOptions);
            // Toolbar labels here
            
            var customWaves = serializedObject.FindProperty("_customWaves");
            var intVal = customWaves.boolValue ? 1 : 0;
            intVal = GUILayout.Toolbar(intVal, wavesTypeOptions);
            customWaves.boolValue = intVal == 1 ? true : false;

            EditorGUILayout.Space();

            switch(customWaves.boolValue ? 1 : 0)
            {
            case 0: //// Automatic ////
            {
                var basicSettings = serializedObject.FindProperty("_basicWaveSettings");
                // Wave count (display warning of on mobile platform and over 6) dropdown  1 > 10
                var autoCount = basicSettings.FindPropertyRelative("numWaves");
                EditorGUILayout.IntSlider(autoCount, 1, 10, new GUIContent("Wave Count", waveCountTT), null);
                // Average Wave height - slider 0.05 - 30
                var avgHeight = basicSettings.FindPropertyRelative("amplitude");
                EditorGUILayout.Slider(avgHeight, 0.1f, 30.0f, new GUIContent("Avg Swell Height", avgHeightTT), null);
                // Average Wavelength - slider 1 - 200
                var avgWavelength = basicSettings.FindPropertyRelative("wavelength");
                EditorGUILayout.Slider(avgWavelength, 1.0f, 200.0f, new GUIContent("Avg Wavelength", avgWavelengthTT), null);
                // Wind direction - slider -180-180
                EditorGUILayout.BeginHorizontal();
                var windDir = basicSettings.FindPropertyRelative("direction");
                EditorGUILayout.Slider(windDir, -180.0f, 180.0f, new GUIContent("Wind Direction", windDirTT), null);
                if(GUILayout.Button(new GUIContent("Align to scene camera", alignButtonTT)))
                    windDir.floatValue = CameraRelativeDirection();
                EditorGUILayout.EndHorizontal();
                        // [override] - random otherwise(on creation/override check)
                        // Random seed - int input
                        EditorGUILayout.BeginHorizontal();
				var randSeed = serializedObject.FindProperty("randomSeed");
                        randSeed.intValue = EditorGUILayout.IntField(new GUIContent("Random Seed", randSeedTT), randSeed.intValue);
                        if (GUILayout.Button("Randomize Waves"))
				{
					randSeed.intValue = System.DateTime.Now.Millisecond * 100 - System.DateTime.Now.Millisecond;
				}
                        EditorGUILayout.EndHorizontal();
            }
            break;
            case 1: //// Customized ////
            {
                        EditorGUI.indentLevel -= 1;
                        // Re-orderable list with wave details
                        waveList.DoLayoutList();
                /// Type - Directional / Omi-directional
                //// Amplitude - slider 0.05 - 30
                //// Wavelength - slider 1 - 200
                ////// Direction(facing) - slider -180-180 && face scene camera direction(if scene view)
                // OR
                ////// Origin(point of origin) - Vector input && origin at camera aim
            }
            break;
            }

            EditorUtility.SetDirty(this);
            serializedObject.ApplyModifiedProperties();
        }

		void DoSmallHeader(string header)
		{
            EditorGUI.indentLevel -= 1;
            EditorGUILayout.LabelField(header, EditorStyles.miniBoldLabel);
            EditorGUI.indentLevel += 1;
		}

        void DoInlineLabel(string label, string tooltip, float width)
		{
            var preWidth = EditorGUIUtility.labelWidth;
            EditorGUIUtility.labelWidth = width;
            EditorGUILayout.LabelField(new GUIContent(label, tooltip));
            EditorGUIUtility.labelWidth = preWidth;
		}

        void Setup()
		{
            WaterSurfaceData wsd = (WaterSurfaceData)target;
            wsd._init = true;
            wsd._absorptionRamp = DefaultAbsorptionGrad();
            wsd._scatterRamp = DefaultScatterGrad();
            EditorUtility.SetDirty(wsd);
        }

        Gradient DefaultAbsorptionGrad() // Preset for absorption
        {
            Gradient g = new Gradient();
            GradientColorKey[] gck = new GradientColorKey[5];
            GradientAlphaKey[] gak = new GradientAlphaKey[1];
            gak[0].alpha = 1;
            gak[0].time = 0;
            gck[0].color = Color.white;
            gck[0].time = 0f;
            gck[1].color = new Color(0.22f, 0.87f, 0.87f);
            gck[1].time = 0.082f;
            gck[2].color = new Color(0f, 0.47f, 0.49f);
            gck[2].time = 0.318f;
            gck[3].color = new Color(0f, 0.275f, 0.44f);
            gck[3].time = 0.665f;
            gck[4].color = Color.black;
            gck[4].time = 1f;
            g.SetKeys(gck, gak);
            return g;
        }

        Gradient DefaultScatterGrad() // Preset for scattering
        {
            Gradient g = new Gradient();
            GradientColorKey[] gck = new GradientColorKey[4];
            GradientAlphaKey[] gak = new GradientAlphaKey[1];
            gak[0].alpha = 1;
            gak[0].time = 0;
            gck[0].color = Color.black;
            gck[0].time = 0f;
            gck[1].color = new Color(0.08f, 0.41f, 0.34f);
            gck[1].time = 0.15f;
            gck[2].color = new Color(0.13f, 0.55f, 0.45f);
            gck[2].time = 0.42f;
            gck[3].color = new Color(0.21f, 0.62f, 0.6f);
            gck[3].time = 1f;
            g.SetKeys(gck, gak);
            return g;
        }

        float CameraRelativeDirection()
        {
            float degrees = 0;

            Vector3 camFwd = UnityEditor.SceneView.lastActiveSceneView.camera.transform.forward;
            camFwd.y = 0f;
            camFwd.Normalize();
            float dot = Vector3.Dot(-Vector3.forward, camFwd);
            degrees = Mathf.LerpUnclamped(90.0f, 180.0f, dot);
            if(camFwd.x < 0)
                degrees *= -1f;

            return Mathf.RoundToInt(degrees * 1000) / 1000;
        }

        Vector2 CameraRelativeOrigin(Vector2 original)
        {
            Camera sceneCam = UnityEditor.SceneView.lastActiveSceneView.camera;

            float angle = 90f - Vector3.Angle(sceneCam.transform.forward, Vector3.down);
            if (angle > 0.1f)
            {
                Vector3 intersect = Vector2.zero;
                float hypot = (sceneCam.transform.position.y) * (1 / Mathf.Sin(Mathf.Deg2Rad * angle));
                Vector3 fwd = sceneCam.transform.forward * hypot;
                intersect = fwd + sceneCam.transform.position;
                return new Vector2(intersect.x, intersect.z);
            }
            else
            {
                return original;
            }
        }

        static string[] wavesTypeOptions = new string[] { "Automatic", "Customized" };

        static string[] foamTypeOptions = new string[3] { "Automatic", "Simple Curve", "Density Curves" };


        ////TOOLTIPS////
        private string maxDepthTT = "This controls the max depth of the waters transparency/visiblility, the absorption and scattering gradients map to this depth. Units:Meters";
        private string absorpRampTT = "This gradient controls the color of the water as it gets deeper, darkening the surfaces under the water as they get deeper.";
        private string scatterRampTT = "This gradient controls the 'scattering' of the water from shallow to deep, lighting the water as there becomes more of it.";
        private string waveCountTT = "Number of waves the automatic setup creates, if aiming for mobile set to 6 or less";
        private string avgHeightTT = "The average height of the waves. Units:Meters";
        private string avgWavelengthTT = "The average wavelength of the waves. Units:Meters";
        private string windDirTT = "The general wind direction, this is in degrees from Z+";
        private string alignButtonTT = "This aligns the wave direction to the current scene view camera facing direction";
        private string foamCurveTT = "This curve control the foam propagation. X is wave height and Y is foam opacity";
        private string foam3curvesTT = "These three curves control the Lite, Medium and Dense foam propagation. X is wave height and Y is foam opacity";
        private string randSeedTT = "This seed controls the automatic wave generation";
    }
}