using GiraffeRoller.entities.bullets;
using GiraffeRoller.entities.enemies;
using GiraffeRoller.scenes.Levels;
using GiraffeRoller.Utility;
using Otter;
using System;

namespace GiraffeRoller.entities
{
    class Boss : Entity
    {
        private Spritemap<string> _spriteMap;
        private bool _hMove = true;
        private bool _vMove = true;
        public static bool canPlayerCollide = true;
        public static bool isSpritemapChanged = true;
        private BasicMovement movement = new BasicMovement(400, 400, 400);//was 400,400,400
        private const float player2aiSpeedMax = 4f;//testing, update this as needed
        private const float bossFightSpeedMax = 1f;//testing, update this as needed
        private float player2aiSpeed = player2aiSpeedMax;//testing, update this as needed

        //ai follow me code
        private bool isSeekingTarget = true;
        private float closestDistanceItem = 3650f;
        private Entity closestItem;
        private double angleInRadians = 1;//this is about 57 degrees. ;-)
        private const int ScanTimerMax = 50;//50 is fine for slower speeds
        private int scanAgain = ScanTimerMax;
        private int hitTimerMax = Rand.Int(100, 150);
        private float _X;
        private float _Y;
        private float _grappleDragSpeed = 9f;
        private Vector2 velocity;

        private bool _isCloseEnoughToPlayer;
        private bool isInLevelOne;
        private bool isInBase;
        private bool canHitBoss;
        private float timer = 0;
        //sound bools in response to triggers
        private bool getHim;
        private bool comeBackHere;
        private bool getAwayOH;
        private bool bossHurt1;
        private bool bossHurt2;
        private bool death;

        public Boss(float x, float y)
        {
            X = x; Y = y;
            SetHitbox(130, 100, (int)Global.Type.BOSS);
            Collider.X = -55;
            Collider.Y = -55;
            Axis axis = Axis.CreateArrowKeys();
            movement.Axis = axis;
            movement.Collider = Collider;
            movement.AddCollision((int)Global.Type.SOLID);//walls
            AddComponents(axis, movement);
            LoadCorrectSpritemap();
            _spriteMap.CenterOrigin();
            movement.Speed = Global.BossSpeed;//oh... this is how it works. LoL this was not set. xD what happens if it IS...
        }
        public override void Update()
        {
            base.Update();
            if (isSpritemapChanged)
                LoadCorrectSpritemap();
            //follow the player around. for the AI she needs to actually walk to your destination. (where you were. Then stand there till you move)
            //so that when you move she moves in random stints to give you a slower animation cycle. :D she'll teleport to you on map changes.
            if (Global.StuckInLoop)
            {
                var scenes = Global.GAME.Scenes;//get all scenes             
                foreach (var s in scenes)//find the scene that is active
                {//run checks on that one scene(none of the others)
                    if (s == Global.GAME.GetScene<LevelOne>())
                    {
                        isInLevelOne = true;
                    }
                    if (s == Global.GAME.GetScene<Base>())
                    {
                        isInBase = true;
                    }
                    if (isInBase && _spriteMap.CurrentAnim != "death")
                    {
                        if (Global.EventFourCanKillBoss)
                        {
                            if (!comeBackHere)
                            {
                                comeBackHere = true;
                                SoundManager.PlayRequestedSound("_bossComeBackHere", 2f);
                            }
                            SeekPlayerMovement();
                        }
                        else
                            CaptureGiraffes();
                    }
                    if (isInLevelOne)
                    {
                        if (!getAwayOH)
                        {
                            getAwayOH = true;
                            SoundManager.PlayRequestedSound("_hitCar", 2f);
                            SoundManager.PlayRequestedSound("_bossOof", 2f);
                        }
                        ChangeAnimTo("lossLoopWalkRight");
                        _spriteMap.FlippedX = true;
                        X += 1;
                        if (X >= Global.MW + 200)
                            RemoveSelf();
                    }
                }

            }
            else
            {
                if (!getHim)
                {
                    getHim = true;
                    SoundManager.PlayRequestedSound("_bossGetHim", 4f);
                    SoundManager.PlayRequestedSound("_carEntrance", .5f);
                }
                SeekPlayerMovement();
            }
            if (Global.EventFourCanKillBoss)
            {
                var chargingPlayer = Collider.Collide(X, Y, (int)Global.Type.PLAYER);
                if (chargingPlayer != null && Player.Charging && canHitBoss)
                {
                    canHitBoss = false;
                    Global.BossHealth--;
                    Global.Score += 50;
                    Global.StopPlayerCharge = true;
                    SoundManager.PlayRequestedSound(RandomHit());
                }
                else if (chargingPlayer == null && !Player.Charging && !canHitBoss)
                {
                    canHitBoss = true;
                }
            }
            if (_spriteMap.CurrentAnim == "death" && _spriteMap.CurrentFrameIndex == 5)
            {
                Global.Score += 500;
                Global.EventFourCanKillBoss = false;
                Global.TriggerEnd = true;
                RemoveSelf();
            }
        }//end update
        private string RandomHit()
        {
            switch (Rand.Int(1, 4))//one more than the last case number (so counting the default would be an additional one)
            {
                case 1:
                    return "_hitCar";
                case 2:
                    return "_bossHitCar2";
                case 3:
                    return "_bossHitCar3";
                default:
                    return "_bossHitCar4";
            }
        }

        private void PlayCorrectSpritemap()
        {
            if (Global.BossHealth >= 10)
                Shared.ChangeAnimTo("shootLoop0", _spriteMap);
            else if (Global.BossHealth >= 8 && Global.BossHealth < 10)
            {
                if (!bossHurt1)
                {
                    bossHurt1 = true;
                    SoundManager.PlayRequestedSound("_bossHurt", 2f);
                }
                Shared.ChangeAnimTo("shootLoop1", _spriteMap);
            }
            else if (Global.BossHealth >= 6 && Global.BossHealth < 8)
                Shared.ChangeAnimTo("shootLoop2", _spriteMap);
            else if (Global.BossHealth >= 4 && Global.BossHealth < 6)
            {
                if (!bossHurt2)
                {
                    bossHurt2 = true;
                    SoundManager.PlayRequestedSound("_bossHurt2", 2f);
                }
                Shared.ChangeAnimTo("shootLoop3", _spriteMap);
            }
            else if (Global.BossHealth >= 2 && Global.BossHealth < 4)
                Shared.ChangeAnimTo("shootLoop4", _spriteMap);
            else if (Global.BossHealth <= 0)
            {
                if (!death)
                {
                    death = true;
                    SoundManager.PlayRequestedSound("_bossDeath");
                }
                Shared.ChangeAnimTo("death", _spriteMap);
            }
        }

        public void SeekPlayerMovement()
        {
            if (isSeekingTarget)
            {
                ScanAllTargets();
            }
            else if (!AmICloseEnoughToPlayer())//travel to target
            {
                if (!Global.StuckInLoop)
                    player2aiSpeed += .01f;
                else if (Global.EventFourCanKillBoss)
                    player2aiSpeed = bossFightSpeedMax;
                else
                    player2aiSpeed = player2aiSpeedMax;


                X -= player2aiSpeed * (float)Math.Cos(angleInRadians);
                Y -= player2aiSpeed * (float)Math.Sin(angleInRadians);
                if (Global.EventFourCanKillBoss)
                {
                    if (Timer > timer)
                    {
                        timer += Rand.Int(1, 80);
                        var anyTeslas = GetEntities<Player>();
                        foreach (var item in anyTeslas)
                        {
                            var dist = Util.Distance(this, item);
                            if (dist <= closestDistanceItem)
                            {
                                closestDistanceItem = dist;
                                closestItem = item;
                            }
                        }
                        for (var j = 0; j < 1; j++)
                        {
                            if (X >= Global.PlayerX)
                                Scene.Add(new Rocket(X + 50, Y - 50, closestItem));
                            else
                                Scene.Add(new Rocket(X - 50, Y - 50, closestItem));

                        }
                    }
                }
                if (X >= Global.PlayerX)//this needs to be a range of values for where the player is actually left of. so, there should be a dead zone where the animal doesn't move!
                {
                    if (Global.EventFourCanKillBoss)
                        PlayCorrectSpritemap();
                    else if (Global.StuckInLoop)
                        ChangeAnimTo("lossLoopWalkLeft");
                    else
                        ChangeAnimTo("walkLeft");
                    _spriteMap.FlippedX = false;
                }
                else
                {
                    if (Global.EventFourCanKillBoss)
                        PlayCorrectSpritemap();
                    else if (Global.StuckInLoop)
                        ChangeAnimTo("lossLoopWalkRight");
                    else
                        ChangeAnimTo("walkRight");
                    _spriteMap.FlippedX = true;
                }
            }
            else if (_isCloseEnoughToPlayer)
            {
                if (X >= Global.PlayerX)//this needs to be a range of values for where the player is actually left of. so, there should be a dead zone where the animal doesn't move!
                {
                    if (Global.EventFourCanKillBoss)
                        PlayCorrectSpritemap();
                    else if (Global.StuckInLoop)
                        ChangeAnimTo("lossLoopStandLeft");
                    else
                        ChangeAnimTo("standLeft");
                    _spriteMap.FlippedX = false;
                }
                else
                {
                    if (Global.EventFourCanKillBoss)
                        PlayCorrectSpritemap();
                    else if (Global.StuckInLoop)
                        ChangeAnimTo("lossLoopStandRight");
                    else
                        ChangeAnimTo("standRight");
                    _spriteMap.FlippedX = true;
                }
            }
            if (scanAgain <= 0)
            {
                isSeekingTarget = true;
                closestItem = null;
                closestDistanceItem = 1670;
                scanAgain = ScanTimerMax;
            }
            if (scanAgain > 0)
                scanAgain--;
        }

        private void ScanAllTargets()
        {
            var anyPlayer = GetEntities<Player>();
            foreach (var item in anyPlayer)
            {
                var dist = Util.Distance(this, item);
                if (dist <= closestDistanceItem)
                {
                    closestDistanceItem = dist;
                    closestItem = item;
                }
            }
            if (closestItem != null)
            {
                isSeekingTarget = false;
                GetDirection();
            }
        }

        public void CaptureGiraffes()
        {
            if (isSeekingTarget)
            {
                ScanClosestGiraffe();
            }
            else if (!AmICloseEnoughToPlayer())//travel to target
            {
                player2aiSpeed = player2aiSpeedMax;
                X -= player2aiSpeed * (float)Math.Cos(angleInRadians);
                Y -= player2aiSpeed * (float)Math.Sin(angleInRadians);
                if (X >= closestItem.X)//this needs to be a range of values for where the player is actually left of. so, there should be a dead zone where the animal doesn't move!
                {
                    if (Global.StuckInLoop)
                        ChangeAnimTo("lossLoopWalkLeft");
                    else
                        ChangeAnimTo("walkLeft");
                    _spriteMap.FlippedX = false;
                }
                else
                {
                    if (Global.StuckInLoop)
                        ChangeAnimTo("lossLoopWalkRight");
                    else
                        ChangeAnimTo("walkRight");
                    _spriteMap.FlippedX = true;
                }
            }
            else if (_isCloseEnoughToPlayer)
            {
                if (X >= closestItem.X)//this needs to be a range of values for where the player is actually left of. so, there should be a dead zone where the animal doesn't move!
                {
                    if (Global.StuckInLoop)
                        ChangeAnimTo("lossLoopStandLeft");
                    else
                        ChangeAnimTo("standLeft");
                    _spriteMap.FlippedX = false;
                }
                else
                {
                    if (Global.StuckInLoop)
                        ChangeAnimTo("lossLoopStandRight");
                    else
                        ChangeAnimTo("standRight");
                    _spriteMap.FlippedX = true;
                }
            }
            if (scanAgain <= 0)
            {
                isSeekingTarget = true;
                closestItem = null;
                closestDistanceItem = 1670;
                scanAgain = ScanTimerMax;
            }
            if (scanAgain > 0)
                scanAgain--;
        }

        private void ScanClosestGiraffe()
        {
            var anyGiraffe = GetEntities<WildGiraffe>();
            foreach (var item in anyGiraffe)
            {
                var dist = Util.Distance(this, item);
                if (dist <= closestDistanceItem)
                {
                    closestDistanceItem = dist;
                    closestItem = item;
                }
            }
            if (closestItem != null)
            {
                isSeekingTarget = false;
                GetDirection();
            }
        }

        public void GetDirection()
        {
            var vector1 = new Point((int)closestItem.X, (int)closestItem.Y);
            var vector2 = new Point((int)X, (int)Y);
            angleInRadians = Math.Atan2(vector2.Y - vector1.Y, vector2.X - vector1.X);
            var angleInFrisbees = MathHelper.ToDegrees((float)angleInRadians);
            if (angleInFrisbees < 0)
            {
                angleInFrisbees += 360;
                angleInRadians = MathHelper.ToRadians(angleInFrisbees);
            }
        }

        public bool AmICloseEnoughToPlayer()
        {
            //get the distance to closest item. if close shoot at it!
            var dist = Util.Distance(this, closestItem);
            if (dist <= 20)//hmm
            {
                _isCloseEnoughToPlayer = true;
                return true;
            }
            else
            {
                _isCloseEnoughToPlayer = false;
                return false;
            }
        }

        public void GetRealDirection(int destX, int destY, int sourceX, int sourceY)
        {
            var vector1 = new Point(sourceX, sourceY);
            var vector2 = new Point(destX, destY);
            angleInRadians = Math.Atan2(vector2.Y - vector1.Y, vector2.X - vector1.X);
            var angleInFrisbees = MathHelper.ToDegrees((float)angleInRadians);
            if (angleInFrisbees < 0)
            {
                angleInFrisbees += 360;
                angleInRadians = MathHelper.ToRadians(angleInFrisbees);
            }
        }

        private void GetVelocity(double originalAngle)//these speeds are not variable enough. :/
        {
            var angle = (int)MathHelper.ToDegrees((float)originalAngle);
            //Console.WriteLine("angle: {0}", angle);
            _X = _grappleDragSpeed * (float)Math.Cos(originalAngle);
            _Y = _grappleDragSpeed * (float)Math.Sin(originalAngle);
            velocity = new Vector2(_X, _Y);
        }

        public double GetAngle(float angle)
        {
            return MathHelper.ToRadians(angle);
        }

        public void ChangeAnimTo(string desiredAnimation)
        {
            if (_spriteMap.CurrentAnim != desiredAnimation)
                _spriteMap.Play(desiredAnimation);
        }

        public void StopWalkingAnimations()
        {
            if (!_hMove && !_vMove)
            {
                switch (_spriteMap.CurrentAnim)
                {
                    case ("walkLeft"):
                        _spriteMap.Play("standLeft");
                        break;
                    case ("walkRight"):
                        _spriteMap.Play("standRight");
                        break;
                    case ("walkUp"):
                        _spriteMap.Play("standUp");
                        break;
                    case ("walkDown"):
                        _spriteMap.Play("standDown");
                        break;
                    case ("lossLoopWalkLeft"):
                        _spriteMap.Play("lossLoopStandLeft");
                        break;
                    case ("lossLoopWalkRight"):
                        _spriteMap.Play("lossLoopStandRight");
                        break;
                    case ("lossLoopWalkUp"):
                        _spriteMap.Play("lossLoopStandUp");
                        break;
                    case ("LossLoopWalkDown"):
                        _spriteMap.Play("LossLoopStandDown");
                        break;
                    default:
                        break;
                }
            }
        }

        public void LoadCorrectSpritemap()
        {
            isSpritemapChanged = false;
            if (_spriteMap != null)
                _spriteMap.Clear();
            //if no other spritemaps are needed, this will work fine.
            _spriteMap = null;
            _spriteMap = new Spritemap<string>(Assets.BOSS, 200, 200);
            AddNewSprites();
            _spriteMap.Update();
            _spriteMap.CenterOrigin();
            _spriteMap.Play("standDown");
            Graphic = _spriteMap;
        }

        #region AddSprites
        public void AddNewSprites()
        {
            var hMs = 5f;//horizontal animation speed
            var vMs = 5f;//vertical animation speed
            _spriteMap.Add("standRight", new int[] { 2 }, new float[] { hMs, hMs });
            _spriteMap.Add("standLeft", new int[] { 2 }, new float[] { hMs, hMs });
            _spriteMap.Add("standUp", new int[] { 2 }, new float[] { hMs, hMs });
            _spriteMap.Add("standDown", new int[] { 2 }, new float[] { hMs, hMs });
            _spriteMap.Add("walkRight", new int[] { 2, 3 }, new float[] { hMs, hMs });
            _spriteMap.Add("walkLeft", new int[] { 2, 3 }, new float[] { hMs, hMs });
            _spriteMap.Add("walkUp", new int[] { 2, 3 }, new float[] { vMs, vMs });
            _spriteMap.Add("walkDown", new int[] { 2, 3 }, new float[] { vMs, vMs });
            _spriteMap.Add("lossLoopStandRight", new int[] { 0 }, new float[] { hMs, hMs });
            _spriteMap.Add("lossLoopStandLeft", new int[] { 0 }, new float[] { hMs, hMs });
            _spriteMap.Add("lossLoopStandUp", new int[] { 0 }, new float[] { hMs, hMs });
            _spriteMap.Add("LossLoopStandDown", new int[] { 0 }, new float[] { hMs, hMs });
            _spriteMap.Add("lossLoopWalkRight", new int[] { 0, 1 }, new float[] { hMs, hMs });
            _spriteMap.Add("lossLoopWalkLeft", new int[] { 0, 1 }, new float[] { hMs, hMs });
            _spriteMap.Add("lossLoopWalkUp", new int[] { 0, 1 }, new float[] { hMs, hMs });
            _spriteMap.Add("LossLoopWalkDown", new int[] { 0, 1 }, new float[] { hMs, hMs });
            _spriteMap.Add("shootLoop0", new int[] { 4, 5 }, new float[] { hMs, hMs });
            _spriteMap.Add("shootLoop1", new int[] { 6, 7 }, new float[] { hMs, hMs });
            _spriteMap.Add("shootLoop2", new int[] { 8, 9 }, new float[] { hMs, hMs });
            _spriteMap.Add("shootLoop3", new int[] { 10, 11 }, new float[] { hMs, hMs });
            _spriteMap.Add("shootLoop4", new int[] { 12, 13 }, new float[] { hMs, hMs });
            _spriteMap.Add("death", new int[] { 14, 15, 16, 17, 18, 19 }, new float[] { 17, 17 });
        }
        #endregion

        public override void Render()//mainly for testing colliders :D
        {
            base.Render();
            //Collider.Render(Color.Random);
        }

        public void Destroy()
        {
            RemoveSelf();
        }

    }
}


