namespace Dreamteck.Splines.Editor
{
    using UnityEngine;
    using System.Collections;
    using UnityEditor;
    using System.Collections.Generic;

    [CustomEditor(typeof(ObjectBender), true)]
    [CanEditMultipleObjects]
    public class ObjectBenderEditor : SplineUserEditor
    {
        List<int> selected = new List<int>();
        Vector2 scroll = Vector2.zero;
        bool generatedUvs = false;

        protected override void Awake() 
        {
            ObjectBender bender = (ObjectBender)target;
            if(!Application.isPlaying) bender.UpdateReferences();
            base.Awake();
        }

        void PropertyEditor(ObjectBender.BendProperty[] properties)
        {
            if (selected.Count == 0) return;
            int applyRotationCount = 0, applyScaleCount = 0, enableCount = 0, bendMeshCount = 0, bendColliderCount = 0, bendSplineCount = 0;
            bool showMesh = false, showCollider = false, showSpline = false;
            float colliderUpdateRate = 0f;
            for(int i = 0; i < selected.Count; i++)
            {
                ObjectBender.BendProperty property = properties[selected[i]];
                if (property.enabled) enableCount++;
                if (property.applyRotation) applyRotationCount++;
                if (property.applyScale) applyScaleCount++;
                if (property.bendMesh) bendMeshCount++;
                if (property.bendCollider) bendColliderCount++;
                if (property.bendSpline) bendSplineCount++;
                if (property.filter != null) showMesh = true;
                if (property.collider != null) showCollider = true;
                if (property.splineComputer != null) showSpline = true;
                colliderUpdateRate += property.colliderUpdateRate;
            }
            bool enabled = enableCount == selected.Count;
            bool applyRotation = applyRotationCount == selected.Count;
            bool applyScale = applyScaleCount == selected.Count;
            bool bendMesh = bendMeshCount == selected.Count;
            bool bendCollider = bendColliderCount == selected.Count;
            bool bendSpline = bendSplineCount == selected.Count;
            colliderUpdateRate /= selected.Count;
            bool lastEnabled = enabled, lastApplyRotation = applyRotation, lastApplyScale = applyScale, lastBendMesh = bendMesh, lastBendCollider = bendCollider, lastBendSpline = bendSpline;
            float lastColliderUpdateRate = colliderUpdateRate;

            EditorGUIUtility.labelWidth = 90;
            EditorGUI.BeginChangeCheck();
            GUI.color = Color.white;
            EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(EditorGUIUtility.currentViewWidth - 50));
            EditorGUILayout.BeginHorizontal();
            enabled = EditorGUILayout.Toggle(enabled, GUILayout.Width(20));
            if (selected.Count == 1) EditorGUILayout.LabelField(properties[selected[0]].transform.transform.name);
            else EditorGUILayout.LabelField("Multiple objects");
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();

            EditorGUILayout.BeginVertical();
            applyRotation = EditorGUILayout.Toggle("Apply rotation", applyRotation);
            applyScale = EditorGUILayout.Toggle("Apply scale", applyScale);
            if (showSpline) bendSpline = EditorGUILayout.Toggle("Bend Spline", bendSpline);
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical();
            if (showMesh)
            {
                bendMesh = EditorGUILayout.Toggle("Bend Mesh", bendMesh);
                if (bendMesh)
                {
                    if (showCollider)
                    {
                        bendCollider = EditorGUILayout.Toggle("Bend Collider", bendCollider);
                        if (bendCollider)
                        {
                            EditorGUI.indentLevel++;
                            colliderUpdateRate = EditorGUILayout.FloatField("Update Rate", colliderUpdateRate);
                            EditorGUI.indentLevel--;
                        }
                    }
                    else GUI.Label(new Rect(EditorGUIUtility.currentViewWidth / 2f - 25, 40, EditorGUIUtility.currentViewWidth / 2f - 30, 22), "No Mesh Colliders Available");
                }
            }
            else EditorGUILayout.LabelField("No Meshes Available");
            EditorGUILayout.EndVertical();

            EditorGUILayout.EndHorizontal();

            EditorGUILayout.EndVertical();
            if (EditorGUI.EndChangeCheck())
            {
                for(int i = 0; i < selected.Count; i++)
                {
                    if (lastEnabled != enabled) properties[selected[i]].enabled = enabled;
                    if (lastApplyRotation != applyRotation) properties[selected[i]].applyRotation = applyRotation;
                    if (lastApplyScale != applyScale) properties[selected[i]].applyScale = applyScale;
                    if (bendMesh != lastBendMesh) properties[selected[i]].bendMesh = bendMesh;
                    if (bendCollider != lastBendCollider) properties[selected[i]].bendCollider = bendCollider;
                    if (bendSpline != lastBendSpline) properties[selected[i]].bendSpline = bendSpline;
                    if (lastColliderUpdateRate != colliderUpdateRate) properties[selected[i]].colliderUpdateRate = colliderUpdateRate;
                }
            }
        }

        void GetChildCount(Transform parent, ref int count)
        {
            foreach(Transform child in parent)
            {
                count++;
                GetChildCount(child, ref count);
            }
        }

        public override void OnInspectorGUI()
        {
            showAveraging = false;
            base.OnInspectorGUI();
        }

        protected override void BodyGUI()
        {
            base.BodyGUI();
            ObjectBender bender = (ObjectBender)target;

            serializedObject.Update();
            SerializedProperty axis = serializedObject.FindProperty("_axis");
            SerializedProperty normalMode = serializedObject.FindProperty("_normalMode");
            SerializedProperty forwardMode = serializedObject.FindProperty("_forwardMode");

            for (int i = 0; i < targets.Length; i++)
            {
                ObjectBender objBender = (ObjectBender)targets[i];
                int childCount = 0;
                GetChildCount(objBender.transform, ref childCount);
                if (objBender.bendProperties.Length - 1 != childCount && !Application.isPlaying) objBender.UpdateReferences();
            }
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(axis, new GUIContent("Axis"));
            EditorGUILayout.PropertyField(normalMode, new GUIContent("Up Vector"));

            if (normalMode.intValue == (int)ObjectBender.NormalMode.Custom)
            {
                SerializedProperty customNormal = serializedObject.FindProperty("_customNormal");
                EditorGUILayout.PropertyField(customNormal, new GUIContent("Custom Up"));
            }
            EditorGUILayout.PropertyField(forwardMode, new GUIContent("Forward Vector"));
            if (forwardMode.intValue == (int)ObjectBender.ForwardMode.Custom)
            {
                SerializedProperty customForward = serializedObject.FindProperty("_customForward");
                EditorGUILayout.PropertyField(customForward, new GUIContent("Custom Forward"));
            }

            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
                for (int i = 0; i < targets.Length; i++)
                {
                    ObjectBender objBender = (ObjectBender)targets[i];
                    objBender.Rebuild();
                }
            }

            if (targets.Length > 1)
            {
                EditorGUILayout.LabelField("Object properties unavailable when multiple benders are selected.", EditorStyles.centeredGreyMiniLabel);
                return;
            }
            if (!bender.bend)
            {
                float scrollHeight = Mathf.Min(bender.bendProperties.Length, 15) * 22;
                scroll = EditorGUILayout.BeginScrollView(scroll, GUILayout.Height(scrollHeight+5));

                for (int i = 0; i < bender.bendProperties.Length; i++)
                {
                    bool isSelected = selected.Contains(i);
                    if (!bender.bendProperties[i].enabled)
                    {
                        GUI.color = Color.gray;
                        if (isSelected) GUI.color = Color.Lerp(Color.gray, SplinePrefs.highlightColor, 0.5f);
                    }
                    else
                    {
                        if (isSelected) GUI.color = SplinePrefs.highlightColor;
                        else GUI.color = Color.white;
                    }
                    GUILayout.Box(bender.bendProperties[i].transform.transform.name, GUILayout.Height(18), GUILayout.Width(EditorGUIUtility.currentViewWidth - 60));
                    if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown)
                    {
                        if (Event.current.control)
                        {
                            if (!selected.Contains(i)) selected.Add(i);
                        }
                        else if (Event.current.shift && selected.Count > 0)
                        {
                            int from = selected[0];
                            selected.Clear();
                            if (from < i)
                            {
                                for (int n = from; n <= i; n++) selected.Add(n);
                            }
                            else
                            {
                                for (int n = from; n >= i; n--) selected.Add(n);
                            }
                        }
                        else
                        {
                            selected.Clear();
                            selected.Add(i);
                        }
                        Repaint();
                        SceneView.RepaintAll();
                    }
                    GUI.color = Color.white;
                }
                EditorGUILayout.EndScrollView();

                if (selected.Count > 0)
                {
                    PropertyEditor(bender.bendProperties);
                }

                if (selected.Count > 0)
                {
                    if (Event.current.type == EventType.KeyDown)
                    {
                        if (Event.current.keyCode == KeyCode.DownArrow)
                        {
                            if (selected.Count > 1)
                            {
                                int temp = selected[selected.Count - 1];
                                selected.Clear();
                                selected.Add(temp);
                            }
                            selected[0]++;
                            if (selected[0] >= bender.bendProperties.Length) selected[0] = 0;
                        }
                        if (Event.current.keyCode == KeyCode.UpArrow)
                        {
                            if (selected.Count > 1)
                            {
                                int temp = selected[0];
                                selected.Clear();
                                selected.Add(temp);
                            }
                            selected[0]--;
                            if (selected[0] < 0) selected[0] = bender.bendProperties.Length - 1;
                        }

                        Repaint();
                        SceneView.RepaintAll();
                        Event.current.Use();
                    }
                }

            }
            string editModeText = "Enter Edit Mode";
            if (!bender.bend) editModeText = "Bend";
            if (GUILayout.Button(editModeText))
            {
                if (bender.bend) bender.bend = false;
                else bender.bend = true;
            }
            if (bender.bend && !generatedUvs)
            {
                if (GUILayout.Button("Generate Lightmap UVS"))
                {
                    bender.EditorGenerateLightmapUVs();
                    generatedUvs = true;
                }
            }
        }

        protected override void DuringSceneGUI(SceneView currentSceneView)
        {
            base.DuringSceneGUI(currentSceneView);
            ObjectBender bender = (ObjectBender)target;
            if (selected.Count > 0)
            {
                Handles.BeginGUI();
                for(int i = 0; i < selected.Count; i++)
                {
                    Vector2 screenPosition = HandleUtility.WorldToGUIPoint(bender.bendProperties[selected[i]].transform.transform.position);
                    DreamteckEditorGUI.Label(new Rect(screenPosition.x - 120 + bender.bendProperties[selected[i]].transform.transform.name.Length * 4, screenPosition.y, 120, 25), bender.bendProperties[selected[i]].transform.transform.name);
                }
                Handles.EndGUI();
            }
            for(int i = 0; i < bender.bendProperties.Length; i++)
            {
                if(bender.bendProperties[i].bendSpline && bender.bendProperties[i].splineComputer != null)
                {
                    DSSplineDrawer.DrawSplineComputer(bender.bendProperties[i].splineComputer, 0.0, 1.0, 0.2f);
                }
            }

            //Draw bounds
            if (bender.bend) return;
            TS_Bounds bound = bender.GetBounds();
            Vector3 a = bender.transform.TransformPoint(bound.min);
            Vector3 b = bender.transform.TransformPoint(new Vector3(bound.max.x, bound.min.y, bound.min.z));
            Vector3 c = bender.transform.TransformPoint(new Vector3(bound.max.x, bound.min.y, bound.max.z));
            Vector3 d = bender.transform.TransformPoint(new Vector3(bound.min.x, bound.min.y, bound.max.z));

            Vector3 e = bender.transform.TransformPoint(new Vector3(bound.min.x, bound.max.y, bound.min.z));
            Vector3 f = bender.transform.TransformPoint(new Vector3(bound.max.x, bound.max.y, bound.min.z));
            Vector3 g = bender.transform.TransformPoint(new Vector3(bound.max.x, bound.max.y, bound.max.z));
            Vector3 h = bender.transform.TransformPoint(new Vector3(bound.min.x, bound.max.y, bound.max.z));

            Handles.color = Color.gray;
            Handles.DrawLine(a, b);
            Handles.DrawLine(b, c);
            Handles.DrawLine(c, d);
            Handles.DrawLine(d, a);

            Handles.DrawLine(e, f);
            Handles.DrawLine(f, g);
            Handles.DrawLine(g, h);
            Handles.DrawLine(h, e);

            Handles.DrawLine(a, e);
            Handles.DrawLine(b, f);
            Handles.DrawLine(c, g);
            Handles.DrawLine(d, h);

            Vector3 r = bender.transform.right;
            Vector3 fr = bender.transform.forward;

            switch (bender.axis)
            {
                case ObjectBender.Axis.Z: Handles.color = Color.blue; Handles.DrawLine(r + b, r + c);  break;
                case ObjectBender.Axis.X: Handles.color = Color.red; Handles.DrawLine(b - fr, a - fr); break;
                case ObjectBender.Axis.Y: Handles.color = Color.green; Handles.DrawLine(b- fr + r, f - fr + r); break;
            }
        }

        protected override void OnDestroy()
        {
            SplineUser user = (SplineUser)target;
            if (Application.isEditor && !Application.isPlaying)
            {
                if (user == null) OnDelete(); //The object or the component is being deleted
                else if (user.spline != null)
                {
                    if(!generatedUvs)  user.Rebuild();
                }
            }
            SplineComputerEditor.hold = false;
        }
    }
}