using Microsoft.Xna.Framework;
using MonoGame.FourEngine.Attributes;
using MonoGame.FourEngine.Raycast;
using RoyT.AStar;

namespace Raycast.Win32.Services
{
    public class PathfindingService : IPathfindingService
    {
        private IRaycastMap _map;
        public IRaycastMap Map
        {
            get => _map;
            set
            {
                _map = value;
                UpdateGrid();
            }
        }

        protected Grid Grid { get; set; }

        public Point NextStep(Point from, Point to)
        {
            Point step;
            var path = Grid.GetPath(Position(from), Position(to), MovementPatterns.LateralOnly);
            step = path.Length > 1
                ? Point(path[1])
                : from;

            return step;
        }

        public void Block(Point point) => Grid.BlockCell(Position(point));

        public void Unblock(Point point) => Grid.UnblockCell(Position(point));

        public bool CanPassThrough(Point point)
        {
            if (point.X < 0 || point.Y < 0 || point.X >= Grid.DimX || point.Y >= Grid.DimY)
                return false;

            var cost = Grid.GetCellCost(Position(point));
            return !float.IsInfinity(cost);
        }

        private void UpdateGrid()
        {
            var map = Map.Layers[0].Data;
            var width = map.GetLength(0);
            var height = map.GetLength(1);
            var grid = new Grid(width, height);

            for (var x = 0; x < width; x++)
                for (var y = 0; y < height; y++)
                    if (map[x, y] > 0) grid.BlockCell(new Position(x, y));
                    else grid.UnblockCell(new Position(x, y));

            Grid = grid;
        }

        private Position Position(Point p) => new Position(p.X, p.Y);
        private Point Point(Position p) => new Point(p.X, p.Y);
    }

    [Service]
    public interface IPathfindingService
    {
        IRaycastMap Map { get; set; }
        Point NextStep(Point from, Point to);

        void Block(Point point);
        void Unblock(Point point);
        bool CanPassThrough(Point point);
    }
}