using System;
using System.Collections.Generic;
using System.Text;
using CommonLang;
using CommonAI.RTS;
using CommonLang.Vector;
using CommonAI.RTS.Manhattan;
using CommonAI.Zone.Helper;

namespace CommonAI.Zone.Instance
{
    
    /// <summary>
    /// 观察一定范围的触发器
    /// </summary>
    abstract public class ViewTrigger : Ability
    {
        /// <summary>
        /// 监听单位进出观察范围
        /// </summary>
        public interface ViewTriggerListener
        {
            /// <summary>
            /// 当一个单位进入观察范围后触发
            /// </summary>
            /// <param name="src">观察者</param>
            /// <param name="obj">进入视野的单位</param>
            void onObjectEnterView(ViewTrigger src, InstanceZoneObject obj);

            /// <summary>
            /// 当一个单位离开观察范围后触发
            /// </summary>
            /// <param name="src">观察者</param>
            /// <param name="obj">离开视野范围的单位</param>
            void onObjectLeaveView(ViewTrigger src, InstanceZoneObject obj);

            /// <summary>
            /// 是否可见
            /// </summary>
            /// <param name="src"></param>
            /// <param name="obj"></param>
            /// <returns></returns>
            bool select(ViewTrigger src, InstanceZoneObject obj);
        }


        private bool mEnable = true;
        private int mMaxViewd = int.MaxValue;
        private ViewTriggerListener mViewListener;
        private List<InstanceZoneObject> mViewd = new List<InstanceZoneObject>();

        /// <summary>
        /// 检测频率
        /// </summary>
        private readonly TimeInterval<int> mCheckRate;

        public bool Enable
        {
            get { return mEnable; }
            set { mEnable = value; }
        }

        public IEnumerable<InstanceZoneObject> InViewed
        {
            get { return mViewd; }
        }

        public ViewTrigger(InstanceZone zone)
            : base(zone)
        {
            mCheckRate = new TimeInterval<int>(Zone.Templates.CFG.AI_VIEW_TRIGGER_CHECK_TIME_MS);
        }
        public override void Dispose()
        {
            mEnable = false;
            base.Dispose();
            mViewd.Clear();
        }

        public void addViewed(InstanceZoneObject obj)
        {
            mViewd.Add(obj);
        }
        public InstanceZoneObject getRandomViewed(Random random)
        {
            return CUtils.GetRandomInArray<InstanceZoneObject>(mViewd, random);
        }
        [Obsolete]
        public List<InstanceZoneObject> getRandomViewedList(Random random)
        {
            List<InstanceZoneObject> ret = new List<InstanceZoneObject>(mViewd);
            CUtils.RandomList<InstanceZoneObject>(random, ret);
            return ret;
        }
        public void getRandomViewedList(Random random, List<InstanceZoneObject> ret)
        {
            ret.AddRange(mViewd);
            CUtils.RandomList<InstanceZoneObject>(random, ret);
        }


        /// <summary>
        /// 设置最大观察单位数量
        /// </summary>
        /// <param name="m"></param>
        public void setMaxViewd(int m)
        {
            this.mMaxViewd = m;
        }

        /// <summary>
        /// 增加观察者监听器
        /// </summary>
        /// <param name="listener"></param>
        public void setListener(ViewTriggerListener listener)
        {
            mViewListener = listener;
        }

        public virtual void onLookUpdate(float x, float y)
        {
            if (mEnable)
            {
                bool is_check = mCheckRate.Update(Zone.UpdateIntervalMS);
                this.check(x, y, is_check);
            }
            else if (mViewd.Count > 0)
            {
                for (int i = mViewd.Count - 1; i >= 0; --i)
                {
                    onObjectLeaveView(mViewd[i]);
                }
                mViewd.Clear();
            }
        }

        protected void onObjectEnterView(InstanceZoneObject o)
        {
            if (mViewListener != null)
            {
                mViewListener.onObjectEnterView(this, o);
            }
        }
        protected void onObjectLeaveView(InstanceZoneObject o)
        {
            if (mViewListener != null)
            {
                mViewListener.onObjectLeaveView(this, o);
            }
        }

        protected virtual void check(float x, float y, bool check)
        {
            if (check || this.IsNearChanged(x, y))
            {
                // 检索已看到的消失
                for (int i = mViewd.Count - 1; i >= 0; --i)
                {
                    InstanceZoneObject o = mViewd[i];
                    if (!o.Enable || !mViewListener.select(this, o) || !TestInView(o, x, y))
                    {
                        mViewd.RemoveAt(i);
                        onObjectLeaveView(o);
                    }
                }
                this.ForEachNearObjects(x, y, (InstanceZoneObject o) =>
                {
                    if (o.Enable && mViewListener.select(this, o) && !mViewd.Contains(o) && TestInView(o, x, y))
                    {
                        mViewd.Add(o);
                        onObjectEnterView(o);
                    }
                });
            }
        }

        abstract protected bool IsNearChanged(float x, float y);
        abstract protected bool TestInView(InstanceZoneObject o, float x, float y);
        abstract protected void ForEachNearObjects(float x, float y, Action<InstanceZoneObject> indexer);
    }


    /// <summary>
    /// 瞎子
    /// </summary>
    public class ViewTriggerBlind : ViewTrigger
    {
        public ViewTriggerBlind(InstanceZone zone) : base(zone)
        {
        }
        public override void onLookUpdate(float x, float y)
        {
        }
        protected override void ForEachNearObjects(float x, float y, Action<InstanceZoneObject> indexer)
        {
        }
        protected override bool IsNearChanged(float x, float y)
        {
            return false;
        }
        protected override bool TestInView(InstanceZoneObject o, float x, float y)
        {
            return false;
        }
    }

    /// <summary>
    /// 观察目标坐标是否在圆形范围内
    /// </summary>
    public class ViewTriggerRoundCenter : ViewTrigger
    {
        private float mLookRange;

        public ViewTriggerRoundCenter(InstanceZone zone, float r)
            : base(zone)
        {
            this.mLookRange = r;
        }
        protected override bool IsNearChanged(float x, float y)
        {
            return Zone.IsNearChanged(x, y, mLookRange);
        }
        protected override bool TestInView(InstanceZoneObject o, float x, float y)
        {
            return Collider.Object_Pos_IncludeInRound(o, x, y, mLookRange);
        }
        protected override void ForEachNearObjects(float x, float y, Action<InstanceZoneObject> indexer)
        {
            Zone.ForEachNearObjects(x, y, mLookRange, (InstanceZoneObject o, ref bool cancel) =>
            {
                indexer(o);
            });
        }
        /*
        public override void Check(InstanceZone zone, float x, float y, bool check)
        {
            if (check || zone.IsNearChanged(x, y, mLookRange))
            {
                // 检索已看到的消失
                for (int i = mViewd.Count - 1; i >= 0; --i)
                {
                    InstanceZoneObject o = mViewd[i];
                    if (!o.Enable || !mViewListener.select(this, o) || !Collider.Object_Pos_IncludeInRound(o, x, y, mLookRange))
                    {
                        mViewd.RemoveAt(i);
                        onObjectLeaveView(o);
                    }
                }
                zone.ForEachNearObjects(x, y, mLookRange, (InstanceZoneObject o, ref bool cancel) =>
                {
                    if (o.Enable && mViewListener.select(this, o) && !mViewd.Contains(o) && Collider.Object_Pos_IncludeInRound(o, x, y, mLookRange))
                    {
                        mViewd.Add(o);
                        onObjectEnterView(o);
                    }
                });
            }
        }
        */
    }

    /// <summary>
    /// 观察目标BlockBody是否在圆形范围内
    /// </summary>
    public class ViewTriggerRoundBody : ViewTrigger
    {
        private float mLookRange;

        public ViewTriggerRoundBody(InstanceZone zone, float r)
            : base(zone)
        {
            this.mLookRange = r;
        }
        public void SetLookRange(float r)
        {
            this.mLookRange = r;
        }
        protected override bool IsNearChanged(float x, float y)
        {
            return Zone.IsNearChanged(x, y, mLookRange);
        }
        protected override bool TestInView(InstanceZoneObject o, float x, float y)
        {
            return Collider.Object_BlockBody_TouchRound(o, x, y, mLookRange);
        }
        protected override void ForEachNearObjects(float x, float y, Action<InstanceZoneObject> indexer)
        {
            Zone.ForEachNearObjects(x, y, mLookRange, (InstanceZoneObject o, ref bool cancel) =>
            {
                indexer(o);
            });
        }

        /*
        public override void Check(InstanceZone zone, float x, float y, bool check)
        {
            if (check || zone.IsNearChanged(x, y, mLookRange))
            {
                // 检索已看到的消失
                for (int i = mViewd.Count - 1; i >= 0; --i)
                {
                    InstanceZoneObject o = mViewd[i];
                    if (!o.Enable || !mViewListener.select(this, o) || !Collider.Object_BlockBody_TouchRound(o, x, y, mLookRange))
                    {
                        mViewd.RemoveAt(i);
                        onObjectLeaveView(o);
                    }
                }
                zone.ForEachNearObjects(x, y, mLookRange, (InstanceZoneObject o, ref bool cancel) =>
                {
                    if (o.Enable && mViewListener.select(this, o) && !mViewd.Contains(o) && Collider.Object_BlockBody_TouchRound(o, x, y, mLookRange))
                    {
                        mViewd.Add(o);
                        onObjectEnterView(o);
                    }
                });
            }
        }
        */
    }

    /// <summary>
    /// 观察目标坐标是否在矩形范围内
    /// </summary>
    public class ViewTriggerRectCenter : ViewTrigger
    {
        private float mSizeW;
        private float mSizeH;
        private float sx, sy, dx, dy;

        public ViewTriggerRectCenter(InstanceZone zone, float w, float h)
            : base(zone)
        {
            this.mSizeW = w;
            this.mSizeH = h;
        }
        protected override void check(float x, float y, bool check)
        {
            this.sx = x - mSizeW / 2;
            this.sy = y - mSizeH / 2;
            this.dx = sx + mSizeW;
            this.dy = sy + mSizeH;
            base.check(x, y, check);
        }
        protected override bool IsNearChanged(float x, float y)
        {
            return Zone.IsNearChanged(sx, sy, dx, dy);
        }
        protected override bool TestInView(InstanceZoneObject o, float x, float y)
        {
            return Collider.Object_Pos_IncludeInRect(o, sx, sy, dx, dy);
        }
        protected override void ForEachNearObjects(float x, float y, Action<InstanceZoneObject> indexer)
        {
            Zone.ForEachNearObjectsRect(sx, sy, dx, dy, (InstanceZoneObject o, ref bool cancel) =>
            {
                indexer(o);
            });
        }
        /*
        public override void Check(InstanceZone zone, float x, float y, bool check)
        {
            if (check || zone.IsNearChanged(x, y, x + mSizeW, y + mSizeH))
            {
                float sx = x - mSizeW / 2;
                float sy = y - mSizeH / 2;
                float dx = sx + mSizeW;
                float dy = sy + mSizeH;

                // 检索已看到的消失
                for (int i = mViewd.Count - 1; i >= 0; --i)
                {
                    InstanceZoneObject o = mViewd[i];
                    if (!o.Enable || !mViewListener.select(this, o) || !Collider.Object_Pos_IncludeInRect(o, sx, sy, dx, dy))
                    {
                        mViewd.RemoveAt(i);
                        onObjectLeaveView(o);
                    }
                }
                zone.ForEachNearObjectsRect(sx, sy, dx, dy, (InstanceZoneObject o, ref bool cancel) =>
                {
                    if (o.Enable && mViewListener.select(this, o) && !mViewd.Contains(o) && Collider.Object_Pos_IncludeInRect(o, sx, sy, dx, dy))
                    {
                        mViewd.Add(o);
                        onObjectEnterView(o);
                    }
                });
            }
        }*/
    }

    /// <summary>
    /// 观察目标坐标是否在矩形范围内
    /// </summary>
    public class ViewTriggerStripCenter : ViewTrigger
    {
        private float mWidth;
        private float mHigh;
        private float mRange;
        private float mDirection;

        public ViewTriggerStripCenter(InstanceZone zone, float w, float h, float direction)
            : base(zone)
        {
            this.mWidth = w;
            this.mHigh = h;
            this.mRange = Math.Max(this.mWidth, this.mHigh);
            this.mDirection = direction;
        }

        protected override void check(float x, float y, bool check)
        {
            base.check(x, y, check);
            
        }
        protected override bool IsNearChanged(float x, float y)
        {
            return Zone.IsNearChanged(x, y, this.mRange);
        }
        protected override bool TestInView(InstanceZoneObject o, float x, float y)
        {
			Vector2 p0 = new Vector2(x, y);
            Vector2 p1 = new Vector2(x, y);
            MathVector.movePolar(p0, this.mDirection, -this.mHigh/2);
            MathVector.movePolar(p1, this.mDirection, +this.mHigh/2);

			return CMath.includeStripWidthPoint(p0.X, p0.Y, p1.X, p1.Y, this.mWidth, o.X, o.Y);
			//return Collider.Object_Pos_IncludeInRect(o, p0.x, p0.y, p1.x, p1.y);
        }
        protected override void ForEachNearObjects(float x, float y, Action<InstanceZoneObject> indexer)
        {
            Zone.ForEachNearObjects(x, y, this.mRange, (InstanceZoneObject o, ref bool cancel) =>
            {
                indexer(o);
            });
        }
    }


    /// <summary>
    /// 观察目标坐标是否在扇形范围内
    /// </summary>
    public class ViewTriggerFanCenter : ViewTrigger
    {
        private float mLookRange;
        private float mAngleRange;
        private float startAngle, endAngle;

        public float Direction { get; set; }

        public ViewTriggerFanCenter(InstanceZone zone, float range, float angle)
            : base(zone)
        {
            this.mLookRange = range;
            this.mAngleRange = angle;
        }
        public void SetLookRange(float r)
        {
            this.mLookRange = r;
        }
        protected override void check(float x, float y, bool check)
        {
            this.startAngle = Direction - mAngleRange;
            this.endAngle = Direction + mAngleRange;
            base.check(x, y, check);
        }
        protected override bool IsNearChanged(float x, float y)
        {
            return Zone.IsNearChanged(x, y, mLookRange);
        }
        protected override bool TestInView(InstanceZoneObject o, float x, float y)
        {
            return Collider.Object_Pos_IncludeInFan(o, x, y, mLookRange, startAngle, endAngle);
        }
        protected override void ForEachNearObjects(float x, float y, Action<InstanceZoneObject> indexer)
        {
            Zone.ForEachNearObjects(x, y, mLookRange, (InstanceZoneObject o, ref bool cancel) =>
            {
                indexer(o);
            });
        }
        /*
        public override void Check(InstanceZone zone, float x, float y, bool check)
        {
            float startAngle = Direction - mAngleRange;
            float endAngle = Direction + mAngleRange;
            for (int i = mViewd.Count - 1; i >= 0; --i)
            {
                InstanceZoneObject o = mViewd[i];
                if (!o.Enable || !mViewListener.select(this, o) || !Collider.Object_Pos_IncludeInFan(o, x, y, mLookRange, startAngle, endAngle))
                {
                    mViewd.RemoveAt(i);
                    onObjectLeaveView(o);
                }
            }
            zone.ForEachNearObjects(x, y, mLookRange, (InstanceZoneObject o, ref bool cancel) =>
            {
                if (o.Enable && mViewListener.select(this, o) && !mViewd.Contains(o) && Collider.Object_Pos_IncludeInFan(o, x, y, mLookRange, startAngle, endAngle))
                {
                    mViewd.Add(o);
                    onObjectEnterView(o);
                }
            });

        }*/
    }

    /// <summary>
    /// 观察目标身体是否在扇形范围内
    /// </summary>
    public class ViewTriggerFanBody : ViewTrigger
    {
        private float mLookRange;
        private float mAngleRange;
        private float startAngle, endAngle;

        public float Direction { get; set; }

        public ViewTriggerFanBody(InstanceZone zone, float range, float angle)
            : base(zone)
        {
            this.mLookRange = range;
            this.mAngleRange = angle;
        }
        public void SetLookRange(float r)
        {
            this.mLookRange = r;
        }
        protected override void check(float x, float y, bool check)
        {
            this.startAngle = Direction - mAngleRange / 2;
            this.endAngle = Direction + mAngleRange / 2;
            base.check(x, y, check);
        }
        protected override bool IsNearChanged(float x, float y)
        {
            return Zone.IsNearChanged(x, y, mLookRange);
        }
        protected override bool TestInView(InstanceZoneObject o, float x, float y)
        {
            return Collider.Object_BlockBody_TouchFan(o, x, y, mLookRange, startAngle, endAngle);
        }
        protected override void ForEachNearObjects(float x, float y, Action<InstanceZoneObject> indexer)
        {
            Zone.ForEachNearObjects(x, y, mLookRange, (InstanceZoneObject o, ref bool cancel) =>
            {
                indexer(o);
            });
        }
        /*
        public override void Check(InstanceZone zone, float x, float y, bool check)
        {
            float startAngle = Direction - mAngleRange;
            float endAngle = Direction + mAngleRange;
            for (int i = mViewd.Count - 1; i >= 0; --i)
            {
                InstanceZoneObject o = mViewd[i];
                if (!o.Enable || !mViewListener.select(this, o) || !Collider.Object_Pos_IncludeInFan(o, x, y, mLookRange, startAngle, endAngle))
                {
                    mViewd.RemoveAt(i);
                    onObjectLeaveView(o);
                }
            }
            zone.ForEachNearObjects(x, y, mLookRange, (InstanceZoneObject o, ref bool cancel) =>
            {
                if (o.Enable && mViewListener.select(this, o) && !mViewd.Contains(o) && Collider.Object_Pos_IncludeInFan(o, x, y, mLookRange, startAngle, endAngle))
                {
                    mViewd.Add(o);
                    onObjectEnterView(o);
                }
            });

        }*/
    }



    //-------------------------------------------------------------------------------------------

}