using Microsoft.Xna.Framework;
using MonoGame.FourEngine;
using MonoGame.FourEngine.Components;
using MonoGame.FourEngine.Resources;
using Raycast.Win32.Scenes;
using System;

namespace Raycast.Win32.Entities
{
    public class PlayerGun : Entity
    {
        private const int MaxShake = 20;
        private const float MaxRecoil = 100;
        private const float MaxStrafeOffset = 300;
        private const float ReloadTime = 400;

        private readonly Point GunPosition = new Point(168, 360);
        private readonly Point GunFireOffset = new Point(28, -56);

        public bool CanShoot { get; private set; }

        private float ShakeIntensity => 2 * Math.Abs(_player.CurrentSpeed.Length()) / Player.MaxMoveSpeed;

        private SpriteComponent _gun;
        private SpriteComponent _fire;

        private Player _player;

        private Point _gunShake;
        private int _shakePosition;
        private bool _shakeDirection;

        private Vector2 _gunRecoil;

        private double _msToShoot;

        private RenderedScene _scene;

        private SfxComponent _sfx;

        public PlayerGun(Player player)
        {
            _player = player;

            Components.Add(
                _gun = new SpriteComponent(
                    new SpriteDefinition
                    {
                        FileName = Resources.Sprites.Gun,
                        Size = new Point(456, 260)
                    })
                {
                    Layer = int.MaxValue,
                    Position = GunPosition
                });
            Components.Add(
                _fire = new SpriteComponent(
                    new SpriteDefinition
                    {
                        FileName = Resources.Sprites.GunFire,
                        Size = new Point(400, 256)
                    })
                {
                    Layer = _gun.Layer - 1
                });
            Components.Add(new ActionComponent(Update));
            Components.Add(_sfx = new SfxComponent(Resources.Sfx.RocketShoot));

            MessageCenter.PlayerShoot += OnPlayerShoot;
        }

        private void OnPlayerShoot(object sender, EventArgs e)
        {
            if (CanShoot && _scene != null)
            {
                var direction = _player.AimedAt == null
                    ? _player.Direction
                    : _player.AutoAimDirection;

                var ball = RecyclePool<Rocket>.Get();

                ball.Position = _player.Position + _player.Direction / 2;
                ball.Direction = direction;

                _scene.AddEntity(ball);

                CanShoot = false;
                _gunRecoil = new Vector2(0, MaxRecoil);
                _msToShoot = ReloadTime;

                _sfx.Play();
            }
        }

        private void Update(UpdateEventArgs args)
        {
            if (!CanShoot)
            {
                if (_msToShoot > args.DeltaTime.TotalMilliseconds)
                {
                    _msToShoot -= args.DeltaTime.TotalMilliseconds;
                }
                else
                {
                    CanShoot = true;
                }
            }

            if (_shakeDirection)
            {
                _shakePosition++;
                if (_shakePosition == MaxShake) _shakeDirection = !_shakeDirection;
            }
            else
            {
                _shakePosition--;
                if (_shakePosition == -MaxShake) _shakeDirection = !_shakeDirection;
            }
            
            var shakeVector = new Vector2(_shakePosition, -Math.Abs(_shakePosition / 4)) * ShakeIntensity;
            shakeVector -= new Vector2(_player.CurrentSpeed.X * MaxStrafeOffset, 0);
            var smoothedShake = (_gunShake.ToVector2() * 3 + shakeVector) / 4;
            _gunShake = smoothedShake.ToPoint();

            _gun.Position = GunPosition + _gunShake + _gunRecoil.ToPoint();
            _fire.Position = _gun.Position + GunFireOffset;
            if (CanShoot)
            {
                _fire.Color = Color.Transparent;
            }
            else
            {
                var percent = 1 - (_msToShoot / ReloadTime);
                var frameY = 0;
                if (percent > 0.25) frameY = 1;
                if (percent > 0.50) frameY = 2;
                if (percent > 0.75) frameY = 3;
                var fade = (float)(1 - percent) + 0.1f;
                _fire.Color = Color.White * fade;
                _fire.Frame = new Point(0, frameY);
            }

            _gunRecoil *= 0.8f;
        }

        public override void OnAttach(Scene scene)
        {
            _scene = scene as RenderedScene;
        }

        public override void OnDetach(Scene scene)
        {
            _scene = null;
        }
    }
}