using CommonAI.RTS; using CommonLang.Vector;
using CommonAI.Zone.Instance;
using CommonLang;
using CommonLang.Property;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonAI.Zone.Helper
{
    public enum AttackShape : byte
    {
        [DescAttribute("单体")]
        Single = 255,
        [DescAttribute("圆形")]
        Round = 0,
        [DescAttribute("扇形")]
        Fan = 1,

        [DescAttribute("胶囊条状")]
        Strip = 2,
        [DescAttribute("胶囊射线(以原点出去)")]
        StripRay = 3,
        [DescAttribute("胶囊射线,接触到最近")]
        StripRayTouchEnd = 4,



        [DescAttribute("方形条状")]
        RectStrip = 12,
        [DescAttribute("方形射线(以原点出去)")]
        RectStripRay = 13,
        [DescAttribute("横向胶囊条状")]
        WideStrip = 14,

        [DescAttribute("连线类型,比如激光塔持续造成伤害")]
        LineToTarget = 5,
        [DescAttribute("连线类型,比如伸出去的钩子")]
        LineToStart = 7,
        [DescAttribute("圆环,中间是空的")]
        Circle = 6,
    }

    public enum AttackReason : byte
    {
        [Desc("范围攻击或者技能攻击")]
        Attack = 0,
        [Desc("搜索范围内可攻击目标")]
        Look = 1,
        [Desc("被攻击后反击")]
        Damaged = 2,
        [Desc("移动被阻挡后攻击")]
        MoveBlocked = 3,
        [Desc("检测仇恨目标")]
        Tracing = 4,
    }

    public class AttackRangeHelper
    {
        private readonly InstanceUnit mLauncherUnit;
        private readonly InstanceZone mParent;
        private float mCircleInR;
        private AttackReason mAttackReason = AttackReason.Attack;
        private ITemplateData mWeapon;

        public AttackRangeHelper(InstanceUnit launcher)
        {
            this.mLauncherUnit = launcher;
            this.mParent = launcher.Parent;
        }
        public AttackShape Shape { get; set; }
        public SkillTemplate.CastTarget ExpectTarget { get; set; }
        public float BodySize { get; set; }
        public float FanAngle { get; set; }
        public float Direction { get; set; }
        public float Distance { get; set; }

        public float StripWide { get; set; }

        public bool IsHadAttackable(float X, float Y, float r, SpellTemplate spell)
        {
            var b = mParent.ForEachNearObjects<InstanceUnit>(X, Y, r, (InstanceUnit o, ref bool cancel) =>
           {
               if (mLauncherUnit.AoiStatus == o.AoiStatus)
               {
                   if (mParent.IsAttackable(mLauncherUnit, o, spell.ExpectTarget, AttackReason.Attack, spell))
                   {
                       cancel = true;
                   }
               }
           });
            return b;
        }


        /// <summary>
        ///        范围伤害,碰撞检测 
        /// </summary>
        /// <param name="list"></param>
        /// <param name="reason"></param>
        /// <param name="weapon"></param>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="PrevX"></param>
        /// <param name="PrevY"></param>
        /// <param name="max_affect"></param>
        /// <param name="aoi"></param>
        /// <returns></returns>
        /// 
        public void GetShapeAttackable(
            List<InstanceUnit> list,
            AttackReason reason,
            ITemplateData weapon,
            float X,
            float Y,
            float PrevX,
            float PrevY,
            int max_affect,
            ObjectAoiStatus aoi)
        {
            mAttackReason = reason;
            mWeapon = weapon;
            switch (Shape)
            {
                case AttackShape.Round:
                    if (PrevX != X || PrevY != Y)
                    {
                        mParent.getObjectsRoundLineRange<InstanceUnit>(touch_RoundLine, PrevX, PrevY, X, Y, BodySize, list, aoi);
                    }
                    else
                    {
                        mParent.getObjectsRoundRange<InstanceUnit>(touch_Round, X, Y, BodySize, list, aoi);
                    }
                    break;
                case AttackShape.Circle:
                    if (BodySize > StripWide)
                    {
                        mCircleInR = BodySize - StripWide;
                        mParent.getObjectsRoundRange<InstanceUnit>(touch_Circle, X, Y, BodySize, list, aoi);
                    }
                    else
                    {
                        mParent.getObjectsRoundRange<InstanceUnit>(touch_Round, X, Y, BodySize, list, aoi);
                    }
                    break;
                case AttackShape.Fan:
                    {
                        float dfan = FanAngle / 2f;
                        mParent.getObjectsFanRange<InstanceUnit>(touch_Fan, X, Y, BodySize, Direction - dfan, Direction + dfan, list, aoi);
                    }
                    break;
                case AttackShape.Strip:
                    {
                        //float d_angle = this.Direction + CMath.PI_DIV_2;
                        float d_width = StripWide / 2f;
                        float d_distance = Distance / 2f;
                        Vector2 p0 = new Vector2(X, Y);
                        Vector2 p1 = new Vector2(X, Y);
                        MathVector.movePolar(p0, this.Direction, -d_distance);
                        MathVector.movePolar(p1, this.Direction, d_distance);
                        mParent.getObjectsRoundLineRange<InstanceUnit>(touch_RoundLine, p0.X, p0.Y, p1.X, p1.Y, d_width, list, aoi);
                    }
                    break;
                case AttackShape.StripRay:
                    {
                        float d_width = StripWide / 2f;
                        float d_distance = Distance;
                        Vector2 p1 = new Vector2(X, Y);
                        MathVector.movePolar(p1, this.Direction, d_distance);
                        mParent.getObjectsRoundLineRange<InstanceUnit>(touch_RoundLine, X, Y, p1.X, p1.Y, d_width, list, aoi);
                    }
                    break;
                case AttackShape.StripRayTouchEnd:
                    {
                        float d_width = StripWide / 2f;
                        float d_distance = Distance;
                        Vector2 p1 = new Vector2(X, Y);
                        MathVector.movePolar(p1, this.Direction, d_distance);
                        mParent.getObjectsRoundLineRange<InstanceUnit>(touch_RoundLine, X, Y, p1.X, p1.Y, d_width, list, aoi);
                        if (list.Count > 1)
                        {
                            list.Sort(new ObjectBodySorterNearest<InstanceUnit>(X, Y, d_width));
                            list.RemoveRange(1, list.Count - 1);
                        }
                    }
                    break;
                case AttackShape.RectStrip:
                    {
                        //float d_angle = this.Direction + CMath.PI_DIV_2;
                        float d_width = StripWide / 2f;
                        float d_distance = Distance / 2f;
                        Vector2 p0 = new Vector2(X, Y);
                        Vector2 p1 = new Vector2(X, Y);
                        MathVector.movePolar(p0, this.Direction, -d_distance);
                        MathVector.movePolar(p1, this.Direction, d_distance);
                        mParent.getObjectsRectLineRange<InstanceUnit>(touch_RectLine, p0.X, p0.Y, p1.X, p1.Y, d_width, list, aoi);
                    }
                    break;
                case AttackShape.RectStripRay:
                    {
                        float d_width = StripWide / 2f;
                        float d_distance = Distance;
                        Vector2 p1 = new Vector2(X, Y);
                        MathVector.movePolar(p1, this.Direction, d_distance);
                        mParent.getObjectsRectLineRange<InstanceUnit>(touch_RectLine, X, Y, p1.X, p1.Y, d_width, list, aoi);
                    }
                    break;
                case AttackShape.WideStrip:
                    {
                        float d_width = StripWide / 2f;
                        float d_angle = Direction + CMath.PI_DIV_2;
                        float d_distance = Distance / 2f;
                        Vector2 p0 = new Vector2(X, Y);
                        Vector2 p1 = new Vector2(X, Y);
                        MathVector.movePolar(p0, d_angle, -d_width);
                        MathVector.movePolar(p1, d_angle, +d_width);
                        mParent.getObjectsRoundLineRange<InstanceUnit>(touch_RoundLine, p0.X, p0.Y, p1.X, p1.Y, d_distance, list, aoi);
                    }
                    break;
                case AttackShape.Single:
                case AttackShape.LineToTarget:
                case AttackShape.LineToStart:
                    //此类型作为单独命中指定目标//
                    break;
                default:
                    {
                        mParent.getObjectsRoundRange<InstanceUnit>(touch_Round, X, Y, BodySize, list, aoi);
                    }
                    break;
            }
            // 最大攻击数量 //
            if (max_affect > 0 && list.Count > max_affect)
            {
                CUtils.RandomList(mParent.RandomN, list);
                while (list.Count > max_affect)
                {
                    list.RemoveAt(list.Count - 1);
                }
            }
        }
        private bool touch_Round(InstanceZoneObject o, float x, float y, float r)
        {
            if (CMath.intersectRound(x, y, r, o.X, o.Y, o.BodyHitSize))
            {
                return mParent.IsAttackable(mLauncherUnit, o as InstanceUnit, ExpectTarget, mAttackReason, mWeapon);
            }
            return false;
        }
        private bool touch_Fan(InstanceZoneObject o, float x, float y, float distance, float startAngle, float endAngle)
        {
            if (CMath.intersectFanRound(x, y, distance, o.X, o.Y, o.BodyHitSize, startAngle, endAngle))
            {
                return mParent.IsAttackable(mLauncherUnit, o as InstanceUnit, ExpectTarget, mAttackReason, mWeapon);
            }
            return false;
        }
        private bool touch_RoundLine(InstanceZoneObject o, float x1, float y1, float x2, float y2, float line_r)
        {
            if (Collider.Object_HitBody_TouchRoundLine(o, x1, y1, x2, y2, line_r))
            {
                return mParent.IsAttackable(mLauncherUnit, o as InstanceUnit, ExpectTarget, mAttackReason, mWeapon);
            }
            return false;
        }
        private bool touch_RectLine(InstanceZoneObject o, float x1, float y1, float x2, float y2, float line_r)
        {
            if (Collider.Object_HitBody_TouchRectLine(o, x1, y1, x2, y2, line_r))
            {
                return mParent.IsAttackable(mLauncherUnit, o as InstanceUnit, ExpectTarget, mAttackReason, mWeapon);
            }
            return false;
        }
        private bool touch_Circle(InstanceZoneObject o, float x, float y, float r)
        {
            if (CMath.intersectRound(x, y, r, o.X, o.Y, o.BodyHitSize))
            {
                if (mParent.IsAttackable(mLauncherUnit, o as InstanceUnit, ExpectTarget, mAttackReason, mWeapon))
                {
                    if (mCircleInR < o.BodyHitSize)
                    {
                        return true;
                    }
                    return (!CMath.includeRound2(x, y, mCircleInR, o.X, o.Y, o.BodyHitSize));
                }
            }
            return false;
        }

    }
}