using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.UIElements;
using Random = System.Random;

namespace DungeonGenerator
{
    public class EvenDisperser
    {
        private List<Room> _samples = new List<Room>(){new Room(
            RoomType.Normal, 
            1,
            1,
            new Point(1000, 1000))};
        private HashSet<Point> _availablePoints;
        
        public EvenDisperser(int xLength, int yLength, HashSet<Point> availablePoints)
        {
            _availablePoints = availablePoints;
        }

        private HashSet<Point> GenerateAvailablePoints(int xLength, int yLength, List<Point> excludedPoints)
        {
            HashSet<Point> availablePoints = new HashSet<Point>();
            for (var x = 0; x < xLength; x++)
            {
                for (var y = 0; y < yLength; y++)
                {
                    if (!excludedPoints.Contains(new Point(x, y)))
                    {
                        availablePoints.Add(new Point(x, y));
                    }
                }
            }

            return availablePoints;
        }
        
        public Room GenerateAndPlaceRoom(int xLength, int yLength, RoomType roomType)
        {
            int numCandidates = 100; // Increasing improves results but greatly effects performance.
            Random rnd = new Random();
            Room bestCandidate = new Room(roomType, xLength, yLength, new Point(Int32.MaxValue, Int32.MaxValue));
            int bestDistance = 0;
            for (var i = 0; i < numCandidates; i++)
            {
                var candidate = new Room(
                    roomType, xLength, yLength, _availablePoints.ToList()[rnd.Next(0, _availablePoints.Count)]);
                var distance = candidate.GetDistanceToRoom(FindClosestRoom(_samples, candidate));
                if (distance > bestDistance
                    && candidate.GetPointsInRoom().All(room => _availablePoints.Contains(room)))
                {
                    _availablePoints.ExceptWith(candidate.GetPointsInRoom());
                    bestCandidate = candidate;
                    bestDistance = distance;
                }
            }
            
            _samples.Add(bestCandidate);
            return bestCandidate;
        }
    
        private Room FindClosestRoom(List<Room> options, Room reference)
        {
            Room closest = options[0];
            foreach (var option in options)
            {
                if (reference.GetDistanceToRoom(option) < reference.GetDistanceToRoom(closest))
                {
                    closest = option;
                }
            }

            return closest;
        }
    }
}