import { getSpriteRows, normalizeImageRecordForSave, normalizeImagesPayloadForSave, normalizeTileRecordForSave, type JsonObject, type JsonValue, } from "../editorCore"; export type GraphicRole = "tile" | "sprite" | "other"; export function normalizeGraphicRoles(value: unknown): string[] { if (!Array.isArray(value)) { return []; } return Array.from(new Set( value .map((entry) => String(entry || "").trim().toLowerCase()) .filter((entry) => entry === "tile" || entry === "sprite"), )); } export function hydrateImageRecordRows(entry: JsonObject | null | undefined): JsonObject | null { if (!entry || typeof entry !== "object" || Array.isArray(entry)) { return null; } return { ...entry, rows: getSpriteRows(entry), }; } export function getImageRecordFromPayload( imagesPayload: JsonObject | null | undefined, recordId: string, ): JsonObject | null { const normalizedId = String(recordId || "").trim(); if (!normalizedId) { return null; } const records = Array.isArray(imagesPayload?.images) ? imagesPayload.images : []; const matchedEntry = (records.find((entry) => ( entry && typeof entry === "object" && !Array.isArray(entry) && String((entry as JsonObject).id || "").trim() === normalizedId )) as JsonObject | undefined) || null; return hydrateImageRecordRows(matchedEntry); } export function buildImageRecordFromTileRecord( record: JsonObject, existingRecord?: JsonObject | null, cloneValue: (value: T) => T = structuredClone, ): JsonObject { const existing = existingRecord && typeof existingRecord === "object" && !Array.isArray(existingRecord) ? existingRecord : {}; const existingRoles = normalizeGraphicRoles(existing.roles); return normalizeImageRecordForSave({ ...cloneValue(existing), id: String(record?.id || existing.id || "").trim(), name: String(record?.name || existing.name || "").trim(), description: String(record?.description || existing.description || "").trim(), width: Math.max(1, Number(record?.width) || Number(existing.width) || 16), height: Math.max(1, Number(record?.height) || Number(existing.height) || 16), pixelScale: Math.max(1, Number(record?.pixelScale) || Number(existing.pixelScale) || 1), opacity: Math.max(0, Math.min(1, Number(record?.opacity ?? existing.opacity ?? 1))), rows: Array.isArray(record?.rows) ? record.rows.map((row) => String(row || "")) : getSpriteRows(existing), tags: cloneValue(record?.tags) || cloneValue(existing.tags) || [], roles: Array.from(new Set([...existingRoles, "tile"])), tileSymbol: String(record?.tileSymbol || record?.symbol || existing.tileSymbol || "").trim().charAt(0), }); } export function buildImageRecordFromSpriteRecord( record: JsonObject, graphicRole: GraphicRole, existingRecord?: JsonObject | null, cloneValue: (value: T) => T = structuredClone, ): JsonObject { const existing = existingRecord && typeof existingRecord === "object" && !Array.isArray(existingRecord) ? existingRecord : {}; const existingRoles = normalizeGraphicRoles(existing.roles); const wantsSpriteRole = graphicRole !== "other"; const nextRoles = wantsSpriteRole ? Array.from(new Set([...existingRoles, "sprite"])) : existingRoles.filter((entry) => entry !== "sprite"); return normalizeImageRecordForSave({ ...cloneValue(existing), id: String(record?.id || existing.id || "").trim(), name: String(record?.name || existing.name || "").trim(), description: String(record?.description || existing.description || "").trim(), width: Math.max(1, Number(record?.width) || Number(existing.width) || 16), height: Math.max(1, Number(record?.height) || Number(existing.height) || 16), pixelScale: Math.max(1, Number(record?.pixelScale) || Number(existing.pixelScale) || 1), opacity: Math.max(0, Math.min(1, Number(record?.opacity ?? existing.opacity ?? 1))), rows: Array.isArray(record?.rows) ? record.rows.map((row) => String(row || "")) : getSpriteRows(existing), tags: cloneValue(record?.tags) || cloneValue(existing.tags) || [], roles: nextRoles, tileSymbol: String(existing.tileSymbol || record?.tileSymbol || record?.symbol || "").trim().charAt(0), }); } export function normalizeImagesPayloadSnapshot( payload: JsonObject | null | undefined, cloneValue: (value: T) => T = structuredClone, ): JsonObject { const normalized = (normalizeImagesPayloadForSave(cloneValue(payload || { schemaVersion: 1, images: [] }) as JsonValue) as JsonObject) || { schemaVersion: 1, images: [], }; const records = Array.isArray(normalized.images) ? normalized.images : []; return { ...normalized, images: records.map((entry) => { const hydratedEntry = hydrateImageRecordRows( entry && typeof entry === "object" && !Array.isArray(entry) ? entry as JsonObject : null, ); return hydratedEntry || entry; }), }; } export function buildTileRecordFromImageRecord(entry: JsonObject, cloneValue: (value: T) => T = structuredClone): JsonObject { return normalizeTileRecordForSave({ id: String(entry.id || "").trim(), symbol: String(entry.tileSymbol || entry.symbol || "").trim().charAt(0), name: String(entry.name || "").trim(), description: String(entry.description || "").trim(), width: Number(entry.width) || 16, height: Number(entry.height) || 16, pixelScale: Number(entry.pixelScale) || 1, opacity: Number(entry.opacity ?? 1), rows: getSpriteRows(entry), tags: cloneValue(entry.tags) || [], }); }