Editor/Tileset/TilesetEditor/Inspector.cs
using Editor;
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;

namespace SpriteTools.TilesetEditor.Inspector;

public class Inspector : Widget
{
    internal MainWindow MainWindow { get; }
    internal int SelectedTab => segmentedControl.SelectedIndex;

    ControlSheet controlSheet;
    ControlSheet selectedTileSheet;
    ControlSheet selectedAutotileSheet;
    internal SegmentedControl segmentedControl;

    internal Button btnRegenerate;
    Button btnDeleteAll;
    WarningBox warningBox;
    ExpandGroup selectedTileGroup;
    ExpandGroup selectedAutotileGroup;
    internal TilesetTileListControl tileList;
    internal AutotileBrushListControl autotileBrushList;

    AutotileType SelectedAutotileType
    {
        get => _selectedAutotileType;
        set
        {
            if (_selectedAutotileType == value) return;

            var brush = autotileBrushList?.SelectedBrush?.Brush;
            if (brush is null) return;

            if ((brush.Tiles?.Length ?? 0) > 0 && brush.Tiles.Any(x => (x?.Tiles?.Count ?? 0) > 0))
            {
                var popup = new PopupWindow(
                    "Change Brush Type?",
                    "Are you sure you want to change the Brush Type?\nThis will remove all existing tiles in the brush.",
                    "Cancel", new Dictionary<string, Action>{
                        {"OK", () => {
                            _selectedAutotileType = value;
                            brush.SetAutotileType(value);
                        }}
                    });

                popup.Show();
            }
            else
            {
                _selectedAutotileType = value;
                brush.SetAutotileType(value);
            }
        }
    }
    AutotileType _selectedAutotileType;

    public Inspector(MainWindow mainWindow) : base(null)
    {
        MainWindow = mainWindow;

        Name = "Inspector";
        WindowTitle = "Inspector";
        SetWindowIcon("manage_search");

        Layout = Layout.Column();
        Layout.Margin = 8;

        controlSheet = new ControlSheet();
        selectedTileSheet = new ControlSheet();
        selectedAutotileSheet = new ControlSheet();

        MinimumWidth = 350f;

        var scroller = new ScrollArea(this);
        scroller.Canvas = new Widget();
        scroller.Canvas.Layout = Layout.Column();
        scroller.Canvas.VerticalSizeMode = SizeMode.CanGrow;
        scroller.Canvas.HorizontalSizeMode = SizeMode.Flexible;

        var importLayout = scroller.Canvas.Layout.Add(Layout.Row());
        importLayout.Margin = new Sandbox.UI.Margin(16, 8, 16, 0);

        segmentedControl = Layout.Add(new SegmentedControl());
        segmentedControl.AddOption("Setup", "auto_fix_high");
        segmentedControl.AddOption("Tiles", "grid_on");
        segmentedControl.AddOption("Autotile Brushes", "brush");
        segmentedControl.OnSelectedChanged = (index) =>
        {
            UpdateControlSheet();
        };

        scroller.Canvas.Layout.Add(controlSheet);

        scroller.Canvas.Layout.AddSpacingCell(8);

        {
            selectedTileGroup = scroller.Canvas.Layout.Add(new ExpandGroup(this));
            selectedTileGroup.Title = "Selected Tile";
            selectedTileGroup.SetOpenState(true);
            var w = new Widget();
            w.Layout = Layout.Column();
            w.VerticalSizeMode = SizeMode.CanGrow;
            w.HorizontalSizeMode = SizeMode.Flexible;
            w.Layout.Add(selectedTileSheet);
            w.Layout.AddSpacingCell(8);
            selectedTileGroup.SetWidget(w);
        }

        {
            selectedAutotileGroup = scroller.Canvas.Layout.Add(new ExpandGroup(this));
            selectedAutotileGroup.Title = "Selected Autotile";
            selectedAutotileGroup.SetOpenState(true);
            var w = new Widget();
            w.Layout = Layout.Column();
            w.VerticalSizeMode = SizeMode.CanGrow;
            w.HorizontalSizeMode = SizeMode.Flexible;
            w.Layout.Add(selectedAutotileSheet);
            w.Layout.AddSpacingCell(8);
            selectedAutotileGroup.SetWidget(w);
        }

        btnRegenerate = scroller.Canvas.Layout.Add(new Button("Regenerate Tiles", icon: "refresh"));
        btnRegenerate.Clicked = MainWindow.GenerateTiles;
        scroller.Canvas.Layout.AddSpacingCell(8);

        btnDeleteAll = scroller.Canvas.Layout.Add(new Button("Delete All Tiles", icon: "delete"));
        btnDeleteAll.Clicked = MainWindow.DeleteAllTiles;
        scroller.Canvas.Layout.AddSpacingCell(8);

        warningBox = scroller.Canvas.Layout.Add(new WarningBox("", this));
        scroller.Canvas.Layout.AddStretchCell();
        Layout.Add(scroller);

        SetSizeMode(SizeMode.Default, SizeMode.CanShrink);

        UpdateControlSheet();
        UpdateSelectedSheet();
        UpdateSelectedAutotileSheet();
    }

    [EditorEvent.Hotload]
    void OnHotload()
    {
        UpdateControlSheet();
        UpdateSelectedSheet();
        UpdateSelectedAutotileSheet();
    }

    internal void UpdateControlSheet()
    {
        controlSheet?.Clear(true);

        var serializedObject = MainWindow.Tileset.GetSerialized();

        serializedObject.OnPropertyChanged += (prop) =>
        {
            if (prop is null) return;
            if (!prop.HasAttribute<PropertyAttribute>()) return;

            var undoName = $"Modify {prop.Name}";

            string buffer = "";
            if (MainWindow.UndoStack.MostRecent is not null)
            {
                if (MainWindow.UndoStack.MostRecent.name == undoName)
                {
                    buffer = MainWindow.UndoStack.MostRecent.undoBuffer;
                    MainWindow.UndoStack.PopMostRecent();
                }
                else
                {
                    buffer = MainWindow.UndoStack.MostRecent.redoBuffer;
                }
            }

            MainWindow.PushUndo(undoName, buffer);
            MainWindow.PushRedo();

            if (prop.Name == "FilePath")
                MainWindow.preview.UpdateTexture(prop.GetValue<string>());
        };

        controlSheet.AddObject(serializedObject, (SerializedProperty prop) =>
        {
            if (segmentedControl.SelectedIndex == 0 && prop.GroupName != "Tileset Setup") return false;
            if (segmentedControl.SelectedIndex > 0 && prop.GroupName == "Tileset Setup") return false;
            if (segmentedControl.SelectedIndex == 1 && (prop.GroupName?.Contains("Autotile") ?? false)) return false;
            if (segmentedControl.SelectedIndex == 2 && !(prop.GroupName?.Contains("Autotile") ?? false)) return false;
            return prop.HasAttribute<PropertyAttribute>() && !prop.HasAttribute<HideAttribute>();
        });

        var setupVisible = segmentedControl.SelectedIndex == 0;
        var hasTiles = (MainWindow?.Tileset?.Tiles?.Count ?? 0) > 0;
        selectedTileGroup.Visible = (segmentedControl.SelectedIndex == 1) && hasTiles;
        selectedAutotileGroup.Visible = (segmentedControl.SelectedIndex == 2) && (MainWindow?.Tileset?.AutotileBrushes?.Count ?? 0) > 0;
        btnRegenerate.Visible = setupVisible;
        btnRegenerate.Text = hasTiles ? "Regenerate Tiles" : "Generate Tiles";
        btnDeleteAll.Visible = setupVisible && hasTiles;
        warningBox.Visible = setupVisible == hasTiles;
        warningBox.Label.Text =
            setupVisible ? "Pressing \"Regenerate Tiles\" will regenerate all tiles in the tileset. This will remove all your existing tiles. You can undo this action at any time before you close the window."
            : "No tiles have been generated. Make sure you visit the Setup tab to slice the sheet accordingly.";
    }

    internal void UpdateSelectedSheet()
    {
        selectedTileSheet?.Clear(true);

        if (MainWindow.SelectedTiles.Count == 0) return;

        MultiSerializedObject objs = new();
        foreach (var tile in (MainWindow?.SelectedTiles ?? new()))
        {
            if (tile is null) continue;
            objs.Add(tile.GetSerialized());
        }
        objs.Rebuild();

        objs.OnPropertyChanged += (prop) =>
        {
            MainWindow.SetDirty();
        };
        selectedTileSheet.AddObject(objs, (SerializedProperty prop) =>
        {
            return !prop.HasAttribute<HideAttribute>() && prop.HasAttribute<PropertyAttribute>();
        });
    }

    internal void UpdateSelectedAutotileSheet()
    {
        selectedAutotileSheet?.Clear(true);

        if (autotileBrushList?.SelectedTile is not null)
        {

            var serializedObject = autotileBrushList.SelectedTile.GetSerialized();
            selectedAutotileSheet.AddObject(serializedObject, (SerializedProperty prop) =>
            {
                return !prop.HasAttribute<HideAttribute>() && prop.HasAttribute<PropertyAttribute>();
            });

            return;
        }

        if (autotileBrushList?.SelectedBrush is null) return;

        _selectedAutotileType = autotileBrushList.SelectedBrush.Brush.AutotileType;

        var serializedBrush = autotileBrushList.SelectedBrush.Brush.GetSerialized();
        selectedAutotileSheet.AddObject(serializedBrush, (SerializedProperty prop) =>
        {
            return !prop.HasAttribute<HideAttribute>() && prop.HasAttribute<PropertyAttribute>();
        });

        selectedAutotileSheet.AddObject(this.GetSerialized(), (SerializedProperty prop) =>
        {
            return prop.Name == "SelectedAutotileType";
        });
    }

}