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

    public class PointMoveModule : PointTransformModule
    {
        public bool snap = false;
        public float snapGridSize = 1f;
        public bool surfaceMode = false;
        public float surfaceOffset = 0f;
        public LayerMask surfaceLayerMask = ~0;

        public PointMoveModule(SplineEditor editor) : base(editor)
        {

        }

        public override GUIContent GetIconOff()
        {
            return EditorGUIUtility.IconContent("MoveTool");
        }

        public override GUIContent GetIconOn()
        {
            return EditorGUIUtility.IconContent("MoveTool On");
        }

        public override void LoadState()
        {
            base.LoadState();
            snap = LoadBool("snap");
            snapGridSize = LoadFloat("snapGridSize", 0.5f);
            surfaceOffset = LoadFloat("surfaceOffset", 0f);
            surfaceMode = LoadBool("surfaceMode");
            surfaceLayerMask = LoadInt("surfaceLayerMask", ~0);
        }

        public override void SaveState()
        {
            base.SaveState();
            SaveBool("snap", snap);
            SaveFloat("snapGridSize", snapGridSize);
            SaveFloat("surfaceOffset", surfaceOffset);
            SaveBool("surfaceMode", surfaceMode);
            SaveInt("surfaceLayerMask", surfaceLayerMask);
        }

        public override void BeforeSceneDraw(SceneView current)
        {
            base.BeforeSceneDraw(current);
            if (Event.current.type == EventType.MouseUp) GetRotation();
        }

        protected override void OnDrawInspector()
        {
            editSpace = (EditSpace)EditorGUILayout.EnumPopup("Edit Space", editSpace);
            surfaceMode = EditorGUILayout.Toggle("Move On Surface", surfaceMode);
            if (surfaceMode)
            {
                surfaceLayerMask = DreamteckEditorGUI.LayermaskField("Surface Mask", surfaceLayerMask);
                surfaceOffset = EditorGUILayout.FloatField("Surface Offset", surfaceOffset);
            }
            snap = EditorGUILayout.Toggle("Snap to Grid", snap);
            if (snap)
            {
                snapGridSize = EditorGUILayout.FloatField("Grid Size", snapGridSize);
                if (snapGridSize < 0.0001f) snapGridSize = 0.0001f;
            }
        }

        private Vector3 SurfaceMoveHandle(Vector3 inputPosition, float size = 0.2f)
        {
            Vector3 lastPosition = inputPosition;
            inputPosition = SplineEditorHandles.FreeMoveHandle(inputPosition, HandleUtility.GetHandleSize(inputPosition) * size, Vector3.zero, Handles.CircleHandleCap);
            if (lastPosition != inputPosition)
            {
                Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit, Mathf.Infinity, surfaceLayerMask))
                {
                    inputPosition = hit.point + hit.normal * surfaceOffset;
                    Handles.DrawLine(hit.point, hit.point + hit.normal * HandleUtility.GetHandleSize(hit.point) * 0.5f);
                }
            }
            return inputPosition;
        }

        protected override void OnDrawScene()
        {
            if (selectedPoints.Count == 0) return;
            Vector3 c = selectionCenter;
            Vector3 lastPos = c;
            if (surfaceMode)
            {
                c = SurfaceMoveHandle(c, 0.2f);
            }
            else
            {
                c = Handles.PositionHandle(c, rotation);
            }
            if (lastPos != c)
            {
                RegisterChange();
                for (int i = 0; i < selectedPoints.Count; i++)
                {
                    points[selectedPoints[i]].SetPosition(points[selectedPoints[i]].position + (c - lastPos));
                    if (snap) points[selectedPoints[i]].SetPosition(SnapPoint(points[selectedPoints[i]].position));
                }
            }

            if (splineType == Spline.Type.Bezier && selectedPoints.Count == 1)
            {
                int index = selectedPoints[0];
                lastPos = points[index].tangent;
                Vector3 newPos = Vector3.zero;
                if (surfaceMode)
                {
                    newPos = SurfaceMoveHandle(points[index].tangent, 0.15f);
                } else
                {
                    newPos = Handles.PositionHandle(points[index].tangent, rotation);
                }

                if (snap) newPos = SnapPoint(newPos);
                if (newPos != lastPos)
                {
                    RegisterChange();
                }
                points[index].SetTangentPosition(newPos);

                lastPos = points[index].tangent2;
                if (surfaceMode)
                {
                    newPos = SurfaceMoveHandle(points[index].tangent2, 0.15f);
                } else
                {
                    newPos = Handles.PositionHandle(points[index].tangent2, rotation);
                }
                    
                if (snap) newPos = SnapPoint(newPos);
                if (newPos != lastPos)
                {
                    RegisterChange();
                }
                points[index].SetTangent2Position(newPos);
            }
        }

        public Vector3 SnapPoint(Vector3 point)
        {
            point.x = Mathf.RoundToInt(point.x / snapGridSize) * snapGridSize;
            point.y = Mathf.RoundToInt(point.y / snapGridSize) * snapGridSize;
            point.z = Mathf.RoundToInt(point.z / snapGridSize) * snapGridSize;
            return point;
        }
    }
}