using AlienAttack.MonoGame.Things.Bullets;
using AlienAttack.MonoGame.Things.Enemies;
using AlienAttack.MonoGame.Things.Items;
using AlienAttack.MonoGame.Things.Muzzles;
using AlienAttack.MonoGame.Things.Weapons;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;

namespace AlienAttack.MonoGame.Things;

public enum PlayerHorizontalMoveState
{
    None,
    Left,
    Right
}

[Flags]
public enum MoveFlag
{
    None = 0,
    Left = 1,
    Right = 2,
    Up = 4,
    Down = 8,
    Boost = 16
}

public record DrawState
{
    public int Frame { get; init; }
    public SpriteEffects SpriteEffects { get; init; }
}

public class Player : MoveableSprite
{
    protected PlayerHorizontalMoveState MoveState = PlayerHorizontalMoveState.None;
    protected MoveFlag MoveFlags;
    protected ulong MoveThreshold = 0;
    protected ICollection<IWeapon> ActiveWeapons = [];

    protected DrawState DrawState;

    protected int CurrentExhaustFrame = 1;
    protected int MaxExhaustFrames = 6;
    protected int ExhaustAnimationThreshold = 30;
    protected int CurrentExhaustAnimationThreshold = 10;
    protected int CurrentExhaustDirection = 1;

    public int Health { get; protected set; }
    public int Shield { get; protected set; }

    public IReadOnlyList<Muzzle> Muzzles { get; protected set; } =
    [
        new Muzzle(MuzzleId.Center, new Vector2(31.5f, 1f)),
        new Muzzle(MuzzleId.Left,   new Vector2(15f, 27f)),
        new Muzzle(MuzzleId.Right,  new Vector2(47f, 27f)),
    ];

    public Player(int x, int y) : base(x, y)
    {
        Origin = new(32, 32);

        Muzzles =
        [
            new Muzzle(MuzzleId.Center, new Vector2(31.5f, 1f)),
            new Muzzle(MuzzleId.Left,   new Vector2(15f, 27f)),
            new Muzzle(MuzzleId.Right,  new Vector2(47f, 27f)),
        ];

        BoundBox = new Rectangle(0, 0, 64, 64);
        //ActiveWeapons.Add(new Minigun());
        //ActiveWeapons.Add(new MinigunTripleSpreadFast());
        //ActiveWeapons.Add(new MinigunSpread());
        Health = 100;
        Shield = 100;

        DrawState = new()
        {
            Frame = 1,
            SpriteEffects = SpriteEffects.None
        };

        // Weapon 1
        WeaponDef weaponDef = new()
        {
            Name = "Weapon 1",
            Bullet = BulletDefinitions.LaserLarge,
            Pattern = ShotPatterns.TripleSpread,
            FireSfxKey = ""
        };

        WeaponState weasponState = new()
        {
            FireThreshold = 15,
            SpeedMultiplier = 1f,
            DamageBonus = 0
        };

        ConfigurableWeapon configurableWeapon = new(weaponDef, weasponState);

        // Weapon 2
        //WeaponDef weaponDef2 = new()
        //{
        //    Name = "Weapon 2",
        //    Bullet = BulletDefinitions.MinigunSmall,
        //    Pattern = ShotPatterns.Single,
        //    FireSfxKey = ""
        //};

        //WeaponState weasponState2 = new()
        //{
        //    FireThreshold = 20,
        //    SpeedMultiplier = 2f,
        //    DamageBonus = 0
        //};

        //ConfigurableWeapon configurableWeapon2 = new(weaponDef2, weasponState2);

        ActiveWeapons.Add(configurableWeapon);
        //ActiveWeapons.Add(configurableWeapon2);
    }

    public Vector2 GetMuzzleLocal(MuzzleId muzzle)
    {
        switch (muzzle)
        {
            case MuzzleId.Center:
                return Muzzles[0].LocalPx;
            case MuzzleId.Left:
                return Muzzles[1].LocalPx;
            case MuzzleId.Right:
                return Muzzles[2].LocalPx;
        }

        return new();
    }

    //Texture2D texture = Game
    public override void Draw(SpriteDrawArgs args)
    {
        //DrawExhaust(args);

        string frameNumber = MoveState == PlayerHorizontalMoveState.None ? "01" :
            MoveThreshold < 30 ? "02" : "03";
        SpriteEffects spriteEffects = MoveState == PlayerHorizontalMoveState.Right ? SpriteEffects.FlipHorizontally : SpriteEffects.None;

        Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\PlayerRed_Frame_{frameNumber}");
        //args.SpriteBatch.Draw(texture, Position, DrawColor);
        args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, Origin, 1, spriteEffects, 1);

        base.Draw(args);
    }

    private void DrawExhaust(SpriteDrawArgs args)
    {
        Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\Exhaust_Frame_0{CurrentExhaustFrame}_png_processed");
        args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, new Vector2(0, 0), 1, SpriteEffects.None, 1);
    }

    private string GetPlayerTextureName()
    {
        switch (MoveState)
        {
            case PlayerHorizontalMoveState.Left:
                return "PlayerRed_Frame_02";
            case PlayerHorizontalMoveState.Right:
                return "PlayerRed_Frame_03";
            case PlayerHorizontalMoveState.None:
            default:
                return "PlayerRed_Frame_01";
        }
    }

    public override void Update(SpriteUpdateContext context)
    {
        base.Update(context);

        //UpdateExhaustAnimationThreshold();
        CheckMove(context);
        CheckFire(context);

        //base.Update(context);
    }

    private void UpdateExhaustAnimationThreshold()
    {
        if (CurrentExhaustAnimationThreshold > 0)
        {
            CurrentExhaustAnimationThreshold--;
        }
        else
        {
            if (CurrentExhaustFrame == MaxExhaustFrames)
            {
                CurrentExhaustDirection = -1;
            }
            else if (CurrentExhaustFrame == 1)
            {
                CurrentExhaustDirection = 1;
            }

            CurrentExhaustFrame += CurrentExhaustDirection;

            CurrentExhaustAnimationThreshold = ExhaustAnimationThreshold;
        }
    }

    private void CheckMove(SpriteUpdateContext context)
    {
        UpdateMoveFlag();

        if (MoveFlags.HasFlag(MoveFlag.Up))
        {
            //YPosition -= 4;
            YVelocity = -4;
        }
        else if (MoveFlags.HasFlag(MoveFlag.Down))
        {
            //YPosition += 4;
            YVelocity = 4;
        }
        else
        {
            YVelocity = 0;
        }

        if (MoveFlags.HasFlag(MoveFlag.Left))
        {
            //XPosition -= 4;
            XVelocity = -4;

            if (MoveState != PlayerHorizontalMoveState.Left)
            {
                MoveThreshold = 0;
            }
            else
            {
                MoveThreshold++;

                if (MoveThreshold > 30)
                {
                    MoveThreshold = 30;
                }
            }

            MoveState = PlayerHorizontalMoveState.Left;
        }
        else if (MoveFlags.HasFlag(MoveFlag.Right))
        {
            //XPosition += 4;
            XVelocity = 4;

            if (MoveState != PlayerHorizontalMoveState.Right)
            {
                MoveThreshold = 0;
            }
            else
            {
                MoveThreshold++;

                if (MoveThreshold > 30)
                {
                    MoveThreshold = 30;
                }
            }

            MoveState = PlayerHorizontalMoveState.Right;
        }
        else
        {
            XVelocity = 0;
            MoveState = PlayerHorizontalMoveState.None;
            MoveThreshold = 0;
        }

        if (MoveFlags.HasFlag(MoveFlag.Boost))
        {
            XVelocity *= 1.5f;
        }

        if (XPosition < 0)
        {
            XPosition = 0;
        }

        if (XPosition + BoundBox.Width > context.ViewTransform.ScreenWidth)
        {
            XPosition = context.ViewTransform.ScreenWidth - BoundBox.Width;
        }

        if (YPosition < 0)
        {
            YPosition = 0;
        }

        if (YPosition + BoundBox.Height > context.ViewTransform.ScreenHeight)
        {
            YPosition = context.ViewTransform.ScreenHeight - BoundBox.Height;
        }
    }

    private void UpdateMoveFlag()
    {
        MoveFlags = MoveFlag.None;

        KeyboardState keyState = Keyboard.GetState();
        GamePadState gamepadState = GamePad.GetState(0);

        if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W) || gamepadState.ThumbSticks.Left.Y > 0)
        {
            MoveFlags |= MoveFlag.Up;
        }
        else if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S) || gamepadState.ThumbSticks.Left.Y < 0)
        {
            MoveFlags |= MoveFlag.Down;
        }

        if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A) || gamepadState.ThumbSticks.Left.X < 0)
        {
            MoveFlags |= MoveFlag.Left;
        }
        else if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D) || gamepadState.ThumbSticks.Left.X > 0)
        {
            MoveFlags |= MoveFlag.Right;
        }

        if (gamepadState.Buttons.RightShoulder == ButtonState.Pressed)
        {
            MoveFlags |= MoveFlag.Boost;
        }
    }

    private void CheckFire(SpriteUpdateContext context)
    {
        foreach (IWeapon weapon in ActiveWeapons)
        {
            weapon.UpdateFireThreshold();
        }

        if (IsFireButtonPressed() == false)
            return;

        foreach (IWeapon weapon in ActiveWeapons)
        {
            weapon.TryFire(this, context);
        }
    }

    private static bool IsFireButtonPressed()
    {
        KeyboardState keyState = Keyboard.GetState();
        MouseState mouseState = Mouse.GetState();
        GamePadState gamePadState = GamePad.GetState(0);

        return keyState.IsKeyDown(Keys.Space) || mouseState.LeftButton == ButtonState.Pressed || gamePadState.Buttons.A == ButtonState.Pressed;
    }

    private void TakeDamage(int amount)
    {
        if (Shield >= amount)
        {
            TakeShieldDamage(amount);
            return;
        }

        if (Shield > 0)
        {
            int remainder = amount - Shield;

            TakeShieldDamage(Shield);
            TakeHealthDamage(remainder);

            return;
        }

        TakeHealthDamage(amount);
    }

    private void GiveShield(int amount)
    {
        Shield = Math.Clamp(Shield + amount, 0, 100);
    }

    private void TakeShieldDamage(int amount)
    {
        Shield = Math.Clamp(Shield - amount, 0, 100);
    }

    private void GiveHealth(int amount)
    {
        Health = Math.Clamp(Health + amount, 0, 100);
    }

    private void TakeHealthDamage(int amount)
    {
        Health = Math.Clamp(Health - amount, 0, 100);
    }

    public override void OnCollision(SpriteCollisionContext context)
    {
        if (context.Sprite is Bullet bullet && bullet.Owner is not Player)
        {
            TakeDamage(bullet.Damage);
        }

        if (context.Sprite is EnemyShip enemyShip)
        {
            TakeDamage(enemyShip.CrashDamage);
        }

        if (context.Sprite is Shields)
        {
            //SCIEnrg_Shield Activate_01_SFRMS_SCIWPNS

            //int number = context.Random.Next(1, 6);

            //SoundEffect soundEffect = context.Content.Load<SoundEffect>(@$"Sfx\Shield\SCIEnrg_Shield Activate_0{number}_SFRMS_SCIWPNS");
            //soundEffect.Play(0.85f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0);

            //context.AudioManager.PlayPickup(PickupKind.Shield);

            GiveShield(20);
        }

        if (context.Sprite is Health)
        {
            GiveHealth(10);
        }
    }
}