#if TOOLS
using Godot;
using Godot.Collections;

namespace Mr.BrickAdventures.Tools.CaLevelGenerator;

public static class TilemapPainter
{
    public static void Paint(
        TileMapLayer layer,
        bool[,] grid,
        CaGeneratorSettings settings,
        EditorUndoRedoManager undoRedo)
    {
        var before = layer.TileMapData;
        ApplyGrid(layer, grid, settings);
        var after = layer.TileMapData;
        RegisterUndo(layer, before, after, undoRedo, "CA Generate");
    }

    public static void Smooth(
        TileMapLayer layer,
        CaGeneratorSettings settings,
        EditorUndoRedoManager undoRedo)
    {
        var grid = ReadGrid(layer, settings.Offset, new Vector2I(settings.Width, settings.Height));
        grid = CaGenerator.Smooth(grid);
        var before = layer.TileMapData;
        ApplyGrid(layer, grid, settings);
        var after = layer.TileMapData;
        RegisterUndo(layer, before, after, undoRedo, "CA Smooth");
    }

    public static void Clear(
        TileMapLayer layer,
        CaGeneratorSettings settings,
        EditorUndoRedoManager undoRedo)
    {
        var before = layer.TileMapData;
        for (int x = 0; x < settings.Width; x++)
            for (int y = 0; y < settings.Height; y++)
                layer.EraseCell(settings.Offset + new Vector2I(x, y));
        var after = layer.TileMapData;
        RegisterUndo(layer, before, after, undoRedo, "CA Clear");
    }

    private static bool[,] ReadGrid(TileMapLayer layer, Vector2I offset, Vector2I size)
    {
        var grid = new bool[size.X, size.Y];
        for (int x = 0; x < size.X; x++)
            for (int y = 0; y < size.Y; y++)
                grid[x, y] = layer.GetCellSourceId(offset + new Vector2I(x, y)) != -1;
        return grid;
    }

    private static void ApplyGrid(TileMapLayer layer, bool[,] grid, CaGeneratorSettings settings)
    {
        int w = grid.GetLength(0), h = grid.GetLength(1);

        for (int x = 0; x < w; x++)
            for (int y = 0; y < h; y++)
                layer.EraseCell(settings.Offset + new Vector2I(x, y));

        if (settings.TerrainSet >= 0)
        {
            var solidCells = new Array<Vector2I>();
            for (int x = 0; x < w; x++)
                for (int y = 0; y < h; y++)
                    if (grid[x, y])
                        solidCells.Add(settings.Offset + new Vector2I(x, y));

            if (solidCells.Count > 0)
                layer.SetCellsTerrainConnect(solidCells, settings.TerrainSet, settings.Terrain);
        }
        else
        {
            for (int x = 0; x < w; x++)
                for (int y = 0; y < h; y++)
                    if (grid[x, y])
                        layer.SetCell(
                            settings.Offset + new Vector2I(x, y),
                            settings.SourceId,
                            settings.AtlasCoords);
        }
    }

    private static void RegisterUndo(
        TileMapLayer layer,
        byte[] before,
        byte[] after,
        EditorUndoRedoManager undoRedo,
        string actionName)
    {
        undoRedo.CreateAction(actionName);
        undoRedo.AddUndoProperty(layer, "tile_map_data", before);
        undoRedo.AddDoProperty(layer, "tile_map_data", after);
        undoRedo.CommitAction(false);
    }
}
#endif
