Code/Tileset/Autotile/AutotileBrush.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Sandbox;

namespace SpriteTools;

public class AutotileBrush
{
    public Guid Id { get; set; } = Guid.NewGuid();
    [Property, Placeholder("Autotile Brush")] public string Name { get; set; }
    public AutotileType AutotileType { get; set; } = AutotileType.Bitmask2x2Edge;
    [Hide] public Tile[] Tiles { get; set; }
    [Hide] public int TileCount => (AutotileType == AutotileType.Bitmask3x3Complete) ? 255 : (AutotileType == AutotileType.Bitmask3x3 ? 47 : 15);
    [Hide, JsonIgnore] public TilesetResource Tileset { get; set; }

    public AutotileBrush() : this(AutotileType.Bitmask2x2Edge) { }

    public AutotileBrush(AutotileType type)
    {
        AutotileType = type;

        var tileCount = TileCount;
        Tiles = new Tile[tileCount];
        for (int i = 0; i < tileCount; i++)
        {
            Tiles[i] = new Tile();
        }
    }

    /// <summary>
    /// Set the autotile type for this brush. This will reset all existing tiles in the brush.
    /// </summary>
    /// <param name="autotileType"></param>
    public void SetAutotileType(AutotileType autotileType)
    {
        AutotileType = autotileType;

        var tileCount = TileCount;
        Tiles = new Tile[tileCount];
        for (int i = 0; i < tileCount; i++)
        {
            Tiles[i] = new Tile();
        }
    }

    public TileReference GetTileFromBitmask(int bitmask)
    {
        if (bitmask < 0)
            return null;

        Tile selectedTile = null;

        switch (AutotileType)
        {
            case AutotileType.Bitmask2x2Edge:
                selectedTile = GetTile2x2Edge(bitmask);
                break;
            // case AutotileType.Bitmask2x2Corner:
            //     selectedTile = GetTile2x2Corner(bitmask);
            //     break;
            case AutotileType.Bitmask3x3:
                selectedTile = GetTile3x3Minimal(bitmask);
                break;
            case AutotileType.Bitmask3x3Complete:
                selectedTile = GetTile3x3Complete(bitmask);
                break;
        }

        if (selectedTile is null) return null;
        if ((selectedTile.Tiles?.Count ?? 0) == 0) return null;

        // TODO: Random weights
        int randomIndex = Random.Shared.Int(0, selectedTile.Tiles.Count - 1);
        return selectedTile.Tiles[randomIndex];
    }

    Tile GetTile2x2Edge(int bitmask)
    {
        // return Tiles[0];
        switch (bitmask)
        {
            // Top-Left Corner
            case 208:
            case 209:
            case 211:
            case 212:
            case 213:
            case 217:
            case 221:
            case 240:
            case 241:
            case 243:
            case 244:
            case 245:
                return Tiles[7];
            // Top-Right Corner
            case 104:
            case 105:
            case 108:
            case 109:
            case 110:
            case 124:
            case 125:
            case 232:
            case 233:
            case 236:
            case 237:
            case 238:
                return Tiles[11];
            // Bottom-Left Corner
            case 22:
            case 23:
            case 54:
            case 55:
            case 62:
            case 118:
            case 119:
            case 150:
            case 151:
            case 182:
            case 183:
            case 190:
                return Tiles[13];
            // Bottom-Right Corner
            case 11:
            case 15:
            case 43:
            case 47:
            case 139:
            case 143:
            case 155:
            case 171:
            case 175:
            case 187:
            case 203:
            case 207:
                return Tiles[14];
            // Top
            case 248:
            case 249:
            case 252:
            case 253:
                return Tiles[3];
            // Down
            case 31:
            case 63:
            case 159:
            case 191:
                return Tiles[12];
            // Left
            case 214:
            case 215:
            case 246:
            case 247:
                return Tiles[5];
            // Right
            case 107:
            case 111:
            case 235:
            case 239:
                return Tiles[10];
            // Inner Top-Left
            case 254:
                return Tiles[1];
            // Inner Top-Right
            case 251:
                return Tiles[2];
            // Inner Bottom-Left
            case 223:
                return Tiles[4];
            // Inner Bottom-Right
            case 127:
                return Tiles[8];
            // Inner Diagonal (Top-Left and Bottom-Right)
            case 126:
                return Tiles[9];
            // Inner Diagonal (Top-Right and Bottom-Left)
            case 216:
            case 219:
                return Tiles[6];
            // Inside
            case 255:
                return Tiles[0];
            default: return Tiles[0];
        }
    }

    Tile GetTile2x2Corner(int bitmask)
    {
        switch (bitmask)
        {
            case 0: return Tiles[0];
            case 1: return Tiles[1];
            case 2: return Tiles[2];
            case 3: return Tiles[3];
            case 4: return Tiles[4];
            case 5: return Tiles[5];
            case 6: return Tiles[6];
            case 7: return Tiles[7];
            case 8: return Tiles[8];
            case 9: return Tiles[9];
            case 10: return Tiles[10];
            case 11: return Tiles[11];
            case 12: return Tiles[12];
            case 13: return Tiles[13];
            case 14: return Tiles[14];
            case 15: return Tiles[15];
            default: return null;
        }
    }

    Tile GetTile3x3Minimal(int bitmask)
    {
        switch (bitmask)
        {
            case 0:
            case 1: return Tiles[46];
            case 2: return Tiles[44];
            case 3: return Tiles[44];
            case 4: return Tiles[46];
            case 5: return Tiles[46];
            case 6: return Tiles[44];
            case 7: return Tiles[44];
            case 8: return Tiles[45];
            case 9: return Tiles[45];
            case 10: return Tiles[39];
            case 11: return Tiles[38];
            case 12: return Tiles[45];
            case 13: return Tiles[45];
            case 14: return Tiles[39];
            case 15: return Tiles[38];
            case 16: return Tiles[43];
            case 17: return Tiles[43];
            case 18: return Tiles[41];
            case 19: return Tiles[41];
            case 20: return Tiles[43];
            case 21: return Tiles[43];
            case 22: return Tiles[40];
            case 23: return Tiles[40];
            case 24: return Tiles[33];
            case 25: return Tiles[33];
            case 26: return Tiles[31];
            case 27: return Tiles[30];
            case 28: return Tiles[33];
            case 29: return Tiles[33];
            case 30: return Tiles[29];
            case 31: return Tiles[28];
            case 32: return Tiles[46];
            case 33: return Tiles[46];
            case 34: return Tiles[44];
            case 35: return Tiles[44];
            case 36: return Tiles[46];
            case 37: return Tiles[46];
            case 38: return Tiles[44];
            case 39: return Tiles[44];
            case 40: return Tiles[45];
            case 41: return Tiles[45];
            case 42: return Tiles[39];
            case 43: return Tiles[38];
            case 44: return Tiles[45];
            case 45: return Tiles[45];
            case 46: return Tiles[39];
            case 47: return Tiles[38];
            case 48: return Tiles[43];
            case 49: return Tiles[43];
            case 50: return Tiles[41];
            case 51: return Tiles[41];
            case 52: return Tiles[43];
            case 53: return Tiles[43];
            case 54: return Tiles[40];
            case 55: return Tiles[40];
            case 56: return Tiles[33];
            case 57: return Tiles[33];
            case 58: return Tiles[31];
            case 59: return Tiles[30];
            case 60: return Tiles[33];
            case 61: return Tiles[33];
            case 62: return Tiles[29];
            case 63: return Tiles[28];
            case 64: return Tiles[42];
            case 65: return Tiles[42];
            case 66: return Tiles[32];
            case 67: return Tiles[32];
            case 68: return Tiles[42];
            case 69: return Tiles[42];
            case 70: return Tiles[32];
            case 71: return Tiles[32];
            case 72: return Tiles[37];
            case 73: return Tiles[37];
            case 74: return Tiles[27];
            case 75: return Tiles[25];
            case 76: return Tiles[37];
            case 77: return Tiles[37];
            case 78: return Tiles[27];
            case 79: return Tiles[25];
            case 80: return Tiles[35];
            case 81: return Tiles[35];
            case 82: return Tiles[19];
            case 83: return Tiles[19];
            case 84: return Tiles[35];
            case 85: return Tiles[35];
            case 86: return Tiles[18];
            case 87: return Tiles[18];
            case 88: return Tiles[23];
            case 89: return Tiles[23];
            case 90: return Tiles[15];
            case 91: return Tiles[14];
            case 92: return Tiles[23];
            case 93: return Tiles[23];
            case 94: return Tiles[13];
            case 95: return Tiles[12];
            case 96: return Tiles[42];
            case 97: return Tiles[42];
            case 98: return Tiles[32];
            case 99: return Tiles[32];
            case 100: return Tiles[42];
            case 101: return Tiles[42];
            case 102: return Tiles[32];
            case 103: return Tiles[32];
            case 104: return Tiles[36];
            case 105: return Tiles[36];
            case 106: return Tiles[26];
            case 107: return Tiles[24];
            case 108: return Tiles[36];
            case 109: return Tiles[36];
            case 110: return Tiles[26];
            case 111: return Tiles[24];
            case 112: return Tiles[35];
            case 113: return Tiles[35];
            case 114: return Tiles[19];
            case 115: return Tiles[19];
            case 116: return Tiles[35];
            case 117: return Tiles[35];
            case 118: return Tiles[18];
            case 119: return Tiles[18];
            case 120: return Tiles[21];
            case 121: return Tiles[21];
            case 122: return Tiles[7];
            case 123: return Tiles[6];
            case 124: return Tiles[21];
            case 125: return Tiles[21];
            case 126: return Tiles[5];
            case 127: return Tiles[4];
            case 128: return Tiles[46];
            case 129: return Tiles[46];
            case 130: return Tiles[44];
            case 131: return Tiles[44];
            case 132: return Tiles[46];
            case 133: return Tiles[46];
            case 134: return Tiles[44];
            case 135: return Tiles[44];
            case 136: return Tiles[45];
            case 137: return Tiles[45];
            case 138: return Tiles[39];
            case 139: return Tiles[38];
            case 140: return Tiles[45];
            case 141: return Tiles[45];
            case 142: return Tiles[39];
            case 143: return Tiles[38];
            case 144: return Tiles[43];
            case 145: return Tiles[43];
            case 146: return Tiles[41];
            case 147: return Tiles[41];
            case 148: return Tiles[43];
            case 149: return Tiles[43];
            case 150: return Tiles[40];
            case 151: return Tiles[40];
            case 152: return Tiles[33];
            case 153: return Tiles[33];
            case 154: return Tiles[31];
            case 155: return Tiles[30];
            case 156: return Tiles[33];
            case 157: return Tiles[33];
            case 158: return Tiles[29];
            case 159: return Tiles[28];
            case 160: return Tiles[46];
            case 161: return Tiles[46];
            case 162: return Tiles[44];
            case 163: return Tiles[44];
            case 164: return Tiles[46];
            case 165: return Tiles[46];
            case 166: return Tiles[44];
            case 167: return Tiles[44];
            case 168: return Tiles[45];
            case 169: return Tiles[45];
            case 170: return Tiles[39];
            case 171: return Tiles[38];
            case 172: return Tiles[45];
            case 173: return Tiles[45];
            case 174: return Tiles[39];
            case 175: return Tiles[38];
            case 176: return Tiles[43];
            case 177: return Tiles[43];
            case 178: return Tiles[41];
            case 179: return Tiles[41];
            case 180: return Tiles[43];
            case 181: return Tiles[43];
            case 182: return Tiles[40];
            case 183: return Tiles[40];
            case 184: return Tiles[33];
            case 185: return Tiles[33];
            case 186: return Tiles[31];
            case 187: return Tiles[30];
            case 188: return Tiles[33];
            case 189: return Tiles[33];
            case 190: return Tiles[29];
            case 191: return Tiles[28];
            case 192: return Tiles[42];
            case 193: return Tiles[42];
            case 194: return Tiles[32];
            case 195: return Tiles[32];
            case 196: return Tiles[42];
            case 197: return Tiles[42];
            case 198: return Tiles[32];
            case 199: return Tiles[32];
            case 200: return Tiles[37];
            case 201: return Tiles[37];
            case 202: return Tiles[27];
            case 203: return Tiles[25];
            case 204: return Tiles[37];
            case 205: return Tiles[37];
            case 206: return Tiles[27];
            case 207: return Tiles[25];
            case 208: return Tiles[34];
            case 209: return Tiles[34];
            case 210: return Tiles[17];
            case 211: return Tiles[17];
            case 212: return Tiles[34];
            case 213: return Tiles[34];
            case 214: return Tiles[16];
            case 215: return Tiles[16];
            case 216: return Tiles[22];
            case 217: return Tiles[22];
            case 218: return Tiles[11];
            case 219: return Tiles[10];
            case 220: return Tiles[22];
            case 221: return Tiles[22];
            case 222: return Tiles[9];
            case 223: return Tiles[8];
            case 224: return Tiles[42];
            case 225: return Tiles[42];
            case 226: return Tiles[32];
            case 227: return Tiles[32];
            case 228: return Tiles[42];
            case 229: return Tiles[42];
            case 230: return Tiles[32];
            case 231: return Tiles[32];
            case 232: return Tiles[36];
            case 233: return Tiles[36];
            case 234: return Tiles[26];
            case 235: return Tiles[24];
            case 236: return Tiles[36];
            case 237: return Tiles[36];
            case 238: return Tiles[26];
            case 239: return Tiles[24];
            case 240: return Tiles[34];
            case 241: return Tiles[34];
            case 242: return Tiles[17];
            case 243: return Tiles[17];
            case 244: return Tiles[34];
            case 245: return Tiles[34];
            case 246: return Tiles[16];
            case 247: return Tiles[16];
            case 248: return Tiles[20];
            case 249: return Tiles[20];
            case 250: return Tiles[3];
            case 251: return Tiles[2];
            case 252: return Tiles[20];
            case 253: return Tiles[20];
            case 254: return Tiles[1];
            case 255: return Tiles[0];
            default: return null;
        }
    }

    Tile GetTile3x3Complete(int bitmask)
    {
        if (bitmask < 0 || bitmask >= Tiles.Length)
            return null;
        return Tiles[bitmask];
    }

    public override int GetHashCode()
    {
        int val = 0;

        foreach (var tile in Tiles)
        {
            if (tile?.Tiles is null) continue;
            foreach (var tileRef in tile.Tiles)
            {
                if (tileRef is null) continue;
                val += HashCode.Combine(tileRef.GetTilePosition());
            }
        }

        return HashCode.Combine(Name, AutotileType, val);
    }

    public class Tile
    {
        // [InlineEditor, WideMode(HasLabel = false)]
        [Property] public List<TileReference> Tiles { get; set; }
    }
    public class TileReference
    {
        [Hide] public TilesetResource Tileset { get; set; }
        public Guid Id { get; set; }
        public Vector2Int Position { get; set; }
        public float Weight { get; set; } = 10f;

        public Vector2Int GetTilePosition()
        {
            if (Tileset is null) return -1;
            foreach (var tile in Tileset.Tiles)
            {
                if (tile is null) continue;
                if (tile.Id == Id) return tile.Position;
            }
            return -1;
        }

        public TileReference()
        {
            Id = Guid.NewGuid();
        }

        public TileReference(Guid guid)
        {
            Id = guid;
        }
    }

}