//Created by Neodrop
//neodrop@unity3d.ru
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;
//#pragma warning disable

[CustomEditor(typeof(AntaresInterpolator))]
public class InterpolatorEditor : Editor 
{
    private AntaresInterpolator _targetObject;
    private SerializedObject _serializableObject;
    private GUIContent[] _fieldContent;
    private List<GUIContent> _fieldContentList = new List<GUIContent>();
    private int _selectedField = 0;

	void OnEnable()
	{
	    _targetObject = target as AntaresInterpolator;
	    _serializableObject = new SerializedObject(_targetObject);
// ReSharper disable PossibleNullReferenceException
        if (!string.IsNullOrEmpty(_targetObject.fieldName))
// ReSharper restore PossibleNullReferenceException
        {
            bool contain = false;
            foreach (GUIContent c in _fieldContentList)
            {
                if (c.text == _targetObject.fieldName)
                {
                    RebuildContent();
                    contain = true;
                    break;
                }
            }
            if (!contain)
            {
                _fieldContentList.Clear();
                _fieldContentList.Add(new GUIContent(_targetObject.fieldName, ""));
                RebuildContent();
            }
        }
	}

    [MenuItem("Antares/GameObject/Add Interpolator &i")]
    static void MenuAddInterpolator()
    {
        Selection.activeGameObject.AddComponent<AntaresInterpolator>();
    }

    [MenuItem("Antares/GameObject/Add Interpolator &i", true)]
    static bool ValidateMenuAddInterpolator()
    {
        return Selection.activeTransform != null;
    }

    bool _apply = false;
    public override void OnInspectorGUI()
    {
        _handleRect = GUILayoutUtility.GetLastRect();
        _apply = false;
        AntaresGameObjectCustom.Routines.DrawHeader("ANTARES INTERPOLATOR");
        _serializableObject.Update();

        GUILayout.BeginHorizontal();
        GUILayout.Label("Animation Clip : ");
        AnimationClip l_clip = _targetObject.sourceClip;
        _targetObject.sourceClip = EditorGUILayout.ObjectField(_targetObject.sourceClip, typeof (AnimationClip), GUILayout.Width(256)) as AnimationClip;
        if (_targetObject.sourceClip != null && _targetObject.sourceClip != l_clip) _targetObject.curve = AnimationUtility.GetAllCurves(_targetObject.sourceClip, true)[0].curve;
        GUILayout.EndHorizontal();

        Component compo = _targetObject.target;

        if (_targetObject.target == null)
        {
            _targetObject.fieldName = "";
            GUI.enabled = false;
            GUILayout.Label("   Drag objects with script which need to\n interpolated value.");
            GUI.enabled = true;
        }
        // Draw Curve
        GUI.enabled = _targetObject.sourceClip != null && _targetObject.curve != null;
        _handleShow = EditorGUILayout.Foldout(_handleShow, "ShowCurve");
        _handleGUIOn = false;
        GUI.enabled = true;
        if(_handleShow)DrawCurve();


        GUI.enabled = _targetObject.sourceClip != null;
        GUILayout.BeginHorizontal();
        GUILayout.Label("Interpolation for : ");
        _targetObject.target = EditorGUILayout.ObjectField(_targetObject.target, typeof(Component), GUILayout.Width(256)) as Component;
        GUILayout.EndHorizontal();
        if (compo != _targetObject.target && _targetObject.target != null && _targetObject.target.GetType() == typeof(Transform))
        {
            _targetObject.fieldName = "";
            _fieldContentList.Clear();
            foreach (Component c in _targetObject.target.GetComponents<Component>()) FindTarget(c);
            RebuildContent();
        }
        GUI.enabled = true;

        if (_fieldContent != null && _fieldContent.Length != 0)
        {
            GUI.enabled = false;
            GUILayout.Label("Select Interpolator variable, which you wish\nuse for interpolation");
            GUI.enabled = true;

            GUILayout.BeginHorizontal();
            GUILayout.Label("Interpolation var : ");
            int before = _selectedField;
            _selectedField = EditorGUILayout.Popup(_selectedField, _fieldContent, GUILayout.Width(256));
            _targetObject.fieldName = _fieldContent[_selectedField].text;
            if(before != _selectedField)_apply = true;
            GUILayout.EndHorizontal();
        }
        else if (_targetObject.sourceClip != null)
        {
            GUI.enabled = false;
            GUILayout.Label("No field found.\n   You must have at least one field\nof type Interpolator in anyone script on your object.\n   But remember : only one script can use interpolation\nfor one GameObject.");
            GUI.enabled = true;
        }

        GUILayout.Space(3);

        if (GUILayout.Button("Reset All settings", EditorStyles.miniButton))
        {
            Undo.RegisterSceneUndo("Reset Interpolator");
            _handleShow = false;
            _targetObject.Reset();
            _apply = true;
        }

        if (_apply) _serializableObject.ApplyModifiedProperties();
        GUILayout.Space(-20);
        AntaresGameObjectCustom.Routines.DrawFooter();
        if (_handleGUIOn)
        {
            Handles.EndGUI();
            HandleUtility.Repaint();
        }
    }

    void OnSceneGUI()
    {
        if(_targetObject.sourceClip != null)_targetObject.curve = AnimationUtility.GetAllCurves(_targetObject.sourceClip, true)[0].curve;
    }

    private Camera _handleCamera;
    private Rect _handleRect, _handleRect0 = new Rect(0, 255, 10,10);
    private bool _handleGUIOn = false, _handleShow = false;
    private readonly int _curvePointsCount = 100;
    private Vector3[] _curvePoints, _curvePointsZero, _curvePointsTime = new Vector3[2];
    
    private void DrawCurve()
    {
        try
        {
            if (_targetObject.curve == null) return;

            _handleCamera = new GameObject("Handle Interpolator Camera", typeof (Camera)).GetComponent<Camera>();
            _handleCamera.gameObject.hideFlags = HideFlags.HideAndDontSave;
            _handleCamera.enabled = false;
            _handleCamera.isOrthoGraphic = true;
            _handleCamera.cullingMask = (int)Mathf.Pow(3, 2);

            CalculateCurve();

            Rect r = _handleRect;
            if (_handleRect.width == 1 || _handleRect.height == 0) r = _handleRect0;
            Handles.SetCamera(new Rect(r.x, r.y + 100, r.width, r.width*.5f), _handleCamera);
            Handles.color = AntaresEdMainWindow.colorSelected;
            Handles.DrawLine(_curvePointsZero[0], _curvePointsZero[1]);
            Handles.DrawLine(_curvePointsTime[0], _curvePointsTime[1]);
            Handles.color = Color.Lerp(Color.cyan, Color.black, .35f);
            Handles.DrawAAPolyLine(_curvePoints);
            if (_handleRect.width != 1) _handleRect0 = _handleRect;
            DestroyImmediate(_handleCamera.gameObject);
        }
        catch (System.Exception ex)
        {
            Debug.Log("Handle Error : " + ex);
        }
        _handleGUIOn = true;
        Handles.BeginGUI(new Rect(0, _handleRect0.y + 100 + _handleRect0.width * .5f, _handleRect0.width, 180));
    }

    private void CalculateCurve()
    {
        if (_curvePoints == null) _curvePoints = new Vector3[_curvePointsCount];
        float min = 0, max = 0, dev = 1f / _curvePointsCount, add = 2.5f;

        for (int i = 0; i < _curvePointsCount; i++)
        {
            float t = _targetObject.Evaluate(dev * i * _targetObject.curve.keys[_targetObject.curve.keys.Length - 1].time);
            _curvePoints[i] = new Vector3((i*add - 125f), t, 150);
            if (_curvePoints[i].y < min) min = _curvePoints[i].y;
            if (_curvePoints[i].y > max) max = _curvePoints[i].y;
        }
        _curvePoints[_curvePointsCount - 1].y = _targetObject.Evaluate(_targetObject.curve.keys[_targetObject.curve.keys.Length - 1].time);

        float appology = .1f/((max + Mathf.Abs(min)) * .001f);

        for (int i = 0; i < _curvePointsCount; i++) _curvePoints[i].y = _curvePoints[i].y*(appology);

        if (_curvePointsZero == null)
        {
            _curvePointsZero = new Vector3[2];
            _curvePointsZero[0] = new Vector3(-140, 0, 151);
            _curvePointsZero[1] = new Vector3(140, 0, 151);
        }

        float curX = _targetObject.GetCurrentTimeForEditor();
        if (!Application.isPlaying || curX == 0 || curX == 1000123) curX = -125f;
        else
            curX = (curX/((_targetObject.curve.keys[_targetObject.curve.keys.Length - 1].time)/_curvePointsCount))*add -
                   125f;
        _curvePointsTime[0] = new Vector3(curX, 100, 151);
        _curvePointsTime[1] = new Vector3(_curvePointsTime[0].x, -80, 151);
    }

    private void RebuildContent()
    {
        _fieldContent = new GUIContent[_fieldContentList.Count];
        _fieldContentList.CopyTo(_fieldContent);
    }

    void FindTarget(Component component)
    {
        FieldInfo[] finfo = component.GetType().GetFields(
                        BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);

        _targetObject.curve = AnimationUtility.GetAllCurves(_targetObject.sourceClip, true)[0].curve;
        foreach (FieldInfo field in finfo)
            if (field.FieldType == typeof(AntaresInterpolator))
            {
                _targetObject.target = component;
                _apply = true;
                _fieldContentList.Add(new GUIContent(field.Name, ""));
            }
    }
}
