using System;
using System.IO;
using Decimation.Content.Buffs.Debuffs;
using Decimation.Content.Items.Boss.Arachnus;
using Decimation.Content.Items.Misc.Souls;
using Decimation.Content.Items.Weapons.Arachnus;
using Decimation.Content.Projectiles;
using Decimation.Content.Tiles.ShrineoftheMoltenOne;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace Decimation.Content.NPCs.Arachnus
{
    [AutoloadBossHead]
    internal class Arachnus : ModNPC
    {
        public enum ArachnusMessageType
        {
            RoarSound,
            FlamesSound
        }

        private int _counter;
        private int _counterMax = 1320;
        private float _speed = 2;

        public override void SetStaticDefaults()
        {
            DisplayName.SetDefault("Arachnus");
            Main.npcFrameCount[npc.type] = 4;
        }

        public override void SetDefaults()
        {
            npc.aiStyle = -1;
            npc.lifeMax = 80000;
            npc.damage = 100;
            npc.defense = 25;
            npc.knockBackResist = 0f;
            npc.width = 200;
            npc.height = 200;
            npc.value = 50000;
            npc.npcSlots = 1f;
            npc.boss = true;
            npc.lavaImmune = true;
            npc.noGravity = true;
            npc.noTileCollide = false; // When not enraged
            npc.HitSound = SoundID.NPCHit6;
            npc.DeathSound = SoundID.NPCDeath10;
            music = mod.GetSoundSlot(SoundType.Music, "Sounds/Music/Arachnus");
            bossBag = ModContent.ItemType<ArachnusTreasureBag>();

            npc.lavaImmune = true;
            npc.buffImmune[BuffID.OnFire] = true;
            npc.buffImmune[BuffID.Burning] = true;
        }

        public override void ScaleExpertStats(int numPlayers, float bossLifeScale)
        {
            npc.lifeMax = (int) (npc.lifeMax * 0.625f * bossLifeScale);
            npc.damage = (int) (npc.damage * 0.6f);
            npc.defense = npc.defense + numPlayers * 2;
        }

        private bool CheckDispawn()
        {
            bool playersActive = false;
            bool playersDead = true;

            foreach (Player player in Main.player)
            {
                if (player.active) playersActive = true;
                if (!player.dead) playersDead = false;
            }

            return playersDead || !playersActive;
        }

        private void Move()
        {
            // Rotate to player
            Vector2 moveTo = Main.player[npc.target].Center - npc.Center;
            float angle = (float) Math.Atan2(moveTo.Y, moveTo.X);
            npc.rotation = (float) (angle + Math.PI * 0.5f);

            // Move
            Vector2 move = moveTo;
            float magnitude = (float) Math.Sqrt(move.X * move.X + move.Y * move.Y);
            if (magnitude > _speed) move *= _speed / magnitude;

            float turnResistance = 50f;
            move = (npc.velocity * turnResistance + move) / (turnResistance + 1f);
            magnitude = (float) Math.Sqrt(move.X * move.X + move.Y * move.Y);
            if (magnitude > _speed) move *= _speed / magnitude;

            npc.velocity = npc.ai[1] != 99 ? move : new Vector2(0, 0);
        }

        private bool CheckForShrine()
        {
            bool tooFarFromShrine = true;
            if (_counter % 60 == 0)
            {
                int validBlockCount = 0;
                for (int i = (int) (-50 + npc.Center.X / 16f); i <= (int) (50 + npc.Center.X / 16f); i++)
                for (int j = (int) (-50 + npc.Center.Y / 16f); j <= (int) (50 + npc.Center.Y / 16f); j++)
                    if (i >= 0 && i <= Main.maxTilesX && j >= 0 && j <= Main.maxTilesY)
                        if (Main.tile[i, j].type == ModContent.TileType<ShrineBrick>() ||
                            Main.tile[i, j].type == ModContent.TileType<LockedShrineDoor>() ||
                            Main.tile[i, j].type == ModContent.TileType<ShrineDoorClosed>() ||
                            Main.tile[i, j].type == ModContent.TileType<ShrineDoorOpened>() ||
                            Main.tile[i, j].type == ModContent.TileType<RedHotSpike>() ||
                            Main.tile[i, j].type == ModContent.TileType<ShrineAltar>())
                            validBlockCount++;

                if (validBlockCount < 15)
                    tooFarFromShrine = true;
                else
                    tooFarFromShrine = false;
            }
            else
            {
                return npc.ai[2] == 1;
            }

            return tooFarFromShrine;
        }

        private void CheckEnraged()
        {
            npc.ai[2] = !Main.player[npc.target].ZoneUnderworldHeight ||
                        !Collision.CanHit(npc.Center, 0, 0, Main.player[npc.target].Center, 0, 0) || CheckForShrine()
                ? 1
                : 0;
        }

        public override void AI()
        {
            if (CheckDispawn())
            {
                npc.velocity = new Vector2(0, 10f);
                npc.noTileCollide = true;
                if (npc.timeLeft > 10) npc.timeLeft = 10;
            }

            npc.TargetClosest();

            CheckEnraged();

            // Normal ai
            if (npc.ai[0] == 0)
            {
                float mouthX = (float) (npc.height / 2 * Math.Cos(npc.rotation - Math.PI * 0.5f)) + npc.Center.X;
                float mouthY = (float) (npc.height / 2 * Math.Sin(npc.rotation - Math.PI * 0.5f)) + npc.Center.Y;

                //Counter
                if (npc.life <= npc.lifeMax / 2) _counterMax = 1500;
                if (_counter >= _counterMax && Main.netMode != 1)
                {
                    _counter = 0;
                    npc.netUpdate = true;
                }

                // Fireballs
                if (_counter <= 600)
                {
                    npc.ai[1] = 0;
                }
                else if (_counter > 600 && _counter < 800)
                {
                    npc.ai[1] = 98;
                }
                // Blast of Heat
                else if (_counter >= 800 && _counter <= 1100)
                {
                    npc.ai[1] = 1;
                }
                // Increase speed
                else if (_counter > 1320 && _counter <= 1500 ||
                         Main.expertMode && _counter > 600 && _counter < 800 && npc.life <= npc.lifeMax / 4)
                {
                    if (_counter == 1321)
                    {
                        if (Main.netMode == NetmodeID.SinglePlayer)
                            Main.PlaySound(SoundID.Roar, (int) npc.position.X, (int) npc.position.Y, 0);
                        if (Main.netMode == NetmodeID.Server) GetPacket(ArachnusMessageType.RoarSound).Send();
                    }

                    npc.ai[1] = 2;
                }
                else
                {
                    npc.ai[1] = 99;
                }

                if (_counter % 40 == 0 && npc.ai[1] == 0 && Main.netMode != 1)
                {
                    float speedX = (float) (6 * Math.Cos(npc.rotation - Math.PI * 0.5f)) * 2;
                    float speedY = (float) (6 * Math.Sin(npc.rotation - Math.PI * 0.5f)) * 2;
                    Projectile.NewProjectile(new Vector2(mouthX, mouthY), new Vector2(speedX, speedY),
                        ModContent.ProjectileType<ArachnusFireball>(), 30, 0f);
                }
                else if (_counter % 5 == 0 && npc.ai[1] == 1)
                {
                    if (Main.netMode != 1)
                    {
                        float speedX = (float) (7 * Math.Cos(npc.rotation - Math.PI * 0.5f));
                        float speedY = (float) (7 * Math.Sin(npc.rotation - Math.PI * 0.5f));
                        Projectile.NewProjectile(new Vector2(mouthX, mouthY), new Vector2(speedX, speedY),
                            npc.ai[2] == 1
                                ? ModContent.ProjectileType<BlastofShadowFlame>()
                                : ModContent.ProjectileType<BlastofHeat>(), 30, 0f);
                    }

                    Main.PlaySound(SoundID.Item34, npc.position);
                    if (Main.netMode == 2)
                        GetPacket(ArachnusMessageType.FlamesSound).Send();
                }
                else if (npc.ai[1] == 2)
                {
                    _speed = 20f;
                    if (Main.expertMode)
                    {
                        _speed = (npc.lifeMax - npc.life) / 500;
                        if (npc.ai[2] == 1)
                            _speed += 20;
                    }
                    else if (npc.ai[2] == 1)
                    {
                        _speed = 40f;
                    }
                }

                if (npc.ai[1] != 2) _speed = 2f;

                // Enraged
                if (npc.ai[2] == 1)
                {
                    npc.noTileCollide = true;
                    Dust.NewDust(npc.position, npc.width, npc.height, DustID.Shadowflame);

                    if (npc.ai[1] != 2)
                    {
                        _speed = 6f;
                        npc.defense = 300;
                    }
                }

                _counter++;
            }

            Move();
        }

        public override void SendExtraAI(BinaryWriter writer)
        {
            writer.Write(_counter);
            writer.Write(_counterMax);
            writer.Write(_speed);
        }

        public override void ReceiveExtraAI(BinaryReader reader)
        {
            _counter = reader.ReadInt32();
            _counterMax = reader.ReadInt32();
            _speed = reader.ReadSingle();
        }

        private ModPacket GetPacket(ArachnusMessageType type)
        {
            ModPacket packet = mod.GetPacket();
            packet.Write((byte) DecimationModMessageType.Arachnus);
            packet.Write(npc.whoAmI);
            packet.Write((byte) type);
            return packet;
        }

        public void HandlePacket(BinaryReader reader)
        {
            ArachnusMessageType type = (ArachnusMessageType) reader.ReadByte();
            switch (type)
            {
                case ArachnusMessageType.RoarSound:
                    Main.PlaySound(SoundID.Roar, (int) npc.position.X, (int) npc.position.Y, 0);
                    break;
                case ArachnusMessageType.FlamesSound:
                    Main.PlaySound(SoundID.Item34, npc.position);
                    break;
            }
        }

        public override void OnHitPlayer(Player target, int damage, bool crit)
        {
            if (npc.ai[1] == 2 && Main.expertMode) target.AddBuff(ModContent.BuffType<Hyperthermic>(), 900);

            base.OnHitPlayer(target, damage, crit);
        }

        public override void FindFrame(int frameHeight)
        {
            npc.frameCounter += 3f;
            if (npc.frameCounter >= 40)
                npc.frameCounter = 0;
            npc.frame.Y = (int) npc.frameCounter / 10 * frameHeight;
        }

        public override void NPCLoot()
        {
            Item.NewItem(npc.Center, ModContent.ItemType<SoulofSpite>(), Main.rand.Next(15, 31));

            if (!Main.expertMode)
            {
                int rand = Main.rand.Next(3);
                if (rand == 0)
                    Item.NewItem(npc.Center, ModContent.ItemType<ChainStynger>());
                else if (rand == 1)
                    Item.NewItem(npc.Center, ModContent.ItemType<GlaiveWeaver>());
                else if (rand == 2)
                    Item.NewItem(npc.Center, ModContent.ItemType<Infernolizer>());
            }
            else
            {
                npc.DropBossBags();
            }
        }

        public override void BossLoot(ref string name, ref int potionType)
        {
            name = "Arachnus";
            // Maybe better
            potionType = ItemID.SuperHealingPotion;

            DecimationWorld.downedArachnus = true;
        }

        public override bool? DrawHealthBar(byte hbPosition, ref float scale, ref Vector2 position)
        {
            scale = 1.5f;
            return null;
        }

        public override bool PreDraw(SpriteBatch spriteBatch, Color drawColor)
        {
            Vector2 frameSize = new Vector2(298, 318);
            Texture2D texture = mod.GetTexture("Content/NPCs/Arachnus/Arachnus");

            spriteBatch.Draw
            (
                texture,
                new Vector2
                (
                    npc.position.X - Main.screenPosition.X + frameSize.X * 0.34f,
                    npc.position.Y - Main.screenPosition.Y + frameSize.Y * 0.31f
                ),
                npc.frame,
                drawColor,
                npc.rotation,
                frameSize * 0.5f,
                npc.scale,
                SpriteEffects.None,
                0f
            );
            return false;
        }
    }
}