using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
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 BloodSplash : RaycastObject, IHaveSizeAndPosition, IRecyclable
    {
        private const float VerticalAcceleration = 0.015f;

        public static readonly Point Size = new Point(24, 48);

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

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

        private IPathfindingService Pathfinding => LazyResolver<IPathfindingService>.Resolve();
        
        private float _speed;
        private float _verticalSpeed;
        private float _fade;

        private Vector2 _direction;

        private RenderedScene _scene;

        public BloodSplash()
        {
            Components.Add(new ActionComponent(OnUpdate));
        }

        public void Recycle()
        {
            Frame = new Point(Random.Next(Sprite.FrameCount.X + 1), 0);

            _speed = 0.1f;
            _verticalSpeed = 0.15f;
            _fade = 1;

            _direction = new Vector2(
                (float)(Random.NextDouble() - 0.5),
                (float)(Random.NextDouble() - 0.5));
            _direction.Normalize();
        }

        public override void Draw(SpriteBatch batch, Rectangle destinationRectangle, Rectangle sourceRectangle, Color color)
            => base.Draw(
                batch, 
                Resize(destinationRectangle), 
                sourceRectangle,
                color * _fade);

        private Rectangle Resize(Rectangle destinationRectangle)
        {
            if (_fade == 1)
                return destinationRectangle;
            var bottom = destinationRectangle.Bottom;
            var height = (int)(destinationRectangle.Height * _fade);
            destinationRectangle.Y = bottom - height;
            destinationRectangle.Height = height;
            return destinationRectangle;
        }

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

            _verticalSpeed -= VerticalAcceleration;
            VerticalPosition += _verticalSpeed;

            if (VerticalPosition < 0)
            {
                VerticalPosition = 0;
                _fade *= 0.9f;
                if (_fade < 0.01f)
                {
                    _scene.RemoveEntity(this);
                }
            }
            else
            {
                var position = Position + _direction * _speed;
                var mapPoint = position.ToPoint();
                if (Pathfinding.CanPassThrough(mapPoint))
                {
                    Position = position;
                    _speed *= 0.9f;
                }
                else
                {
                    _speed = 0;
                }
            }
        }

        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
    }
}