using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using System.Drawing.Drawing2D;

using CommonAI.RTS;
using CommonAI.Zone;
using CommonAI.Zone.Instance;

namespace CommonTest
{

    /// <summary>
    /// 基础对象
    /// </summary>
    public abstract class GameObject
    {
        readonly public Vector2 pos = new Vector2();
        public float pos_z;
        public float direction;

        private GameWorld world;
        private int id;

        public GameObject(GameWorld wd, int id)
        {
            this.world = wd;
            this.id = id;
        }

        public int ID { get { return id; } }
        public GameWorld Parent { get { return world; } }

        abstract public RectangleF Bounds { get; }

        abstract public void doEvent(ObjectEvent e);

        abstract public void update();

        abstract public void render(Graphics g);

        internal void syncPos(float x, float y, float d)
        {
            this.pos.x = x;
            this.pos.y = y;
            this.direction = d;
        }

    }


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

    /// <summary>
    /// 显示对象
    /// </summary>
    abstract public class GameUnit : GameObject
    {
        public enum UnitStateType
        {
            IDLE = 0,
            MOVE,
            ACTION,
            DAMAGE,
            LIMITED,
            DEAD,
        }

        private UnitInfo info;

        private UnitStateType state = UnitStateType.IDLE;

        // Move
        private Vector2 targetPos = null;

        // Status
        private int mHP;
        private int mFreezeTimeHit;
        private int mFreezeTimeAtk;

        Pen[] pens = new Pen[] 
        { 
            new Pen(Color.LightBlue),
            new Pen(Color.Red),
            new Pen(Color.Green),
            new Pen(Color.Magenta),
        };

        public GameUnit(GameWorld wd, UnitInfo info, int id)
            : base(wd, id)
        {
            this.info = info;
            this.pen = pens[info.force];
            this.mHP = info.HealthPoint;

            foreach (int skillID in info.skills)
            {
                SkillTemplate st = TemplateManager.getInstance().getSkill(skillID);
                if (st != null && !mSkillStatus.ContainsKey(skillID)) 
                {
                    mSkillStatus.Add(skillID, new SkillState(st));
                }
            }
        }

        public UnitInfo Info { get { return info; } }
        public float Speed { get { return info.moveSpeed; } }

        public UnitStateType State
        {
            get { return state; }
            set { this.state = value; }
        }

        override public RectangleF Bounds
        {
            get
            {
                return new RectangleF(
                    pos.x - info.bodySize,
                    pos.y - info.bodySize,
                    info.bodySize * 2,
                    info.bodySize * 2);
            }
        }

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

        #region  Render

        private Pen pen;
        private Pen pen_attack = new Pen(Color.FromArgb(0x80, 0xff, 0x00, 0x00));
        private Pen pen_guard = new Pen(Color.FromArgb(0x80, 0xff, 0xff, 0x00));
        private Pen pen_path = new Pen(Color.FromArgb(0xff, 0x80, 0xff, 0x80));
        private Pen pen_hit = new Pen(Color.FromArgb(0xff, 0xff, 0xff, 0xff));

        private Brush brush = new SolidBrush(Color.FromArgb(0x80, 0x80, 0x80, 0x80));
        private Brush brush_hp = new SolidBrush(Color.FromArgb(0xff, 0, 0xff, 0));
        private Brush brush_black = new SolidBrush(Color.FromArgb(0xff, 0, 0, 0));
        private Brush brush_attack = new SolidBrush(Color.FromArgb(0x80, 0xff, 0x00, 0x00));

        override public void render(Graphics g)
        {
            if (mFreezeTimeHit > 0)
            {
                g.TranslateTransform((float)Math.Cos(mFreezeTimeHit * Math.PI / 2) * 2, 0);
            }
            if (state == UnitStateType.DAMAGE || mFreezeTimeHit > 0)
            {
                g.FillPie(brush_attack, -info.bodySize, -info.bodySize, info.bodySize * 2, info.bodySize * 2, 0, 360);
            }
            else if (state == UnitStateType.ACTION)
            {
                g.FillPie(brush, -info.bodySize, -info.bodySize, info.bodySize * 2, info.bodySize * 2, 0, 360);
            }

            // render body
            g.DrawArc(pen, -info.bodySize, -info.bodySize, info.bodySize * 2, info.bodySize * 2, 0, 360);
            // render direction
            renderDirection(g, pen, direction, info.bodySize);
            // render attack angle
            //renderRange(g, pen_attack, direction, info.attackRange, info.attackAngle);
            // render guard angle
            //g.DrawArc(pen_guard, -info.guardRange, -info.guardRange, info.guardRange * 2, info.guardRange * 2, 0, 360);

            if (mFreezeTimeAtk > 0)
            {
            //    fillRange(g, brush_attack, direction, info.attackRange, info.attackAngle);
            }

            if (targetPos != null && info.type == UnitInfo.UnitType.TYPE_PLAYER)
            {
                g.DrawLine(pen_path, 0, 0, targetPos.x - pos.x, targetPos.y - pos.y);
            }

            renderHP(g);
            //g.DrawString("123", GameWorld.global_font, new SolidBrush(Color.White), 0, 0, StringFormat.GenericDefault);
        }

        private void renderHP(Graphics g)
        {
            float sx = -info.bodySize-2;
            float sy = -info.bodySize-8;
            float sw = info.bodySize*2+4;
            float sh = 6;
            g.FillRectangle(brush_black, sx, sy, sw, sh);
            g.FillRectangle(brush_hp, sx + 1, sy + 1, (sw - 2) * mHP / info.HealthPoint, sh - 2);
        }

        private void renderDirection(Graphics g, Pen pen, float direction, float range)
        {
            g.DrawLine(pen, 0, 0,
                (float)Math.Cos(direction) * range,
                (float)Math.Sin(direction) * range);
        }

        private void renderRange(Graphics g, Pen pen, float direction, float range, float angle)
        {
            float startRadians = direction - angle / 2;
            float endRadians = direction + angle / 2;
            g.DrawArc(pen,
                -range,
                -range,
                range * 2,
                range * 2,
                RTSMath.RadiansToDegrees(startRadians),
                RTSMath.RadiansToDegrees(angle));
            g.DrawLine(pen, 0, 0,
                (float)Math.Cos(startRadians) * range,
                (float)Math.Sin(startRadians) * range);
            g.DrawLine(pen, 0, 0,
                (float)Math.Cos(endRadians) * range,
                (float)Math.Sin(endRadians) * range);
        }

        private void fillRange(Graphics g, Brush pen, float direction, float range, float angle)
        {
            float startRadians = direction - angle / 2;
            float endRadians = direction + angle / 2;
            g.FillPie(pen,
                -range,
                -range,
                range * 2,
                range * 2,
                RTSMath.RadiansToDegrees(startRadians),
                RTSMath.RadiansToDegrees(angle));
        }

        #endregion

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

        override public void doEvent(ObjectEvent e)
        {
            if (e is UnitIdleEvent)
            {
                this.State = UnitStateType.IDLE;
                //Parent.showLog("UnitIdleEvent", pos.x, pos.y);
            }
            else if (e is UnitMoveEvent)
            {
                UnitMoveEvent me = (UnitMoveEvent)e;
                this.State = UnitStateType.MOVE;
                this.targetPos = new Vector2(me.x, me.y);
            }
            else if (e is UnitSlipEvent)
            {
                UnitSlipEvent me = e as UnitSlipEvent;
                this.targetPos = null;
            }
            else if (e is UnitFollowEvent)
            {
                UnitFollowEvent me = (UnitFollowEvent)e;
                this.State = UnitStateType.MOVE;
            }
            else if (e is UnitLaunchSkillEvent)
            {
                UnitLaunchSkillEvent me = (UnitLaunchSkillEvent)e;
                SkillState ss;
                if (mSkillStatus.TryGetValue(me.skill_id, out ss))
                {
                    ss.launch();
                }
            }
            else if (e is UnitActionkEvent)
            {
                UnitActionkEvent me = (UnitActionkEvent)e;
                this.State = UnitStateType.ACTION;
            }
            else if (e is UnitAttackEvent)
            {
                UnitAttackEvent me = (UnitAttackEvent)e;
                this.mFreezeTimeAtk = me.freeze_time;
            }
            else if (e is UnitHitEvent)
            {
                UnitHitEvent me = (UnitHitEvent)e;
                if (me.damage)
                {
                    this.State = UnitStateType.DAMAGE;
                }
                this.mFreezeTimeHit = me.freeze_time;
                this.mHP -= me.hp;
                this.mHP = Math.Max(0, mHP);
                Parent.showLog("" + me.hp, pos.x, pos.y);
            }
            else if (e is UnitDeadEvent)
            {
                UnitDeadEvent me = (UnitDeadEvent)e;
                this.State = UnitStateType.DEAD;
                Parent.showLog("UnitDeadEvent", pos.x, pos.y);
            }
        }

        override public void update()
        {
            if (mFreezeTimeAtk > 0)
            {
                mFreezeTimeAtk--;
            }

            if (mFreezeTimeHit > 0)
            {
                mFreezeTimeHit--;
            }

            switch (state)
            {
                case UnitStateType.IDLE:
                    break;
                case UnitStateType.MOVE:
                    break;
                case UnitStateType.DAMAGE:
                    break;
                case UnitStateType.ACTION:
                    break;
                default:
                    break;
            }

            foreach (SkillState ss in mSkillStatus.Values)
            {
                ss.update();
            }
        }


        //-----------------------------------------------------------------------------------------------
        #region SkillState

        // Skills
        private IDictionary<int, SkillState> mSkillStatus = new SortedDictionary<int, SkillState>();


        public class SkillState
        {
            readonly public SkillTemplate Data;

            private int cool_down_time;

            public SkillState(SkillTemplate data)
            {
                this.Data = data;
            }

            internal void launch()
            {
                this.cool_down_time = Data.cool_down;
            }

            internal void update()
            {
                if (cool_down_time > 0)
                {
                    cool_down_time--;
                }
            }

            public float getCDPercent()
            {
                if (Data.cool_down == 0)
                {
                    return 0;
                }
                return (cool_down_time / (float)Data.cool_down);
            }

            public bool isCD()
            {
                return cool_down_time > 0;
            }
        }

        public ICollection<SkillState> getSkillStatus()
        {
            return new List<SkillState>(mSkillStatus.Values);
        }

        #endregion

    }

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

    public class GameUnitActor : GameUnit
    {
        public GameUnitActor(GameWorld wd, UnitInfo info, int id)
            : base(wd, info, id)
        {

        }
    }

    public class GameUnitNpc : GameUnit
    {
        public GameUnitNpc(GameWorld wd, UnitInfo info, int id)
            : base(wd, info, id)
        {

        }
    }

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

    public class GameSpellObject : GameObject
    {
        private SpellTemplate info;

        private Pen pen = new Pen(Color.FromArgb(0xff, 0xff, 0xff, 0x00));

        private float size;

        public GameSpellObject(GameWorld wd, SpellTemplate sp, int id, GameUnit launcher, GameUnit target)
            : base(wd, id)
        {
            this.info = sp;
            this.size = info.bodySize;
        }

        override public RectangleF Bounds
        {
            get
            {
                return new RectangleF(
                    pos.x - info.bodySize,
                    pos.y - info.bodySize,
                    info.bodySize * 2,
                    info.bodySize * 2);
            }
        }

        public override void doEvent(ObjectEvent e)
        {

        }

        public override void update()
        {
            if (info.motionType == SpellTemplate.MotionType.AOE)
            {
                size += info.motionSpeed;
            }
        }

        public override void render(Graphics g)
        {
            if (info.motionType == SpellTemplate.MotionType.Laser)
            {
                g.DrawLine(pen, 0, 0,
                    (float)Math.Cos(direction) * info.bodySize,
                    (float)Math.Sin(direction) * info.bodySize);
            }
            else
            {
                // render body
                g.DrawArc(pen, -size, -size, size * 2, size * 2, 0, 360);
                g.DrawLine(pen, 0, 0,
                    (float)Math.Cos(direction) * info.bodySize,
                    (float)Math.Sin(direction) * info.bodySize);
            }
        }
    }

    //-----------------------------------------------------------------------------------------------
   
}