using Microsoft.Xna.Framework;
using MonoGame.FourEngine;
using MonoGame.FourEngine.Components;
using MonoGame.FourEngine.Interfaces;
using MonoGame.FourEngine.Raycast;
using MonoGame.FourEngine.Resources;
using Raycast.Win32.Scenes;
using Raycast.Win32.Services;
using System;
using static MonoGame.FourEngine.ServiceLocator;

namespace Raycast.Win32.Entities
{
    public class EnemyProjectile : RaycastObject, IHaveSizeAndPosition, IRecyclable
    {
        public const float Radius = 0.12f;
        public const float HurtRadius = 0.4f;
        public const float MinSpeed = 0.4f;

        public static readonly Point Size = new Point(34);

        private static readonly Random Random = new Random(DateTime.UtcNow.Millisecond);

        private readonly Vector2 Corner1 = new Vector2(-Radius, -Radius);
        private readonly Vector2 Corner2 = new Vector2(+Radius, -Radius);
        private readonly Vector2 Corner3 = new Vector2(-Radius, +Radius);
        private readonly Vector2 Corner4 = new Vector2(+Radius, +Radius);

        protected IPathfindingService Pathfinding => LazyResolver<IPathfindingService>.Resolve();
        
        private Vector2 _direction;
        public Vector2 Direction
        {
            get => _direction;
            set => _direction = value;
        }

        protected override SpriteDefinition SpriteDefinition
            => new SpriteDefinition
            {
                FileName = Resources.Sprites.EnemyProjectile,
                Size = Size
            };

        public float _speed;

        private RenderedScene _scene;

        public EnemyProjectile()
        {
            VerticalPosition = 0.45f;
            Components.Add(new ActionComponent(OnUpdate));
        }

        public void Recycle()
        {
            _speed = 0.8f;
        }

        private void OnUpdate(UpdateEventArgs args)
        {
            if (_scene == null) return;

            _speed = (_speed * 3 + MinSpeed) / 4;

            bool CheckWalls(Vector2 from, Vector2 to, int depthLeft = 3)
            {
                if (depthLeft == 0) return true; // I don't know, let's say there's no way if we didn't find it yet

                var fromPoint = from.ToPoint();
                var point = to.ToPoint();

                var distancePoint = point - fromPoint;
                var distance = Math.Abs(distancePoint.X) + Math.Abs(distancePoint.Y);
                if (distance >= 1)
                {
                    var newMove = (from + to) / 2;
                    var recursiveCheck = CheckWalls(from, newMove, depthLeft - 1);
                    if (!recursiveCheck) return false;
                }

                return Pathfinding.CanPassThrough(point);
            }

            // Check collision with player
            if (_scene?.Player != null)
            {
                var distance = Math.Abs((Position - _scene.Player.Position).Length());
                if (distance < Radius + Player.Radius)
                {
                    Explode();
                    return;
                }
            }

            var position = Position + Direction * _speed;

            if (CheckWalls(Position, position + Corner1) &&
                CheckWalls(Position, position + Corner2) &&
                CheckWalls(Position, position + Corner3) &&
                CheckWalls(Position, position + Corner4))
            {
                Position = position;
                if (Random.NextDouble() > 0.7)
                {
                    var smoke = RecyclePool<Smoke>.Get();

                    smoke.Position = Position;

                    _scene.AddEntity(smoke);
                }
            }
            else
            {
                Explode();
            }
        }

        private void Hurt(Player player)
        {
            player.Hurt(8);
        }

        private void Explode()
        {
            var distance = Math.Abs((Position - _scene.Player.Position).Length());
            if (distance < HurtRadius + Player.Radius)
            {
                Hurt(_scene.Player);
            }

            var explosion = RecyclePool<Explosion>.Get();
            explosion.Position = Position;
            _scene.AddEntity(explosion);
            _scene.RemoveEntity(this);
        }

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

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

        #region IHaveSizeAndPosition
        Point IHaveSizeAndPosition.Position
        {
            get => Position.ToPoint();
            set => Position = value.ToVector2();
        }
        int IHaveSizeAndPosition.Width { get; set; }
        int IHaveSizeAndPosition.Height { get; set; }
        #endregion

        #region IHaveReadOnlySizeAndPosition
        Point IHaveReadOnlySizeAndPosition.Position => ((IHaveSizeAndPosition)this).Position;
        int IHaveReadOnlySizeAndPosition.Width => ((IHaveSizeAndPosition)this).Width;
        int IHaveReadOnlySizeAndPosition.Height => ((IHaveSizeAndPosition)this).Height;
        #endregion
    }
}