using System;
using System.Numerics;
namespace AcDream.App.UI.Layout;
///
/// Generic dat element: draws its active state's media by DrawMode (Normal=tile,
/// Alphablend/Overlay=blended overlay). The fallback renderer for every element type
/// without a dedicated behavioral widget (chrome corners/edges, drag bars, resize grips);
/// faithful because retail's base element render is exactly "stamp the media per draw-mode".
///
///
/// For Plan 1, all observed draw modes produce the same alpha-blended tiled quad — the
/// sprite shader already alpha-blends, so no per-mode branch is needed here. The named
/// constants document the real enum for Plan 2.
///
///
///
/// DrawModeType (DatReaderWriter.Enums), stored as int in to
/// keep this dat-free. See docs/research/2026-06-15-layoutdesc-format.md §6:
/// Undefined=0, Normal=1, Overlay=2, Alphablend=3. There is no Stretch mode.
///
///
///
/// Tiling uses UV-repeat on BOTH axes (Width/tw, Height/th) so vertical
/// chrome edges (e.g. a 5×10 sprite drawn over a 5×48 rect) tile vertically too.
/// sets
/// GL_REPEAT on both S and T, so vertical tiling is always active.
///
///
public sealed class UiDatElement : UiElement
{
// DrawModeType enum values from DatReaderWriter.Enums.
// See docs/research/2026-06-15-layoutdesc-format.md §6.
#pragma warning disable IDE0051 // private constants kept for documentation / Plan 2
private const int DrawUndefined = 0;
private const int DrawNormal = 1;
private const int DrawOverlay = 2;
private const int DrawAlphablend = 3;
#pragma warning restore IDE0051
private readonly ElementInfo _info;
private readonly Func _resolve;
/// Which state name to render. "" = the unnamed DirectState.
/// Falls back to DirectState if the named state is absent.
public string ActiveState { get; set; } = "";
/// Merged for this element.
/// Dat file-id → (GL texture handle, native px width, native px height).
/// Returns (0,0,0) when the texture is not yet uploaded.
public UiDatElement(ElementInfo info, Func resolve)
{
_info = info;
_resolve = resolve;
ClickThrough = true; // generic decoration; behavioral widgets opt back in
// Pick the initial active state: retail applies DefaultState when set; falls back
// to "Normal" when the element has a Normal-state sprite (retail's implicit default
// for stateful elements like tabs and buttons); else the unnamed DirectState ("").
if (!string.IsNullOrEmpty(info.DefaultStateName))
ActiveState = info.DefaultStateName;
else if (info.StateMedia.ContainsKey("Normal"))
ActiveState = "Normal";
// else ActiveState stays "" (DirectState)
}
///
/// Returns the (File, DrawMode) for the current ,
/// falling back to the DirectState ("" key) if the named state is absent.
/// Returns (0, 0) if neither exists.
///
// exposed for unit testing
public (uint File, int DrawMode) ActiveMedia()
=> _info.StateMedia.TryGetValue(ActiveState, out var m) ? m
: _info.StateMedia.TryGetValue("", out var d) ? d
: (0u, 0);
/// Optional click handler. Set by a controller for interactive dat
/// elements (e.g. the chat Send / max-min buttons). Requires
/// = false to receive click events.
public Action? OnClick { get; set; }
public override bool OnEvent(in UiEvent e)
{
if (e.Type == UiEventType.Click && OnClick is not null) { OnClick(); return true; }
return false;
}
/// Optional centered text label drawn over the sprite (e.g. the "Send"
/// button face whose dat sprite is a blank frame). Null = sprite only.
public string? Label { get; set; }
/// Dat font for . Required for the label to draw.
public UiDatFont? LabelFont { get; set; }
/// Label color (default white).
public Vector4 LabelColor { get; set; } = Vector4.One;
protected override void OnDraw(UiRenderContext ctx)
{
var (file, _) = ActiveMedia();
if (file != 0)
{
var (tex, tw, th) = _resolve(file);
if (tex != 0 && tw != 0 && th != 0)
{
// Normal → TILE at native size on both axes (UV-repeat; GL_REPEAT-wrapped UI
// texture), matching ImgTex::TileCSI. Overlay/Alphablend use the same blit (the
// sprite shader already alpha-blends). No Stretch mode exists in DrawModeType.
ctx.DrawSprite(tex, 0, 0, Width, Height, 0, 0, Width / tw, Height / th, Vector4.One);
}
}
// Centered text label over the sprite (retail draws button captions as text;
// their dat sprites are blank frames).
if (Label is { Length: > 0 } label && LabelFont is { } lf)
{
float tx = (Width - lf.MeasureWidth(label)) * 0.5f;
float ty = (Height - lf.LineHeight) * 0.5f;
ctx.DrawStringDat(lf, label, tx, ty, LabelColor);
}
}
}