import React from "react";
import {Button, InputNumber, message, Space, Switch, Tooltip} from "antd";
import {QuestionCircleOutlined} from "@ant-design/icons";
import axios from "axios";
import enhance from "../core/enhance.tsx";
import {col_style, row_style, setting_style} from "../Global.tsx";

export type Cesium_Graphics_Config = {
    // 显示 Cesium FPS/帧耗时诊断面板，不改变画质。
    debugShowFramesPerSecond: boolean
    // 限制 Cesium 渲染循环目标帧率，降低上限可减少 GPU 占用。
    targetFrameRate: number
    // 使用浏览器推荐分辨率，避免高 DPI 屏幕按物理像素渲染。
    useBrowserRecommendedResolution: boolean
    // WebGL 内部渲染分辨率缩放，越低越省 GPU 但越模糊。
    resolutionScale: number
    // MSAA 多重采样数量，1 表示关闭几何抗锯齿。
    msaaSamples: number
    // FXAA 后处理抗锯齿，开销低但可能让文字和细线变软。
    fxaa: boolean
    // 场景阴影开关，大量模型或地形开启后 GPU 开销明显。
    shadows: boolean
    // 地球光照开关，开启后地表按光源方向产生明暗。
    enableLighting: boolean
    // 真实日照模式，使用太阳方向、系统时间和动态大气表达昼夜状态。
    solarLighting: boolean
    // 真实日照模式下的太阳光强度，只影响视觉明暗。
    solarLightIntensity: number
    // 地球瓦片屏幕空间误差，值越大越快但地面细节越粗。
    maximumScreenSpaceError: number
    // 仅 3D 模式加载 Terrarium 真实地形，2D/2.5D 不加载。
    terrainEnabledIn3D: boolean
    // 地形垂直起伏倍率，只改变视觉起伏，不增加高程精度。
    terrainExaggeration: number
    // 限制 Terrarium 地形最高请求层级，减少无效子瓦片请求。
    terrainLimitMaximumLevel: boolean
    // 启用层级限制时允许请求的最高地形层级。
    terrainMaximumLevel: number
    // 已解码地形高度瓦片缓存数量，越大越占内存但回看更快。
    terrainCacheTiles: number
}
export const default_cesium_graphics_config: Cesium_Graphics_Config = {
    debugShowFramesPerSecond: true,
    targetFrameRate: 30,
    useBrowserRecommendedResolution: true,
    resolutionScale: 0.7,
    msaaSamples: 1,
    fxaa: false,
    shadows: false,
    enableLighting: false,
    solarLighting: false,
    solarLightIntensity: 1.5,
    maximumScreenSpaceError: 4,
    terrainEnabledIn3D: false,
    terrainExaggeration: 1.0,
    terrainLimitMaximumLevel: true,
    terrainMaximumLevel: 15,
    terrainCacheTiles: 128
};
export const cesium_graphics_config_event = "ecap_cesium_graphics_config_changed";
type Number_Config_Key = "targetFrameRate" | "resolutionScale" | "msaaSamples" | "solarLightIntensity" | "maximumScreenSpaceError" | "terrainExaggeration" | "terrainMaximumLevel" | "terrainCacheTiles"
type Boolean_Config_Key = Exclude<keyof Cesium_Graphics_Config, Number_Config_Key>
const graphics_presets: Record<"low" | "medium" | "high", Cesium_Graphics_Config> = {
    low: {...default_cesium_graphics_config},
    medium: {
        debugShowFramesPerSecond: true,
        targetFrameRate: 45,
        useBrowserRecommendedResolution: true,
        resolutionScale: 0.85,
        msaaSamples: 1,
        fxaa: true,
        shadows: false,
        enableLighting: false,
        solarLighting: false,
        solarLightIntensity: 1.5,
        maximumScreenSpaceError: 3,
        terrainEnabledIn3D: true,
        terrainExaggeration: 1.0,
        terrainLimitMaximumLevel: true,
        terrainMaximumLevel: 15,
        terrainCacheTiles: 128
    },
    high: {
        debugShowFramesPerSecond: true,
        targetFrameRate: 60,
        useBrowserRecommendedResolution: false,
        resolutionScale: 1.0,
        msaaSamples: 4,
        fxaa: true,
        shadows: false,
        enableLighting: true,
        solarLighting: true,
        solarLightIntensity: 1.5,
        maximumScreenSpaceError: 2,
        terrainEnabledIn3D: true,
        terrainExaggeration: 1.0,
        terrainLimitMaximumLevel: true,
        terrainMaximumLevel: 17,
        terrainCacheTiles: 256
    }
};
const graphics_help: Record<keyof Cesium_Graphics_Config, string[]> = {
    debugShowFramesPerSecond: ["显示 FPS、每帧耗时和诊断信息。", "只用于判断卡顿，基本不影响画质。"],
    targetFrameRate: ["限制 Cesium 渲染循环的目标帧率。", "越高越流畅，GPU 占用越高；集显建议 30。"],
    useBrowserRecommendedResolution: ["开启后按 CSS 像素渲染，忽略高 DPI 物理像素倍率。", "高 DPI 屏幕上通常能显著降低集显压力。"],
    resolutionScale: ["调整 WebGL 内部渲染分辨率。", "0.7 约等于渲染 49% 像素；越低越快但更模糊。"],
    msaaSamples: ["几何边缘多重采样抗锯齿。", "1 表示关闭；数值越高边缘越平滑，GPU 开销越大。"],
    fxaa: ["屏幕空间后处理抗锯齿。", "开销通常低于 MSAA，但可能让文字和细线变软。"],
    shadows: ["控制模型、Primitive 和地形阴影。", "画面更真实，但对大量模型和地形开销明显。"],
    enableLighting: ["根据场景光源给地球表面做明暗。", "这个开关不驱动系统时间；需要真实昼夜时打开“真实日照”。"],
    solarLighting: ["使用 Cesium 太阳方向、系统时间和动态大气表达昼夜。", "地球和地面对象保持地固坐标，不手动旋转地球；需要更真实的时间态势时开启。"],
    solarLightIntensity: ["真实日照模式下的太阳光强度。", "数值越大白天越亮、明暗对比越强；不会改变飞机、基站和轨迹数据。"],
    maximumScreenSpaceError: ["控制地球瓦片 LOD 的屏幕误差。", "数值越大越快但地面更粗糙；4 是集显平衡档。"],
    terrainEnabledIn3D: ["3D 模式下加载 Terrarium 真实高程地形。", "会增加网络、解码、CPU 和 GPU 压力；2D/2.5D 默认不加载。"],
    terrainExaggeration: ["控制地形垂直方向的视觉夸张倍率。", "1.0 为真实高度；0.0 压平地形；只改变视觉起伏，不增加高程精度，前端不限制范围。"],
    terrainLimitMaximumLevel: ["限制 Terrarium 地形继续请求更深层级。", "只限制高程层级，减少无意义子瓦片请求。"],
    terrainMaximumLevel: ["地形允许加载的最高瓦片层级。", "只有启用地形且限制层级时生效；越高越精细也越耗资源。"],
    terrainCacheTiles: ["内存中保留的已解码地形瓦片数量。", "越大回看越快但占用更多内存；过小会增加重复请求和解码。"]
};
type Cesium_Graphics_Config_Json = {
    debug_show_frames_per_second: boolean
    target_frame_rate: number
    use_browser_recommended_resolution: boolean
    resolution_scale: number
    msaa_samples: number
    fxaa: boolean
    shadows: boolean
    enable_lighting: boolean
    solar_lighting: boolean
    solar_light_intensity: number
    maximum_screen_space_error: number
    terrain_enabled_in_3d: boolean
    terrain_exaggeration: number
    terrain_limit_maximum_level: boolean
    terrain_maximum_level: number
    terrain_cache_tiles: number
}
function number_value(value: unknown, fallback: number, min: number, max: number): number {
    const next = Number(value);
    if (!Number.isFinite(next)) return fallback;
    return Math.min(max, Math.max(min, next));
}
function unrestricted_number_value(value: unknown, fallback: number): number {
    const next = Number(value);
    return Number.isFinite(next) ? next : fallback;
}
function boolean_value(value: unknown, fallback: boolean): boolean {
    return typeof value === "boolean" ? value : fallback;
}
export function normalize_cesium_graphics_config(data: Partial<Cesium_Graphics_Config>): Cesium_Graphics_Config {
    return {
        debugShowFramesPerSecond: boolean_value(data.debugShowFramesPerSecond, default_cesium_graphics_config.debugShowFramesPerSecond),
        targetFrameRate: number_value(data.targetFrameRate, default_cesium_graphics_config.targetFrameRate, 1, 120),
        useBrowserRecommendedResolution: boolean_value(data.useBrowserRecommendedResolution, default_cesium_graphics_config.useBrowserRecommendedResolution),
        resolutionScale: number_value(data.resolutionScale, default_cesium_graphics_config.resolutionScale, 0.2, 1.5),
        msaaSamples: number_value(data.msaaSamples, default_cesium_graphics_config.msaaSamples, 1, 8),
        fxaa: boolean_value(data.fxaa, default_cesium_graphics_config.fxaa),
        shadows: boolean_value(data.shadows, default_cesium_graphics_config.shadows),
        enableLighting: boolean_value(data.enableLighting, default_cesium_graphics_config.enableLighting),
        solarLighting: boolean_value(data.solarLighting, default_cesium_graphics_config.solarLighting),
        solarLightIntensity: number_value(data.solarLightIntensity, default_cesium_graphics_config.solarLightIntensity, 0, 10),
        maximumScreenSpaceError: number_value(data.maximumScreenSpaceError, default_cesium_graphics_config.maximumScreenSpaceError, 1, 16),
        terrainEnabledIn3D: boolean_value(data.terrainEnabledIn3D, default_cesium_graphics_config.terrainEnabledIn3D),
        terrainExaggeration: unrestricted_number_value(data.terrainExaggeration, default_cesium_graphics_config.terrainExaggeration),
        terrainLimitMaximumLevel: boolean_value(data.terrainLimitMaximumLevel, default_cesium_graphics_config.terrainLimitMaximumLevel),
        terrainMaximumLevel: number_value(data.terrainMaximumLevel, default_cesium_graphics_config.terrainMaximumLevel, 0, 24),
        terrainCacheTiles: Math.round(number_value(data.terrainCacheTiles, default_cesium_graphics_config.terrainCacheTiles, 16, 2048))
    };
}
function from_server_config(data: Partial<Cesium_Graphics_Config_Json>): Cesium_Graphics_Config {
    return normalize_cesium_graphics_config({
        debugShowFramesPerSecond: data.debug_show_frames_per_second,
        targetFrameRate: data.target_frame_rate,
        useBrowserRecommendedResolution: data.use_browser_recommended_resolution,
        resolutionScale: data.resolution_scale,
        msaaSamples: data.msaa_samples,
        fxaa: data.fxaa,
        shadows: data.shadows,
        enableLighting: data.enable_lighting,
        solarLighting: data.solar_lighting,
        solarLightIntensity: data.solar_light_intensity,
        maximumScreenSpaceError: data.maximum_screen_space_error,
        terrainEnabledIn3D: data.terrain_enabled_in_3d,
        terrainExaggeration: data.terrain_exaggeration,
        terrainLimitMaximumLevel: data.terrain_limit_maximum_level,
        terrainMaximumLevel: data.terrain_maximum_level,
        terrainCacheTiles: data.terrain_cache_tiles
    });
}
function to_server_config(config: Cesium_Graphics_Config): Cesium_Graphics_Config_Json {
    const normalized = normalize_cesium_graphics_config(config);
    return {
        debug_show_frames_per_second: normalized.debugShowFramesPerSecond,
        target_frame_rate: normalized.targetFrameRate,
        use_browser_recommended_resolution: normalized.useBrowserRecommendedResolution,
        resolution_scale: normalized.resolutionScale,
        msaa_samples: normalized.msaaSamples,
        fxaa: normalized.fxaa,
        shadows: normalized.shadows,
        enable_lighting: normalized.enableLighting,
        solar_lighting: normalized.solarLighting,
        solar_light_intensity: normalized.solarLightIntensity,
        maximum_screen_space_error: normalized.maximumScreenSpaceError,
        terrain_enabled_in_3d: normalized.terrainEnabledIn3D,
        terrain_exaggeration: normalized.terrainExaggeration,
        terrain_limit_maximum_level: normalized.terrainLimitMaximumLevel,
        terrain_maximum_level: normalized.terrainMaximumLevel,
        terrain_cache_tiles: normalized.terrainCacheTiles
    };
}
export async function load_cesium_graphics_config(): Promise<Cesium_Graphics_Config> {
    const response = await axios.get<Cesium_Graphics_Config_Json>("/map/graphics");
    return from_server_config(response.data);
}
export async function save_cesium_graphics_config(config: Cesium_Graphics_Config): Promise<Cesium_Graphics_Config> {
    const response = await axios.post<Cesium_Graphics_Config_Json>("/map/graphics", to_server_config(config));
    const normalized = from_server_config(response.data);
    emit_cesium_graphics_config(normalized);
    return normalized;
}
export function emit_cesium_graphics_config(config: Cesium_Graphics_Config) {
    const normalized = normalize_cesium_graphics_config(config);
    window.dispatchEvent(new CustomEvent<Cesium_Graphics_Config>(cesium_graphics_config_event, {detail: normalized}));
}
export class Cesium_Render_Settings extends enhance.Base {
    config: Cesium_Graphics_Config = {...default_cesium_graphics_config};
    number_input_drafts: Partial<Record<Number_Config_Key, number | null>> = {};
    async on_mount() {
        this.config = await load_cesium_graphics_config();
        this.number_input_drafts = {};
        this.flush();
    }
    number_input_value(key: Number_Config_Key): number | null {
        return Object.prototype.hasOwnProperty.call(this.number_input_drafts, key) ? this.number_input_drafts[key]! : this.config[key];
    }
    set_number(key: Number_Config_Key, value: number | null) {
        this.number_input_drafts[key] = value;
        if (value === null) {
            this.flush();
            return;
        }
        this.config = normalize_cesium_graphics_config({...this.config, [key]: value});
        emit_cesium_graphics_config(this.config);
        this.flush();
    }
    blur_number(key: Number_Config_Key) {
        if (Object.prototype.hasOwnProperty.call(this.number_input_drafts, key)) {
            delete this.number_input_drafts[key];
            this.flush();
        }
    }
    set_boolean(key: Boolean_Config_Key, value: boolean) {
        this.config = normalize_cesium_graphics_config({...this.config, [key]: value});
        emit_cesium_graphics_config(this.config);
        this.flush();
    }
    reset() {
        this.config = {...default_cesium_graphics_config};
        this.number_input_drafts = {};
        emit_cesium_graphics_config(this.config);
        this.flush();
    }
    apply_preset(key: keyof typeof graphics_presets) {
        this.config = {...graphics_presets[key]};
        this.number_input_drafts = {};
        emit_cesium_graphics_config(this.config);
        this.flush();
    }
    async save() {
        this.config = await save_cesium_graphics_config(this.config);
        this.number_input_drafts = {};
        message.success("Cesium画质设置已保存");
        this.flush();
    }
    render_label(label: string, key: keyof Cesium_Graphics_Config) {
        return (
            <Space size={4}>
                <span>{label}</span>
                <Tooltip placement="right" title={<div style={{maxWidth: 360}}>{graphics_help[key].map((line) => <div key={line}>{line}</div>)}</div>}>
                    <QuestionCircleOutlined style={{color: "#1677ff"}} />
                </Tooltip>
            </Space>
        );
    }
    render_switch(label: string, key: Boolean_Config_Key) {
        return (
            <Space style={{display: "flex", justifyContent: "space-between", width: 280}}>
                {this.render_label(label, key)}
                <Switch checked={this.config[key]} onChange={(checked) => this.set_boolean(key, checked)} />
            </Space>
        );
    }
    render_number(label: string, key: Number_Config_Key, min: number, max: number, step: number) {
        return (
            <InputNumber
                addonBefore={this.render_label(label, key)}
                min={min}
                max={max}
                step={step}
                value={this.number_input_value(key)}
                style={{width: 280}}
                onChange={(value) => this.set_number(key, value)}
                onBlur={() => this.blur_number(key)}
            />
        );
    }
    render_terrain_exaggeration() {
        const disabled = !this.config.terrainEnabledIn3D;
        return (
            <Space direction="vertical" size={4} style={{width: 280}}>
                {this.render_label("地形起伏倍率", "terrainExaggeration")}
                <InputNumber
                    step={0.1}
                    disabled={disabled}
                    value={this.number_input_value("terrainExaggeration")}
                    style={{width: 280}}
                    onChange={(value) => this.set_number("terrainExaggeration", value)}
                    onBlur={() => this.blur_number("terrainExaggeration")}
                />
            </Space>
        );
    }
    render(props: any) {
        return (
            <div style={setting_style}>
                <h2 style={row_style}>Cesium画质设置</h2>
                <Space direction="vertical" size={8} style={col_style}>
                    <Space>
                        <Button onClick={() => this.apply_preset("low")}>低画质</Button>
                        <Button onClick={() => this.apply_preset("medium")}>中画质</Button>
                        <Button onClick={() => this.apply_preset("high")}>高画质</Button>
                    </Space>
                    {this.render_switch("显示FPS", "debugShowFramesPerSecond")}
                    {this.render_number("目标帧率", "targetFrameRate", 1, 120, 1)}
                    {this.render_switch("浏览器推荐分辨率", "useBrowserRecommendedResolution")}
                    {this.render_number("分辨率缩放", "resolutionScale", 0.2, 1.5, 0.1)}
                    {this.render_number("MSAA采样", "msaaSamples", 1, 8, 1)}
                    {this.render_switch("FXAA", "fxaa")}
                    {this.render_switch("阴影", "shadows")}
                    {this.render_switch("地球光照", "enableLighting")}
                    {this.render_switch("真实日照", "solarLighting")}
                    {this.render_number("太阳光强度", "solarLightIntensity", 0, 10, 0.1)}
                    {this.render_number("地球细节误差", "maximumScreenSpaceError", 1, 16, 1)}
                    {this.render_switch("3D启用地形", "terrainEnabledIn3D")}
                    {this.render_terrain_exaggeration()}
                    {this.render_switch("限制地形层级", "terrainLimitMaximumLevel")}
                    {this.render_number("地形最大层级", "terrainMaximumLevel", 0, 24, 1)}
                    {this.render_number("地形缓存瓦片数", "terrainCacheTiles", 16, 2048, 16)}
                    <Space>
                        <Button onClick={() => this.reset()}>恢复默认画质</Button>
                        <Button type="primary" onClick={() => this.save()}>确定</Button>
                    </Space>
                </Space>
            </div>
        );
    }
}
