'use strict';

const fs = require('fs');
const path = require('path');

const THEMES_DB_REL = 'assets/resources/theme/themes-database.json';
const PALETTES_INDEX_REL = 'assets/resources/map-tiles/palettes/_index.json';
const LEVELS_DB_REL = 'assets/level-data/levels-database.json';

const ENTITY_FIELDS = [
    { key: 'playerFront', label: '角色正面' },
    { key: 'playerBack', label: '角色背面' },
    { key: 'vehicleNorth', label: '载具北 (上)' },
    { key: 'vehicleEast', label: '载具东 (右)' },
    { key: 'vehicleSouth', label: '载具南 (下)' },
    { key: 'vehicleWest', label: '载具西 (左)' },
    { key: 'prop', label: '可拾取物' },
    { key: 'propGround', label: '可拾取物(空地 nProp)' },
];

const TILE_FIELDS = [
    { key: 'Baseblock', label: 'Baseblock（地面）' },
    { key: 'JumpBlock', label: 'JumpBlock（跳跃块）' },
    { key: 'WallBlock', label: 'WallBlock（墙）' },
    { key: 'borderDecor', label: '装饰墙砖（第4块）' },
];

/** Unity GameManager.changeIcon / UIMain 右侧 HUD 按钮 */
const HUD_FIELDS = [
    { key: 'navigation', label: '导航' },
    { key: 'revert', label: '重置' },
    { key: 'speed1', label: '1倍速' },
    { key: 'speed2', label: '2倍速' },
    { key: 'speed4', label: '4倍速' },
    { key: 'zoomIn', label: '放大' },
    { key: 'zoomOut', label: '缩小' },
    { key: 'audioOn', label: '声音开' },
    { key: 'audioOff', label: '声音关' },
];

const CELL_PIXEL = 100;
const HALF_H = CELL_PIXEL * 0.25;

const DEFAULT_PROP_BLOCK_Y_OFFSET = 14;
const DEFAULT_PROP_GROUND_Y_OFFSET = -11;
const DEFAULT_MOVER_EMPTY_CELL_Y_OFFSET = HALF_H;
const DEFAULT_PLAYER_RIDE_Y_OFFSET = HALF_H * 0.88;
const DEFAULT_PLAYER_STAND_Y_OFFSET = 0;

const ENTITY_DISPLAY_BASE = {
    player: { w: 0.68, h: 0.9 },
    vehicle: { w: 0.96, h: 0.88 },
    prop: { w: 0.52, h: 0.69 },
    propGround: { w: 0.52, h: 0.69 },
};

const DEFAULT_ENTITY_DISPLAY = {
    player: { scale: 1 },
    vehicle: { scale: 1 },
    prop: { scale: 1 },
    propGround: { scale: 1 },
    propBlockYOffset: DEFAULT_PROP_BLOCK_Y_OFFSET,
    propGroundYOffset: DEFAULT_PROP_GROUND_Y_OFFSET,
    moverEmptyCellYOffset: DEFAULT_MOVER_EMPTY_CELL_Y_OFFSET,
    playerRideYOffset: DEFAULT_PLAYER_RIDE_Y_OFFSET,
    playerStandYOffset: DEFAULT_PLAYER_STAND_Y_OFFSET,
};

const ENTITY_DISPLAY_FIELDS = [
    { key: 'player', label: '角色' },
    { key: 'vehicle', label: '载具' },
    { key: 'prop', label: '可拾取物' },
    { key: 'propGround', label: '可拾取物(空地)' },
];

const ENTITY_DISPLAY_OFFSET_FIELDS = [
    { key: 'propBlockYOffset', label: '砖块可拾取物 Y 偏移 (px)', fallback: DEFAULT_PROP_BLOCK_Y_OFFSET },
    { key: 'propGroundYOffset', label: '空地可拾取物 Y 偏移 (px)', fallback: DEFAULT_PROP_GROUND_Y_OFFSET },
    { key: 'moverEmptyCellYOffset', label: '空地/载具格 角色 Y 补偿 (px)', fallback: DEFAULT_MOVER_EMPTY_CELL_Y_OFFSET },
    { key: 'playerRideYOffset', label: '骑乘载具 角色 Y 偏移 (px)', fallback: DEFAULT_PLAYER_RIDE_Y_OFFSET },
    { key: 'playerStandYOffset', label: '角色站立 Y 偏移 (px)', fallback: DEFAULT_PLAYER_STAND_Y_OFFSET },
];

function clampScale(v, fallback = 1) {
    const n = typeof v === 'number' ? v : parseFloat(String(v));
    if (Number.isNaN(n)) return fallback;
    return Math.max(0.1, Math.min(2, n));
}

function readScale(raw, base) {
    if (raw && raw.scale != null) return clampScale(raw.scale);
    if (raw?.w != null && raw?.h != null) {
        return clampScale(Math.min(raw.w / base.w, raw.h / base.h));
    }
    if (raw?.w != null) return clampScale(raw.w / base.w);
    if (raw?.h != null) return clampScale(raw.h / base.h);
    return 1;
}

function readYOffset(raw, fallback) {
    const n = typeof raw === 'number' ? raw : parseFloat(String(raw));
    if (Number.isNaN(n)) return fallback;
    return n;
}

function mergeEntityDisplay(raw) {
    const propScale = readScale(raw?.prop, ENTITY_DISPLAY_BASE.prop);
    const out = {
        player: { scale: readScale(raw?.player, ENTITY_DISPLAY_BASE.player) },
        vehicle: { scale: readScale(raw?.vehicle, ENTITY_DISPLAY_BASE.vehicle) },
        prop: { scale: propScale },
        propGround: { scale: readScale(raw?.propGround ?? raw?.prop, ENTITY_DISPLAY_BASE.propGround) },
        propBlockYOffset: readYOffset(raw?.propBlockYOffset, DEFAULT_PROP_BLOCK_Y_OFFSET),
        propGroundYOffset: readYOffset(raw?.propGroundYOffset, DEFAULT_PROP_GROUND_Y_OFFSET),
        moverEmptyCellYOffset: readYOffset(raw?.moverEmptyCellYOffset, DEFAULT_MOVER_EMPTY_CELL_Y_OFFSET),
        playerRideYOffset: readYOffset(raw?.playerRideYOffset, DEFAULT_PLAYER_RIDE_Y_OFFSET),
        playerStandYOffset: readYOffset(raw?.playerStandYOffset, DEFAULT_PLAYER_STAND_Y_OFFSET),
    };
    return out;
}

function entityDisplayCellBoxes(scales) {
    const out = {};
    for (const f of ENTITY_DISPLAY_FIELDS) {
        const s = scales[f.key].scale;
        const base = ENTITY_DISPLAY_BASE[f.key];
        out[f.key] = { w: base.w * s, h: base.h * s };
    }
    return out;
}

function readEntityDisplayFromDb(db) {
    return mergeEntityDisplay(db?.entityDisplay);
}

function readEntityDisplayFromForm(root) {
    if (!root) return mergeEntityDisplay(undefined);
    const get = (id) => {
        const el = root.querySelector(`#${id}`);
        if (!el) return NaN;
        return parseFloat(String(el.value ?? ''));
    };
    const raw = {};
    for (const f of ENTITY_DISPLAY_FIELDS) {
        raw[f.key] = { scale: get(`tc-ed-${f.key}-scale`) };
    }
    for (const f of ENTITY_DISPLAY_OFFSET_FIELDS) {
        raw[f.key] = get(`tc-ed-${f.key}`);
    }
    return mergeEntityDisplay(raw);
}

function writeEntityDisplayToForm(root, entityDisplay) {
    const scales = mergeEntityDisplay(entityDisplay);
    const boxes = entityDisplayCellBoxes(scales);
    const set = (id, val) => {
        const el = root.querySelector(`#${id}`);
        if (el) el.value = String(val);
    };
    const setHint = (id, base, box) => {
        const el = root.querySelector(`#${id}`);
        if (!el) return;
        const bw = Math.round(base.w * CELL_PIXEL);
        const bh = Math.round(base.h * CELL_PIXEL);
        const w = Math.round(box.w * CELL_PIXEL);
        const h = Math.round(box.h * CELL_PIXEL);
        el.textContent = `默认 ${bw}×${bh} → 现 ≈ ${w}×${h} px`;
    };
    for (const f of ENTITY_DISPLAY_FIELDS) {
        const scale = scales[f.key].scale;
        set(`tc-ed-${f.key}-scale`, scale);
        setHint(`tc-ed-${f.key}-px`, ENTITY_DISPLAY_BASE[f.key], boxes[f.key]);
    }
    for (const f of ENTITY_DISPLAY_OFFSET_FIELDS) {
        set(`tc-ed-${f.key}`, scales[f.key]);
    }
}

function bindEntityDisplayInputs(root, onChange) {
    for (const f of ENTITY_DISPLAY_FIELDS) {
        const el = root.querySelector(`#tc-ed-${f.key}-scale`);
        if (el) el.addEventListener('change', () => onChange?.());
    }
    for (const f of ENTITY_DISPLAY_OFFSET_FIELDS) {
        const el = root.querySelector(`#tc-ed-${f.key}`);
        if (el) el.addEventListener('change', () => onChange?.());
    }
}

function themesDbPath(projectPath) {
    return path.join(projectPath, THEMES_DB_REL);
}

function palettesIndexPath(projectPath) {
    return path.join(projectPath, PALETTES_INDEX_REL);
}

function levelsDbPath(projectPath) {
    return path.join(projectPath, LEVELS_DB_REL);
}

function normalizePath(raw) {
    if (!raw) return '';
    let p = String(raw).trim().replace(/\\/g, '/');
    if (p.startsWith('assets/resources/')) p = p.slice('assets/resources/'.length);
    if (p.startsWith('resources/')) p = p.slice('resources/'.length);
    if (p.startsWith('/')) p = p.slice(1);
    if (p.endsWith('.png')) p = p.slice(0, -4);
    return p;
}

function clampRatio(v, fallback) {
    const n = typeof v === 'number' ? v : parseFloat(String(v));
    if (Number.isNaN(n)) return fallback;
    return Math.max(0.1, Math.min(2, n));
}

function readThemesDb(projectPath) {
    const fp = themesDbPath(projectPath);
    if (!fs.existsSync(fp)) {
        return { version: 1, themes: {} };
    }
    return JSON.parse(fs.readFileSync(fp, 'utf8'));
}

function writeThemesDb(projectPath, data) {
    data.updatedAt = new Date().toISOString();
    if (data.entityDisplay && data.themes) {
        for (const id of Object.keys(data.themes)) {
            if (!data.themes[id].entityDisplay) {
                data.themes[id].entityDisplay = mergeEntityDisplay(data.entityDisplay);
            }
        }
        delete data.entityDisplay;
    }
    const fp = themesDbPath(projectPath);
    fs.mkdirSync(path.dirname(fp), { recursive: true });
    fs.writeFileSync(fp, JSON.stringify(data, null, 2), 'utf8');
}

function themeToPaletteEntry(themeId, theme) {
    const tiles = [];
    const t = theme.tiles || {};
    const decorKey = theme.borderDecorKey || 'kuai11';
    const entries = [
        ['Baseblock', t.Baseblock, 'ground'],
        ['JumpBlock', t.JumpBlock, 'ground'],
        ['WallBlock', t.WallBlock, 'border'],
        [decorKey, t.borderDecor, 'border'],
    ];
    let idx = 0;
    for (const [tileKey, texPath, layer] of entries) {
        if (!texPath) continue;
        tiles.push({
            display: tileKey,
            layer,
            tileKey,
            texture: texPath,
            unityIndex: idx++,
        });
    }
    return {
        displayName: theme.displayName || themeId,
        tiles,
    };
}

function themesToPalettesMap(db) {
    const out = {};
    for (const [id, theme] of Object.entries(db.themes || {})) {
        out[id] = themeToPaletteEntry(id, theme);
    }
    return out;
}

function buildThemeLabels(db) {
    const labels = {};
    for (const [id, theme] of Object.entries(db.themes || {})) {
        labels[id] = theme.displayName || id;
    }
    return labels;
}

function syncPalettesIndex(projectPath, db) {
    const payload = { themes: themesToPalettesMap(db) };
    const fp = palettesIndexPath(projectPath);
    fs.mkdirSync(path.dirname(fp), { recursive: true });
    fs.writeFileSync(fp, JSON.stringify(payload, null, 2), 'utf8');
}

function defaultHudForFolder(folder) {
    return {
        navigation: `textures/${folder}/anniu_03`,
        revert: `textures/${folder}/anniu_06`,
        speed1: `textures/${folder}/anniu_08`,
        speed2: `textures/${folder}/anniu_10`,
        speed4: `textures/${folder}/anniu_12`,
        zoomIn: `textures/${folder}/anniu_17`,
        zoomOut: `textures/${folder}/anniu_19`,
        audioOn: `textures/${folder}/anniu_22`,
        audioOff: `textures/${folder}/anniu_21`,
    };
}

function createEmptyTheme(themeId, textureFolder) {
    const folder = textureFolder || themeId;
    return {
        displayName: themeId,
        textureFolder: folder,
        background: '',
        hud: defaultHudForFolder(folder),
        entities: {
            playerFront: `textures/${folder}/player_F`,
            playerBack: `textures/${folder}/player_B`,
            vehicleFront: `textures/${folder}/ship_F`,
            vehicleBack: `textures/${folder}/ship_B`,
            prop: `textures/${folder}/Prop`,
            propGround: `textures/${folder}/nProp`,
        },
        tiles: {
            Baseblock: `textures/${folder}/Baseblock`,
            JumpBlock: `textures/${folder}/JumpBlock`,
            WallBlock: `textures/${folder}/WallBlock`,
            borderDecor: `textures/${folder}/kuai11`,
        },
        borderDecorKey: 'kuai11',
        entityDisplay: { ...DEFAULT_ENTITY_DISPLAY },
    };
}

function countLevelsUsingTheme(projectPath, themeId) {
    const fp = levelsDbPath(projectPath);
    if (!fs.existsSync(fp)) return 0;
    const db = JSON.parse(fs.readFileSync(fp, 'utf8'));
    let n = 0;
    for (const cfg of Object.values(db.levels || {})) {
        if (cfg && cfg.theme === themeId) n += 1;
    }
    return n;
}

function readHudFromForm(root) {
    const get = (id) => root.querySelector(`#${id}`)?.value ?? '';
    const hud = {};
    for (const f of HUD_FIELDS) {
        const v = normalizePath(get(`tc-hud-${f.key}`));
        if (v) hud[f.key] = v;
    }
    const sx = parseFloat(String(get('tc-hud-iconScaleX')));
    const sy = parseFloat(String(get('tc-hud-iconScaleY')));
    if (!Number.isNaN(sx)) hud.iconScaleX = sx;
    if (!Number.isNaN(sy)) hud.iconScaleY = sy;
    return hud;
}

function writeHudToForm(root, hud) {
    const set = (id, val) => {
        const el = root.querySelector(`#${id}`);
        if (el) el.value = val ?? '';
    };
    for (const f of HUD_FIELDS) {
        set(`tc-hud-${f.key}`, hud?.[f.key] || '');
    }
    set('tc-hud-iconScaleX', hud?.iconScaleX ?? 1);
    set('tc-hud-iconScaleY', hud?.iconScaleY ?? 1);
}

function readThemeFromForm(root) {
    const get = (id) => root.querySelector(`#${id}`)?.value ?? '';
    const theme = {
        displayName: get('tc-display-name'),
        textureFolder: get('tc-texture-folder'),
        background: normalizePath(get('tc-background')),
        borderDecorKey: get('tc-border-key') || 'kuai11',
        entities: {},
        tiles: {},
        hud: readHudFromForm(root),
        entityDisplay: readEntityDisplayFromForm(root.querySelector('#tc-entity-display')),
    };
    for (const f of ENTITY_FIELDS) {
        const v = normalizePath(get(`tc-ent-${f.key}`));
        if (v) theme.entities[f.key] = v;
    }
    for (const f of TILE_FIELDS) {
        const v = normalizePath(get(`tc-tile-${f.key}`));
        if (v) theme.tiles[f.key] = v;
    }
    if (!theme.background) delete theme.background;
    return theme;
}

function writeThemeToForm(root, themeId, theme) {
    const set = (id, val) => {
        const el = root.querySelector(`#${id}`);
        if (el) el.value = val ?? '';
    };
    set('tc-theme-id', themeId || '');
    set('tc-display-name', theme?.displayName || '');
    set('tc-texture-folder', theme?.textureFolder || themeId || '');
    set('tc-background', theme?.background || '');
    set('tc-border-key', theme?.borderDecorKey || 'kuai11');
    for (const f of ENTITY_FIELDS) {
        set(`tc-ent-${f.key}`, theme?.entities?.[f.key] || '');
    }
    for (const f of TILE_FIELDS) {
        set(`tc-tile-${f.key}`, theme?.tiles?.[f.key] || '');
    }
    writeHudToForm(root, theme?.hud);
    const edWrap = root.querySelector('#tc-entity-display');
    if (edWrap) writeEntityDisplayToForm(edWrap, theme?.entityDisplay);
}

module.exports = {
    THEMES_DB_REL,
    PALETTES_INDEX_REL,
    ENTITY_FIELDS,
    TILE_FIELDS,
    HUD_FIELDS,
    ENTITY_DISPLAY_FIELDS,
    ENTITY_DISPLAY_OFFSET_FIELDS,
    ENTITY_DISPLAY_BASE,
    DEFAULT_ENTITY_DISPLAY,
    DEFAULT_PROP_BLOCK_Y_OFFSET,
    DEFAULT_PROP_GROUND_Y_OFFSET,
    CELL_PIXEL,
    themesDbPath,
    readThemesDb,
    writeThemesDb,
    themesToPalettesMap,
    buildThemeLabels,
    syncPalettesIndex,
    createEmptyTheme,
    countLevelsUsingTheme,
    readThemeFromForm,
    writeThemeToForm,
    normalizePath,
    mergeEntityDisplay,
    entityDisplayCellBoxes,
    readEntityDisplayFromDb,
    readEntityDisplayFromForm,
    writeEntityDisplayToForm,
    bindEntityDisplayInputs,
};
