using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Unity.VisualScripting;
using UnityEngine;

namespace DungeonGenerator
{
    public class DungeonMap
    {
        private int _width;
        private int _height;
        private List<Room> _monsterRooms = new List<Room>();
        private List<Room> _entranceRooms = new List<Room>();
        private List<Room> _normalRooms = new List<Room>();
        private Room _bossRoom;
        private HashSet<Point> _unoccupiedPoints = new HashSet<Point>();
        private HashSet<Point> _occupiedPoints = new HashSet<Point>();

        public DungeonMap(int width, int height)
        {
            _width = width;
            _height = height;
            _unoccupiedPoints.AddRange(Enumerable.Range(0, _width)
                .SelectMany(x => Enumerable.Range(0, _height)
                    .Select(y => new Point(x,y))));
        }

        public void AddRoom(Room room)
        {
            switch (room.TypeOfRoom)
            {
                case RoomType.Monster:
                    _monsterRooms.Add(room);
                    break;
                case RoomType.Entrance:
                    _entranceRooms.Add(room);
                    break;
                case RoomType.Normal:
                    _normalRooms.Add(room);
                    break;
                case RoomType.Boss:
                    _bossRoom = room;
                    break;
                default:
                    return;
            }

            AddPointsToOccupied(room.GetPointsInRoom());
        }

        public void AddRooms(List<Room> rooms)
        {
            if (rooms.Count == 0) return;
            
            switch (rooms[0].TypeOfRoom)
            {
                case RoomType.Monster:
                    _monsterRooms.AddRange(rooms);
                    break;
                case RoomType.Entrance:
                    _entranceRooms.AddRange(rooms);
                    break;
                case RoomType.Normal:
                    _normalRooms.AddRange(rooms);
                    break;
                default:
                    return;
            }
            
            AddPointsToOccupied(rooms.SelectMany(room => room.GetPointsInRoom()).ToList());
        }

        public List<Room> GetMonsterRooms()
        {
            return _monsterRooms;
        }

        public Room GetBossRoom()
        {
            return _bossRoom;
        }

        public List<Room> GetEntranceRooms()
        {
            return _entranceRooms;
        }

        public List<Room> GetNormalRooms()
        {
            return _normalRooms;
        }

        public HashSet<Point> GetUnoccupiedPoints()
        {
            return new HashSet<Point>(_unoccupiedPoints);
        }

        public HashSet<Point> GetOccupiedPoints()
        {
            return new HashSet<Point>(_occupiedPoints);
        }
        
        public void PrintMap()
        {
            List<Point> monsterRoomPoints = _monsterRooms.SelectMany(room => room.GetPointsInRoom()).ToList();
            List<Point> entranceRoomPoints = _entranceRooms.SelectMany(room => room.GetPointsInRoom()).ToList();
            List<Point> normalRoomPoints = _normalRooms.SelectMany(room => room.GetPointsInRoom()).ToList();
            char[,] mapMatrix = new Char[_width, _height];
            for (int x = 0; x < _width; x++)
            {
                for (int y = 0; y < _height; y++)
                {
                    Point point = new Point(x, y);
                    if (entranceRoomPoints.Contains(point))
                    {
                        mapMatrix[x, y] = '*';
                    }
                    else if (monsterRoomPoints.Contains(point))
                    {
                        mapMatrix[x, y] = 'X';
                    }
                    else if (_bossRoom.GetPointsInRoom().Contains(point))
                    {
                        mapMatrix[x, y] = 'B';
                    }
                    else if (normalRoomPoints.Contains(point))
                    {
                        mapMatrix[x, y] = 'N';
                    }
                    else
                    {
                        mapMatrix[x, y] = '-';
                    }
                }
            }

            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < mapMatrix.GetLength(1); j++)
            {
                for (int i = 0; i < mapMatrix.GetLength(0); i++)
                {
                    sb.Append(mapMatrix[i, j] + " ");
                }
                sb.Append(Environment.NewLine);
            }

            Debug.Log($"{DateTime.Now}{Environment.NewLine}" +
                      $"X Length: {_width}{Environment.NewLine}" +
                      $"Y Length: {_height}{Environment.NewLine}" +
                      $"Monster Rooms: {_monsterRooms.Count}{Environment.NewLine}" +
                      sb.ToString());
        }

        private void AddPointsToOccupied(List<Point> points)
        {
            _occupiedPoints.AddRange(points);
            _unoccupiedPoints.ExceptWith(points);
        }

        private void RemovePointsFromOccupied(List<Point> points)
        {
            _occupiedPoints.ExceptWith(points);
            _unoccupiedPoints.AddRange(points);
        }
    }
}