using UnityEngine;

namespace FairyGUI
{
    /// <summary>
    /// 长按手势。当按下一定时间后(duration)，派发onAction，如果once为false，则间隔duration时间持续派发onAction，直到手指释放。
    /// </summary>
    public class LongPressGesture : 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 trigger;

        /// <summary>
        /// 派发onAction事件的时间间隔。单位秒。
        /// </summary>
        public float interval;

        /// <summary>
        /// 如果为真，则onAction再一次按下释放过程只派发一次。如果为假，则每隔duration时间派发一次。
        /// </summary>
        public bool once;

        /// <summary>
        /// 手指按住后，移动超出此半径范围则手势停止。
        /// </summary>
        public int holdRangeRadius;

        Vector2 _startPoint;
        bool _started;
        int _touchId;

        public static float TRIGGER = 1.5f;
        public static float INTERVAL = 1f;

        public LongPressGesture(GObject host)
        {
            this.host = host;
            trigger = TRIGGER;
            interval = INTERVAL;
            holdRangeRadius = 50;
            Enable(true);

            onBegin = new EventListener(this, "onLongPressBegin");
            onEnd = new EventListener(this, "onLongPressEnd");
            onAction = new EventListener(this, "onLongPressAction");
        }

        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.onTouchEnd.Add(__touchEnd);
                }
                else
                {
                    host.onTouchBegin.Add(__touchBegin);
                    host.onTouchEnd.Add(__touchEnd);
                }
            }
            else
            {
                if (host == GRoot.inst)
                {
                    Stage.inst.onTouchBegin.Remove(__touchBegin);
                    Stage.inst.onTouchEnd.Remove(__touchEnd);
                }
                else
                {
                    host.onTouchBegin.Remove(__touchBegin);
                    host.onTouchEnd.Remove(__touchEnd);
                }
                Timers.inst.Remove(__timer);
            }
        }

        public void Cancel()
        {
            Timers.inst.Remove(__timer);
            _started = false;
        }

        void __touchBegin(EventContext context)
        {
            InputEvent evt = context.inputEvent;
            _startPoint = host.GlobalToLocal(new Vector2(evt.x, evt.y));
            _started = false;
            _touchId = evt.touchId;

            Timers.inst.Add(trigger, 1, __timer);
            context.CaptureTouch();
        }

        void __timer(object param)
        {
            Vector2 pt = host.GlobalToLocal(Stage.inst.GetTouchPosition(_touchId));
            if (Mathf.Pow(pt.x - _startPoint.x, 2) + Mathf.Pow(pt.y - _startPoint.y, 2) > Mathf.Pow(holdRangeRadius, 2))
            {
                Timers.inst.Remove(__timer);
                return;
            }
            if (!_started)
            {
                _started = true;
                onBegin.Call();

                if (!once)
                    Timers.inst.Add(interval, 0, __timer);
            }

            onAction.Call();
        }

        void __touchEnd(EventContext context)
        {
            Timers.inst.Remove(__timer);

            if (_started)
            {
                _started = false;
                onEnd.Call();
            }
        }
    }
}
