using System.Collections.Generic;
using UnityEngine;

namespace Editor.GON.StageEditor.Baker
{
    public static class StageBuilderHelpers
    {
        public static void ResetTransform(Transform transform)
        {
            // transform.rotation = Quaternion.identity;
            transform.localScale = Vector3.one;
        }
        
        public static List<RectInt> BuildRectanglesFromCellsBest(HashSet<Vector2Int> cells)
        {
            var result = new List<RectInt>();
            var remaining = new HashSet<Vector2Int>(cells);

            while (remaining.Count > 0)
            {
                var rect = FindLargestRectGlobal(remaining);
                result.Add(rect);
                
                RemoveRectCells(rect, remaining);
            }
            return result;
        }
        
        public static List<RectInt> BuildRectanglesFromCells(HashSet<Vector2Int> cells)
        {
            var result = new List<RectInt>();
            var remaining = new HashSet<Vector2Int>(cells);

            while (remaining.Count > 0)
            {
                var start = FindTopLeftMostCell(remaining);

                var rect = FindBestRectFromStart(start, remaining);
                result.Add(rect);

                RemoveRectCells(rect, remaining);
            }

            return result;
        }
        
        private static RectInt FindLargestRectGlobal(HashSet<Vector2Int> remaining)
        {
            var best = new RectInt();
            var bestArea = 0;

            foreach (var cell in remaining)
            {
                var rect = FindBestRectFromStart(cell, remaining);
                var area = rect.width * rect.height;

                if (area > bestArea)
                {
                    best = rect;
                    bestArea = area;
                }
            }

            return best;
        }
        
        private static RectInt FindBestRectFromStart(Vector2Int start, HashSet<Vector2Int> remaining)
        {
            var bestWidth = 1;
            var bestHeight = 1;
            var bestArea = 1;

            var currentMinWidth = int.MaxValue;
            var height = 0;

            while (true)
            {
                var rowWidth = FindRowWidth(start.x, start.y + height, remaining);

                if (rowWidth == 0)
                {
                    break;
                }

                currentMinWidth = Mathf.Min(currentMinWidth, rowWidth);
                height++;

                var area = currentMinWidth * height;

                if (area > bestArea)
                {
                    bestArea = area;
                    bestWidth = currentMinWidth;
                    bestHeight = height;
                }
            }

            return new RectInt(start.x, start.y, bestWidth, bestHeight);
        }

        private static int FindRowWidth(int x, int y, HashSet<Vector2Int> remaining)
        {
            var width = 0;

            while (remaining.Contains(new Vector2Int(x + width, y)))
            {
                width++;
            }

            return width;
        }
        
        private static Vector2Int FindTopLeftMostCell(HashSet<Vector2Int> cells)
        {
            var found = false;
            var best = Vector2Int.zero;

            foreach (var cell in cells)
            {
                if (!found)
                {
                    best = cell;
                    found = true;
                    continue;
                }

                if (cell.y < best.y)
                {
                    best = cell;
                    continue;
                }

                if (cell.y == best.y && cell.x < best.x)
                {
                    best = cell;
                }
            }

            return best;
        }
        
        private static void RemoveRectCells(RectInt rect, HashSet<Vector2Int> remaining)
        {
            for (var y = rect.yMin; y < rect.yMax; y++)
            {
                for (var x = rect.xMin; x < rect.xMax; x++)
                {
                    remaining.Remove(new Vector2Int(x, y));
                }
            }
        }
    }
}