using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Graphics;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using RTCF = Robust.Client.Graphics.RenderTargetColorFormat;

namespace Robust.Client.UserInterface;

[GenerateTypedNameReferences]
internal sealed partial class DevWindowTabRenderTargets : Control
{
    [Dependency] private IClydeInternal _clyde = default!;
    [Dependency] private ILocalizationManager _loc = default!;

    private readonly Control[] _gridHeader;

    private readonly StyleBoxFlat _styleAltRow = new() { BackgroundColor = Color.FromHex("#222") };

#if TOOLS
    private readonly HashSet<IRenderTarget> _copyTextures = new();
#endif // TOOLS

    public DevWindowTabRenderTargets()
    {
        RobustXamlLoader.Load(this);
        IoCManager.InjectDependencies(this);

        _gridHeader = Table.Children.ToArray();

        ReloadButton.OnPressed += _ => Reload();
        FilterEdit.OnTextChanged += _ => Reload();
    }

    protected override void VisibilityChanged(bool newVisible)
    {
        base.VisibilityChanged(newVisible);

        if (newVisible)
        {
            Reload();
        }
        else
        {
            // Clear to release memory when tab not visible.
            Clear();
        }
    }

    protected override void ExitedTree()
    {
        base.ExitedTree();

        Clear();
    }

    private void Clear()
    {
        Table.RemoveAllChildren();

#if TOOLS
        foreach (var copiedTexture in _copyTextures)
        {
            copiedTexture.Dispose();
        }

        _copyTextures.Clear();
#endif
    }

    private void Reload()
    {
        Table.RemoveAllChildren();

        foreach (var header in _gridHeader)
        {
            Table.AddChild(header);
        }

        var totalVram = 0L;

        var even = true;

        var rts = _clyde.GetLoadedRenderTextures().OrderBy(x => x.Item1.Handle.Value).ToArray();
        foreach (var (instance, loaded) in rts)
        {
#if TOOLS
            if (_copyTextures.Contains(instance))
                continue;
#endif // TOOLS

            if (!string.IsNullOrWhiteSpace(FilterEdit.Text))
            {
                if (loaded.Name is not { } name)
                    continue;

                if (!name.Contains(FilterEdit.Text, StringComparison.CurrentCultureIgnoreCase))
                    continue;
            }


            AddColumnText(instance.Handle.Value.ToString());

            if (loaded.Name != null)
                AddColumnText(loaded.Name);
            else if (loaded.WindowId != WindowId.Invalid)
                AddColumnText(loaded.WindowId.ToString());
            else
                AddColumnText(_loc.GetString("dev-window-tab-render-targets-value-null"));

            AddColumnText(loaded.Size.ToString());
            var type = loaded.ColorFormat.ToString();
            if (loaded.DepthStencilHandle != default)
                type += "+DS";
            AddColumnText(type);
            AddColumnText(ByteHelpers.FormatBytes(loaded.MemoryPressure));

            // Disable texture thumbnails outside TOOLS.
            // Avoid people cheating by using devwindow to see through walls. Barely.
#if TOOLS
            if (instance is IRenderTexture renderTexture)
            {
                var clone = CloneTexture(renderTexture.Texture, loaded.ColorFormat);

                _copyTextures.Add(clone);

                AddColumn(new TextureRect
                {
                    Texture = clone.Texture,
                    Stretch = TextureRect.StretchMode.KeepAspect
                });
            }
            else
#endif // TOOLS
            {
                AddColumnText(_loc.GetString("dev-window-tab-render-targets-value-not-available"));
            }

            totalVram += loaded.MemoryPressure;

            even = !even;
        }

        TotalLabel.Text = Loc.GetString(
            "dev-window-tab-render-targets-summary",
            ("vram", ByteHelpers.FormatBytes(totalVram)));

        return;

        void AddColumnText(string text)
        {
            var richTextLabel = new RichTextLabel { Margin = new Thickness(4) };
            richTextLabel.SetMessage(text, defaultColor: Color.White);
            AddColumn(richTextLabel);
        }

        void AddColumn(Control control)
        {
            control.VerticalAlignment = VAlignment.Center;

            if (even)
            {
                control = new PanelContainer
                {
                    PanelOverride = _styleAltRow,
                    Children = { control },
                };
            }
            else
            {
                // Wrapping control so we can use SetHeight.
                control = new Control
                {
                    Children = { control },
                };
            }

            control.SetHeight = 50;

            Table.AddChild(control);
        }
    }

#if TOOLS
    private IRenderTexture CloneTexture(Texture texture, RTCF colorFormat)
    {
        var thumbnailSize = GetThumbnailSize(texture.Size);

        var rt = _clyde.CreateRenderTarget(
            thumbnailSize,
            new RenderTargetFormatParameters
            {
                ColorFormat = colorFormat,
                HasDepthStencil = false,
            },
            new TextureSampleParameters
            {
                Filter = true,
            },
            name: $"{nameof(DevWindowTabRenderTargets)}-clone");

        _clyde.RenderNow(rt,
            handle =>
            {
                handle.DrawingHandleScreen.DrawTextureRect(texture, UIBox2.FromDimensions(Vector2.Zero, thumbnailSize));
            });

        return rt;
    }

    private static Vector2i GetThumbnailSize(Vector2i textureSize)
    {
        const int maxHeight = 50;
        const int maxWidth = 100;

        var (w, h) = (Vector2)textureSize;

        if (h > maxHeight)
        {
            w /= h / maxHeight;
            h = maxHeight;
        }

        if (w > maxWidth)
        {
            h /= w / maxWidth;
            w = maxWidth;
        }

        return new Vector2i((int)w, (int)h);
    }
#endif // TOOLS
}
