using System;
using System.Collections.Generic;
using UnityEngine;

namespace FairyGUI
{
    /// <summary>
    /// 手指反向操作的手势。
    /// </summary>
    public class RotationGesture : EventDispatcher
    {
        /// <summary>
        /// 
        /// </summary>
        public GObject host { get; private set; }

        /// <summary>
        /// 当两个手指开始呈反向操作时派发该事件。
        /// </summary>
        public EventListener onBegin { get; private set; }
        /// <summary>
        /// 当其中一个手指离开屏幕时派发该事件。
        /// </summary>
        public EventListener onEnd { get; private set; }
        /// <summary>
        /// 当手势动作时派发该事件。
        /// </summary>
        public EventListener onAction { get; private set; }

        /// <summary>
        /// 总共旋转的角度。
        /// </summary>
        public float rotation;

        /// <summary>
        /// 从上次通知后的改变量。
        /// </summary>
        public float delta;

        /// <summary>
        /// 是否把变化量强制为整数。默认true。
        /// </summary>
        public bool snapping;

        Vector2 _startVector;
        float _lastRotation;
        int[] _touches;
        bool _started;
        bool _touchBegan;

        public RotationGesture(GObject host)
        {
            this.host = host;
            Enable(true);

            _touches = new int[2];
            snapping = true;

            onBegin = new EventListener(this, "onRotationBegin");
            onEnd = new EventListener(this, "onRotationEnd");
            onAction = new EventListener(this, "onRotationAction");
        }

        public void Dispose()
        {
            Enable(false);
            host = null;
        }

        public void Enable(bool value)
        {
            if (value)
            {
                if (host == GRoot.inst)
                {
                    Stage.inst.onTouchBegin.Add(__touchBegin);
                    Stage.inst.onTouchMove.Add(__touchMove);
                    Stage.inst.onTouchEnd.Add(__touchEnd);
                }
                else
                {
                    host.onTouchBegin.Add(__touchBegin);
                    host.onTouchMove.Add(__touchMove);
                    host.onTouchEnd.Add(__touchEnd);
                }
            }
            else
            {
                _started = false;
                _touchBegan = false;
                if (host == GRoot.inst)
                {
                    Stage.inst.onTouchBegin.Remove(__touchBegin);
                    Stage.inst.onTouchMove.Remove(__touchMove);
                    Stage.inst.onTouchEnd.Remove(__touchEnd);
                }
                else
                {
                    host.onTouchBegin.Remove(__touchBegin);
                    host.onTouchMove.Remove(__touchMove);
                    host.onTouchEnd.Remove(__touchEnd);
                }
            }
        }

        void __touchBegin(EventContext context)
        {
            if (Stage.inst.touchCount == 2)
            {
                if (!_started && !_touchBegan)
                {
                    _touchBegan = true;
                    Stage.inst.GetAllTouch(_touches);
                    Vector2 pt1 = host.GlobalToLocal(Stage.inst.GetTouchPosition(_touches[0]));
                    Vector2 pt2 = host.GlobalToLocal(Stage.inst.GetTouchPosition(_touches[1]));
                    _startVector = pt1 - pt2;

                    context.CaptureTouch();
                }
            }
        }

        void __touchMove(EventContext context)
        {
            if (!_touchBegan || Stage.inst.touchCount != 2)
                return;

            InputEvent evt = context.inputEvent;
            Vector2 pt1 = host.GlobalToLocal(Stage.inst.GetTouchPosition(_touches[0]));
            Vector2 pt2 = host.GlobalToLocal(Stage.inst.GetTouchPosition(_touches[1]));
            Vector2 vec = pt1 - pt2;

            float rot = Mathf.Rad2Deg * ((Mathf.Atan2(vec.y, vec.x) - Mathf.Atan2(_startVector.y, _startVector.x)));
            if (snapping)
            {
                rot = Mathf.Round(rot);
                if (rot == 0)
                    return;
            }

            if (!_started && rot > 5)
            {
                _started = true;
                rotation = 0;
                _lastRotation = 0;

                onBegin.Call(evt);
            }

            if (_started)
            {
                delta = rot - _lastRotation;
                _lastRotation = rot;
                this.rotation += delta;
                onAction.Call(evt);
            }
        }

        void __touchEnd(EventContext context)
        {
            _touchBegan = false;
            if (_started)
            {
                _started = false;
                onEnd.Call(context.inputEvent);
            }
        }
    }
}
