namespace AcDream.UI.Abstractions.Panels.Settings;
///
/// Chat-related preferences persisted to settings.json. Mixes
/// retail's CharacterOptions2 chat-channel filter bits (Hear*Chat
/// + TimeStamp + FilterLanguage + AppearOffline) with a few visual
/// preferences (font size) that don't have a retail bitfield.
/// See docs/research/named-retail/acclient.h:3451+ for the
/// retail bit values.
///
///
/// Campaign CH slice CH3 (2026-08-09): these five Hear*Chat flags
/// are no longer local-only display filters — they ARE retail's server-side
/// Turbine room membership. The App host seeds this draft from
/// RuntimeCharacterOptionsState.Options2 (already parsed out of
/// PlayerDescription) whenever a fresh description lands, and Save publishes
/// any changed bit through SetSingleCharacterOption (0x0005) —
/// RuntimeSettingsController.SyncChatFromServerOptions /
/// SaveChat. There is no sixth toggle for
/// HearAllegianceChat (CharacterOptions1 0x40000000): retail's own
/// Settings UI does not expose one either — Allegiance chat membership rides
/// allegiance membership, not a standalone preference.
///
///
///
/// N5 (CH3 Opus review, 2026-08-09): the server-backed sync/publish
/// wiring above covers ONLY the five Hear*Chat fields.
/// , , and
/// each correspond to a real retail
/// CharacterOptions2 bit (per the field comments below) but are
/// deliberately NOT wired to SyncChatFromServerOptions or
/// SaveChat's SetSingleCharacterOption publish — they stay
/// local-only display preferences. Do not extend the sync/publish pair to
/// them without a corresponding design decision; today they are read and
/// written from settings.json alone.
///
///
public sealed record ChatSettings(
// CharacterOptions2 (32-bit) channel filters.
bool HearGeneralChat, // 0x100 — General channel
bool HearTradeChat, // 0x200 — Trade channel
bool HearLFGChat, // 0x400 — LFG channel
bool HearRoleplayChat, // 0x800 — RP channel
bool HearSocietyChat, // 0x80000 — Society chat (CD/EW/RB)
bool AppearOffline, // 0x1000 — hide /who status
bool ShowTimestamps, // 0x40 — TimeStamp prefix on chat lines
bool FilterProfanity, // 0x20000 — FilterLanguage (Turbine's profanity filter)
// Visual / UX (no retail bitfield).
float FontSize, // chat panel font, 10..20 pt
// Campaign CH slice CH6b: local-only persistence of the four floating
// chat windows' 64-bit text-type filters
// (AcDream.Core.Chat.ChatWindowState — retail's per-window
// 0x1000007F option, docs/research/2026-08-09-chat-retail-color-table.md
// §4). Retail persists these server-side inside the opaque
// 0x1000008C GameplayOptions blob (window-shell research doc §4.4);
// acdream has no writer for that blob yet, so these fields are the
// interim local store, with default values matching retail's own
// ChatInterface::PostInit @0x004F3DD0 seed exactly. Trailing with
// defaults so no existing positional/named ChatSettings construction
// site needed to change.
ulong ChatWindow1Filter = 0x0000101Cu, // Speech, Tell, Speech_Direct_Send, Emote
ulong ChatWindow2Filter = 0x00040C00u, // Social, Social_Send, Allegiance
ulong ChatWindow3Filter = 0x00080000u, // Fellowship
ulong ChatWindow4Filter = 0x78000000u, // Turbine General/Trade/LFG/Roleplay
// Campaign OP slice OP5: the MAIN chat window's own filter — CH6a/b's
// ChatWindowState.SetFilter already accepted window id 0 as a real write
// (its class doc: "Retail's main window IS user-settable through the
// options page; only the settings UI to drive this is future work") but
// nothing persisted it until the Chat tab's main-window filter block
// shipped. Default matches ChatWindowState.MainWindowDefaultFilter.
ulong ChatWindowMainFilter = 0xFBFFFFFFu,
// Campaign CH slice CH6c: retail's two GLOBAL window-opacity options
// (Option_DefaultOpacity_Property 0x10000080 / Option_ActiveOpacity_Property
// 0x10000081, docs/research/2026-08-09-chat-retail-window-shell.md §3).
// DefaultOpacity applies while a window's descendant does NOT have keyboard
// focus; ActiveOpacity while it does. Always active >= default — enforced by
// ChatOpacityLink at every setter, not by clamping here. acdream applies this
// GLOBALLY to every RetailWindowManager-registered window (register row
// AP-190), where retail scopes it to ChatInterface-derived windows only.
//
// CH6c review fix (2026-08-10): the base ChatInterface ctor's 0.5/1.0 pair
// is retail-correct ONLY for the four floating chat windows —
// gmMainChatUI overrides to 1.0/1.0 (0x004CD0F0), and every other
// RetailWindowManager-registered window (radar, vitals, toolbar, ...) has
// no retail opacity fade at all, so applying 0.5 to them out of the box
// rendered the whole registered UI half-transparent forever, including
// several windows that can never take keyboard focus and so were
// PERMANENTLY stuck at 0.5. DefaultOpacity now ships 1.0, matching
// retail-identical opaque presentation for the 11 non-chat windows and
// the main chat window; the four floating chat windows lose their
// retail 0.5-while-idle fade by default, but the Settings → Chat
// transparency slider remains fully user-settable (AP-190).
float DefaultOpacity = 1.0f,
float ActiveOpacity = 1.0f,
// Campaign OP slice OP6: the Config tab's "UI Options" section —
// UI_ChatFontFace (retail Windows TrueType face name, enum choices
// starting "Arial") and UI_ChatFontSize (index into
// Tiny/Small/Medium/Large/XLarge, gmClient::InitUIPreferences
// @0x0040387b/@0x00403a1a). Deliberately NOT the same field as
// FontSize above: FontSize is acdream's own live 10..20pt render
// knob with no verified index-to-point mapping to retail's five-tier
// enum, and acdream's text rendering has no arbitrary system-font-face
// swap (DAT-baked/bitmap fonts only) — both new fields are honest
// store-only round-trips (register row, OP6).
int ChatFontFace = 2,
int ChatFontSizeIndex = 1)
{
///
/// N4 (CH3 Opus review): matches ACE's ACTUAL
/// CharacterOptions2.Default (0x00948700) stance for the five
/// server-backed Hear*Chat bits — General/Trade/LFG are on, but
/// Roleplay (0x800) and Society (0x80000) are OFF (research
/// doc §5.2). The prior "matching the retail 'all on' stance" comment
/// here was wrong: two of the five synced flags are off by default. The
/// server reseed at login (SyncChatFromServerOptions) remains
/// authoritative regardless of this constant — this is only the
/// pre-login / never-connected starting value.
///
public static ChatSettings Default { get; } = new(
HearGeneralChat: true,
HearTradeChat: true,
HearLFGChat: true,
HearRoleplayChat: false,
HearSocietyChat: false,
AppearOffline: false,
ShowTimestamps: true,
FilterProfanity: true,
FontSize: 12f);
}