#pragma once

#include "Gallery_Actions.h"
#include "Gallery_Renderable_Types.h"

#include "adminive/adminive.hpp"
#include "adminive/adapters/magic_enum.hpp"
#include "adminive/adapters/nlohmann_json.hpp"

#include <algorithm>
#include <array>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

namespace renderive::web::gallery_adminive {

enum class Number_Locale_Choice { Point, Comma };
enum class Color_Map_Choice { Spectrum, Ember, Grayscale };

inline Color blend(Color first, Color second, double amount) {
    const auto channel = [amount](std::uint8_t a, std::uint8_t b) {
        return static_cast<std::uint8_t>(std::clamp(
            std::lround(a + (b - a) * amount), 0L, 255L));
    };
    return {channel(first.r, second.r), channel(first.g, second.g),
            channel(first.b, second.b), channel(first.a, second.a)};
}

inline Color_Map color_map(Color_Map_Choice palette) {
    std::array<Color, 5> stops{
        Color{5, 10, 25, 255}, Color{20, 52, 112, 255}, Color{39, 196, 181, 255},
        Color{242, 201, 76, 255}, Color{255, 76, 106, 255}
    };
    if (palette == Color_Map_Choice::Ember) {
        stops = {Color{10, 5, 8, 255}, Color{62, 18, 30, 255}, Color{153, 47, 39, 255},
                 Color{244, 132, 48, 255}, Color{255, 238, 166, 255}};
    } else if (palette == Color_Map_Choice::Grayscale) {
        stops = {Color{0, 0, 0, 255}, Color{52, 52, 52, 255}, Color{112, 112, 112, 255},
                 Color{188, 188, 188, 255}, Color{255, 255, 255, 255}};
    }
    std::vector<Pixel> colors;
    colors.reserve(256);
    for (int index = 0; index < 256; ++index) {
        const double position = index / 255.0 * (stops.size() - 1);
        const auto first = static_cast<std::size_t>(std::floor(position));
        const auto second = std::min(first + 1, stops.size() - 1);
        colors.push_back(premultiply(blend(stops[first], stops[second], position - first)));
    }
    return Color_Map(std::move(colors));
}

inline Color_Map_Choice color_map_choice(const Color_Map& value) {
    for (const Color_Map_Choice choice : magic_enum::enum_values<Color_Map_Choice>()) {
        if (value.colors() == color_map(choice).colors())
            return choice;
    }
    return Color_Map_Choice::Spectrum;
}

template <auto Member>
auto editable(std::string name, std::string label, adminive::Field_Control control) {
    return adminive::field<Member>(std::move(name), std::move(label))
        .editable().control(control);
}

template <auto Member>
auto number(std::string name, std::string label) {
    return editable<Member>(std::move(name), std::move(label), adminive::Field_Control::number);
}

template <auto Member>
auto boolean(std::string name, std::string label) {
    return editable<Member>(std::move(name), std::move(label), adminive::Field_Control::boolean);
}

template <auto Member>
auto select(std::string name, std::string label) {
    return editable<Member>(std::move(name), std::move(label), adminive::Field_Control::select);
}

template <auto Member>
auto color(std::string name, std::string label) {
    return editable<Member>(std::move(name), std::move(label), adminive::Field_Control::color);
}

template <class Object, auto Member>
auto property(std::string name, std::string label, adminive::Field_Control control) {
    return adminive::field(std::move(name), std::move(label),
                           Gallery_Property_Accessor<Object, Member>{})
        .editable()
        .unsynchronized()
        .control(control);
}

template <class Object, auto Member>
auto readonly_property(std::string name, std::string label) {
    return adminive::field(std::move(name), std::move(label),
                           Gallery_Property_Accessor<Object, Member>{});
}

template <class Object, auto Member>
auto property_number(std::string name, std::string label) {
    return property<Object, Member>(std::move(name), std::move(label),
                                    adminive::Field_Control::number);
}

template <class Object, auto Member>
auto property_boolean(std::string name, std::string label) {
    return property<Object, Member>(std::move(name), std::move(label),
                                    adminive::Field_Control::boolean);
}

template <class Object, auto Member>
auto property_select(std::string name, std::string label) {
    return property<Object, Member>(std::move(name), std::move(label),
                                    adminive::Field_Control::select);
}

template <class Object, auto Member>
auto property_color(std::string name, std::string label) {
    return property<Object, Member>(std::move(name), std::move(label),
                                    adminive::Field_Control::color);
}

template <class Object, auto Member>
auto property_object(std::string name, std::string label) {
    return property<Object, Member>(std::move(name), std::move(label),
                                    adminive::Field_Control::automatic);
}

template <class Object, auto Member>
auto property_text(std::string name, std::string label) {
    return property<Object, Member>(std::move(name), std::move(label),
                                    adminive::Field_Control::text);
}

} // namespace renderive::web::gallery_adminive

namespace adminive {

template <class Validator>
struct Renderive_Validator_Metadata {};

template <class T, T Minimum, T Maximum>
struct Renderive_Validator_Metadata<::Range_Validator<T, Minimum, Maximum>> {
    static constexpr T minimum = Minimum;
    static constexpr T maximum = Maximum;
};

template <class T, class Validator, class Json>
struct Value_Adapter<::Validated_Value<T, Validator>, Json>
    : Renderive_Validator_Metadata<Validator> {
    using Storage = ::Validated_Value<T, Validator>;
    using value_type = T;
    static const T& read(const Storage& value) noexcept { return value.get(); }
    static void write(Storage& target, T value) { target = std::move(value); }
};

template <class Json>
struct Value_Adapter<renderive::Color, Json> {
    using value_type = std::string;
    static std::string read(renderive::Color value) {
        char result[8]{};
        std::snprintf(result, sizeof(result), "#%02x%02x%02x", value.r, value.g, value.b);
        return result;
    }
    static void write(renderive::Color& target, const std::string& value) {
        if (value.size() != 7 || value.front() != '#' ||
            !std::all_of(value.begin() + 1, value.end(), [](unsigned char character) {
                return std::isxdigit(character) != 0;
            }))
            throw std::invalid_argument("color must use #RRGGBB");
        const auto channel = [&value](std::size_t offset) {
            return static_cast<std::uint8_t>(std::stoul(value.substr(offset, 2), nullptr, 16));
        };
        target.r = channel(1);
        target.g = channel(3);
        target.b = channel(5);
    }
};

template <class Json>
struct Value_Adapter<renderive::Number_Locale, Json> {
    using Choice = renderive::web::gallery_adminive::Number_Locale_Choice;
    using value_type = Choice;
    static Choice read(renderive::Number_Locale value) {
        return value.decimal_point == ',' ? Choice::Comma : Choice::Point;
    }
    static void write(renderive::Number_Locale& target, Choice value) {
        target.decimal_point = value == Choice::Comma ? ',' : '.';
    }
};

template <class T, T Minimum, T Maximum, T Default, class Json>
struct Value_Adapter<renderive::Clamped_Property<T, Minimum, Maximum, Default>, Json> {
    using Storage = renderive::Clamped_Property<T, Minimum, Maximum, Default>;
    using value_type = T;
    static constexpr T minimum = Minimum;
    static constexpr T maximum = Maximum;
    static T read(const Storage& value) noexcept { return value.get(); }
    static void write(Storage& target, T value) { target = value; }
};

template <class Json>
struct Value_Adapter<renderive::Unit_Interval, Json> {
    using value_type = double;
    static constexpr double minimum = 0.0;
    static constexpr double maximum = 1.0;
    static constexpr double multiple_of = 0.01;
    static double read(const renderive::Unit_Interval& value) noexcept { return value.get(); }
    static void write(renderive::Unit_Interval& target, double value) { target = value; }
};

template <class Json>
struct Value_Adapter<renderive::Color_Map, Json> {
    using Choice = renderive::web::gallery_adminive::Color_Map_Choice;
    using value_type = Choice;
    static Choice read(const renderive::Color_Map& value) {
        return renderive::web::gallery_adminive::color_map_choice(value);
    }
    static void write(renderive::Color_Map& target, Choice value) {
        target = renderive::web::gallery_adminive::color_map(value);
    }
};

template <>
struct Type_Descriptor<renderive::Range> {
    static auto get() {
        using T = renderive::Range;
        using namespace renderive::web::gallery_adminive;
        return adminive::object<T>("range", "范围",
            number<&T::origin>("origin", "起点"),
            number<&T::target>("target", "终点"));
    }
};

template <>
struct Type_Descriptor<renderive::Pen> {
    static auto get() {
        using T = renderive::Pen;
        using namespace renderive::web::gallery_adminive;
        return adminive::object<T>("pen", "画笔",
            color<&T::color>("color", "颜色"),
            number<&T::width>("width", "宽度"),
            select<&T::style>("style", "线型")
                .enum_label<renderive::Line_Style::None>("无")
                .enum_label<renderive::Line_Style::Solid>("实线")
                .enum_label<renderive::Line_Style::Dash>("虚线")
                .enum_label<renderive::Line_Style::Dot>("点线"),
            select<&T::cap>("cap", "端点样式")
                .enum_label<renderive::Line_Cap::Butt>("平头")
                .enum_label<renderive::Line_Cap::Square>("方头")
                .enum_label<renderive::Line_Cap::Round>("圆头"),
            select<&T::join>("join", "连接样式")
                .enum_label<renderive::Line_Join::Miter>("尖角")
                .enum_label<renderive::Line_Join::Bevel>("斜角")
                .enum_label<renderive::Line_Join::Round>("圆角"));
    }
};

template <>
struct Type_Descriptor<renderive::Brush> {
    static auto get() {
        using T = renderive::Brush;
        using namespace renderive::web::gallery_adminive;
        return adminive::object<T>("brush", "画刷",
            color<&T::color>("color", "颜色"),
            select<&T::style>("style", "填充样式")
                .enum_label<renderive::Brush_Style::None>("无")
                .enum_label<renderive::Brush_Style::Solid>("纯色"));
    }
};

template <>
struct Type_Descriptor<renderive::Font> {
    static auto get() {
        using T = renderive::Font;
        using namespace renderive::web::gallery_adminive;
        return adminive::object<T>("font", "字体",
            number<&T::size>("size", "字号"),
            number<&T::weight>("weight", "字重"),
            boolean<&T::italic>("italic", "斜体"));
    }
};

#define RENDERIVE_AXIS_DESCRIPTOR(GalleryType, Name, Label) \
    template <> struct Type_Descriptor<renderive::web::GalleryType> { \
        static auto get() { \
            using T = renderive::web::GalleryType; \
            using B = renderive::Axis_Base_Properties; \
            using P = renderive::Axis_Properties; \
            using namespace renderive::web::gallery_adminive; \
            return adminive::object<T>(Name, Label, \
                readonly_property<T, &B::x>("x", "X 位置"), \
                readonly_property<T, &B::y>("y", "Y 位置"), \
                property_select<T, &B::orientation>("orientation", "方向") \
                    .enum_label<renderive::Orientation::Horizontal>("水平") \
                    .enum_label<renderive::Orientation::Vertical>("垂直"), \
                readonly_property<T, &B::pixel_length>("pixel_length", "像素长度"), \
                property_number<T, &B::tick_length>("tick_length", "主刻度长度"), \
                property_number<T, &B::sub_tick_length>("sub_tick_length", "次刻度长度"), \
                property_color<T, &B::color>("color", "颜色"), \
                property_select<T, &B::locale>("locale", "小数点符号") \
                    .enum_label<renderive::web::gallery_adminive::Number_Locale_Choice::Point>("点") \
                    .enum_label<renderive::web::gallery_adminive::Number_Locale_Choice::Comma>("逗号"), \
                property_text<T, &B::unit_text>("unit_text", "单位文本"), \
                property_object<T, &B::unit_text_font>("unit_text_font", "单位字体"), \
                property_object<T, &B::unit_text_pen>("unit_text_pen", "单位画笔"), \
                property_object<T, &B::unit_text_background_brush>("unit_text_background_brush", "单位背景"), \
                property_number<T, &B::label_rotation_degrees>("label_rotation_degrees", "标签旋转角度"), \
                property_object<T, &P::coordinates>("coordinates", "坐标范围"), \
                property_number<T, &P::precision>("precision", "小数位数"), \
                property_boolean<T, &P::wheel>("wheel", "滚轮缩放"), \
                property_boolean<T, &P::drag>("drag", "拖拽平移")); \
        } \
    }

RENDERIVE_AXIS_DESCRIPTOR(Gallery_Axis, "axis", "数值轴");
RENDERIVE_AXIS_DESCRIPTOR(Gallery_Frequency_Axis, "frequency_axis", "频率轴");
#undef RENDERIVE_AXIS_DESCRIPTOR

template <>
struct Type_Descriptor<renderive::web::Gallery_Time_Axis> {
    static auto get() {
        using T = renderive::web::Gallery_Time_Axis;
        using B = renderive::Axis_Base_Properties;
        using P = renderive::Time_Axis_Properties;
        using namespace renderive::web::gallery_adminive;
        return adminive::object<T>("time_axis", "时间轴",
            readonly_property<T, &B::x>("x", "X 位置"),
            readonly_property<T, &B::y>("y", "Y 位置"),
            property_select<T, &B::orientation>("orientation", "方向")
                .enum_label<renderive::Orientation::Horizontal>("水平")
                .enum_label<renderive::Orientation::Vertical>("垂直"),
            readonly_property<T, &B::pixel_length>("pixel_length", "像素长度"),
            property_number<T, &B::tick_length>("tick_length", "主刻度长度"),
            property_number<T, &B::sub_tick_length>("sub_tick_length", "次刻度长度"),
            property_color<T, &B::color>("color", "颜色"),
            property_select<T, &B::locale>("locale", "小数点符号")
                .enum_label<renderive::web::gallery_adminive::Number_Locale_Choice::Point>("点")
                .enum_label<renderive::web::gallery_adminive::Number_Locale_Choice::Comma>("逗号"),
            property_text<T, &B::unit_text>("unit_text", "单位文本"),
            property_object<T, &B::unit_text_font>("unit_text_font", "单位字体"),
            property_object<T, &B::unit_text_pen>("unit_text_pen", "单位画笔"),
            property_object<T, &B::unit_text_background_brush>("unit_text_background_brush", "单位背景"),
            property_number<T, &B::label_rotation_degrees>("label_rotation_degrees", "标签旋转角度"),
            property_number<T, &P::visible_count>("visible_count", "可见点数"),
            property_number<T, &P::tick_label_spacing_px>("tick_label_spacing_px", "标签间距"),
            property_text<T, &P::format>("format", "时间格式"),
            property_boolean<T, &P::newest_at_start>("newest_at_start", "最新数据在起点"));
    }
};

#define RENDERIVE_GALLERY_DESCRIPTOR(GalleryType, PropertiesType, Name, Label, ...) \
    template <> struct Type_Descriptor<renderive::web::GalleryType> { \
        static auto get() { \
            using T = renderive::web::GalleryType; \
            using P = renderive::PropertiesType; \
            using namespace renderive::web::gallery_adminive; \
            return adminive::object<T>(Name, Label, __VA_ARGS__); \
        } \
    }

RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Spectrum, Spectrum_Properties, "spectrum", "实时频谱",
    property_number<T, &P::frequency_point_size>("frequency_point_size", "频率点数"),
    property_select<T, &P::partition_mode>("partition_mode", "任务分区模式")
        .enum_label<renderive::Render_Partition_Mode::Automatic>("自动瓶颈优化")
        .enum_label<renderive::Render_Partition_Mode::Fixed>("固定分区"),
    property_number<T, &P::partition_count>("partition_count", "固定分区数")
        .description("固定模式下的并行任务数；1 表示不分区")
        .visible_on("${$self.partition_mode == 'Fixed'}"),
    property_object<T, &P::frequency_range>("frequency_range", "频率范围"),
    property_number<T, &P::center_frequency>("center_frequency", "中心频率"),
    property_object<T, &P::sweep_frequency_range>("sweep_frequency_range", "扫频范围"),
    property_boolean<T, &P::max_hold_visible>("max_hold_visible", "显示最大保持"),
    property_boolean<T, &P::min_hold_visible>("min_hold_visible", "显示最小保持"),
    property_boolean<T, &P::max_marker_visible>("max_marker_visible", "显示最大值标记"),
    property_boolean<T, &P::use_min_marker>("use_min_marker", "显示最小值标记"),
    property_boolean<T, &P::sweep_region_visible>("sweep_region_visible", "显示扫频区域"),
    property_boolean<T, &P::visible_range_only>("visible_range_only", "仅绘制可见范围"),
    property_select<T, &P::interpolation_mode>("interpolation_mode", "曲线插值")
        .enum_label<renderive::Line_Interpolation_Mode::Nearest_Sample>("最近样本")
        .enum_label<renderive::Line_Interpolation_Mode::Linear_Value>("数值线性")
        .enum_label<renderive::Line_Interpolation_Mode::Linear_Power_Domain>("功率域线性")
        .enum_label<renderive::Line_Interpolation_Mode::Step_Left>("左阶梯")
        .enum_label<renderive::Line_Interpolation_Mode::Step_Right>("右阶梯")
        .enum_label<renderive::Line_Interpolation_Mode::Cubic_Value>("三次插值"),
    property_object<T, &P::max_brush>("max_brush", "最大保持画刷"),
    property_object<T, &P::current_brush>("current_brush", "当前曲线画刷"),
    property_object<T, &P::min_brush>("min_brush", "最小保持画刷"),
    property_object<T, &P::max_pen>("max_pen", "最大保持画笔"),
    property_object<T, &P::current_pen>("current_pen", "当前曲线画笔"),
    property_object<T, &P::min_pen>("min_pen", "最小保持画笔"),
    property_object<T, &P::selected_marker_pen>("selected_marker_pen", "选中标记画笔"),
    property_object<T, &P::marker_pen>("marker_pen", "标记画笔"),
    property_object<T, &P::middle_frequency_pen>("middle_frequency_pen", "中心频率画笔"),
    property_object<T, &P::sweep_region_brush>("sweep_region_brush", "扫频区域画刷"),
    property_boolean<T, &P::tooltip_enabled>("tooltip_enabled", "启用提示框"),
    property_object<T, &P::tooltip_font>("tooltip_font", "提示框字体"),
    property_object<T, &P::tooltip_text_pen>("tooltip_text_pen", "提示框文字画笔"),
    property_object<T, &P::tooltip_background_brush>("tooltip_background_brush", "提示框背景"));

RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Waterfall, Waterfall_Properties, "waterfall", "频谱瀑布",
    property_object<T, &P::frequency_range>("frequency_range", "频率范围"),
    property_object<T, &P::power_range>("power_range", "功率范围"),
    property_number<T, &P::frequency_bin_count>("frequency_bin_count", "频率格数"),
    property_select<T, &P::partition_mode>("partition_mode", "任务分区模式")
        .enum_label<renderive::Render_Partition_Mode::Automatic>("自动瓶颈优化")
        .enum_label<renderive::Render_Partition_Mode::Fixed>("固定分区"),
    property_number<T, &P::partition_count>("partition_count", "固定分区数")
        .description("固定模式下的并行任务数；1 表示不分区")
        .visible_on("${$self.partition_mode == 'Fixed'}"),
    property_boolean<T, &P::visible_range_only>("visible_range_only", "仅绘制可见范围"),
    property_select<T, &P::interpolation_mode>("interpolation_mode", "图像插值")
        .enum_label<renderive::Image_Interpolation_Mode::Nearest>("最近邻")
        .enum_label<renderive::Image_Interpolation_Mode::Bilinear>("双线性")
        .enum_label<renderive::Image_Interpolation_Mode::Bicubic>("双三次"),
    property_select<T, &P::color_map>("color_map", "色图")
        .enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Spectrum>("频谱")
        .enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Ember>("火焰")
        .enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Grayscale>("灰度"),
    property_boolean<T, &P::tooltip_enabled>("tooltip_enabled", "启用提示框"),
    property_object<T, &P::tooltip_font>("tooltip_font", "提示框字体"),
    property_object<T, &P::tooltip_text_pen>("tooltip_text_pen", "提示框文字画笔"),
    property_object<T, &P::tooltip_background_brush>("tooltip_background_brush", "提示框背景"));

RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Afterglow, Afterglow_Properties, "afterglow", "余辉频谱",
    property_object<T, &P::frequency_range>("frequency_range", "频率范围"),
    property_object<T, &P::power_range>("power_range", "功率范围"),
    property_number<T, &P::frequency_point_size>("frequency_point_size", "频率点数"),
    property_number<T, &P::power_point_size>("power_point_size", "功率点数"),
    property_select<T, &P::partition_mode>("partition_mode", "任务分区模式")
        .enum_label<renderive::Render_Partition_Mode::Automatic>("自动瓶颈优化")
        .enum_label<renderive::Render_Partition_Mode::Fixed>("固定分区"),
    property_number<T, &P::partition_count>("partition_count", "固定分区数")
        .description("固定模式下的并行任务数；1 表示不分区")
        .visible_on("${$self.partition_mode == 'Fixed'}"),
    property_boolean<T, &P::interpolate>("interpolate", "插值功率点"),
    property_number<T, &P::attenuation_rate>("attenuation_rate", "衰减率"),
    property_select<T, &P::color_map>("color_map", "色图")
        .enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Spectrum>("频谱")
        .enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Ember>("火焰")
        .enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Grayscale>("灰度"));

RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Sweep_Spectrum, Sweep_Spectrum_Properties, "sweep_spectrum", "扫频频谱",
    property_object<T, &P::frequency_range>("frequency_range", "频率范围"),
    property_number<T, &P::bins_per_block>("bins_per_block", "每块频率格数"),
    property_number<T, &P::block_count>("block_count", "扫频块数"),
    property_object<T, &P::pen>("pen", "扫频曲线画笔"),
    property_object<T, &P::current_frequency_pen>("current_frequency_pen", "当前频率画笔"),
    property_boolean<T, &P::visible_range_only>("visible_range_only", "仅绘制可见范围"),
    property_select<T, &P::interpolation_mode>("interpolation_mode", "曲线插值")
        .enum_label<renderive::Line_Interpolation_Mode::Nearest_Sample>("最近样本")
        .enum_label<renderive::Line_Interpolation_Mode::Linear_Value>("数值线性")
        .enum_label<renderive::Line_Interpolation_Mode::Linear_Power_Domain>("功率域线性")
        .enum_label<renderive::Line_Interpolation_Mode::Step_Left>("左阶梯")
        .enum_label<renderive::Line_Interpolation_Mode::Step_Right>("右阶梯")
        .enum_label<renderive::Line_Interpolation_Mode::Cubic_Value>("三次插值"));

RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Frequency_Trace, Frequency_Trace_Properties, "frequency_trace", "频率轨迹",
    property_object<T, &P::pen>("pen", "轨迹画笔"));

RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Selection_Rectangle_Overlay, Selection_Rectangle_Overlay_Properties, "selection_overlay", "框选区域",
    property_object<T, &P::label_font>("label_font", "标签字体"),
    property_object<T, &P::label_pen>("label_pen", "标签画笔"),
    property_object<T, &P::selection_brush>("selection_brush", "选区画刷"),
    property_object<T, &P::selection_border_pen>("selection_border_pen", "选区边框画笔"));

RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Constellation_Diagram, Constellation_Diagram_Properties, "constellation", "星座图",
    property_object<T, &P::i_range>("i_range", "I 轴范围"),
    property_object<T, &P::q_range>("q_range", "Q 轴范围"),
    property_color<T, &P::point_color>("point_color", "点颜色"),
    property_color<T, &P::anchor_color>("anchor_color", "锚点颜色"),
    property_number<T, &P::point_lifetime_ms>("point_lifetime_ms", "点保留时间"),
    property_select<T, &P::type>("type", "调制类型")
        .enum_label<renderive::Constellation_Diagram_Type::Psk4>("4 PSK")
        .enum_label<renderive::Constellation_Diagram_Type::Psk8>("8 PSK")
        .enum_label<renderive::Constellation_Diagram_Type::Psk16>("16 PSK"),
    property_number<T, &P::phase_offset_radians>("phase_offset_radians", "相位偏移"));

#undef RENDERIVE_GALLERY_DESCRIPTOR

} // namespace adminive

namespace renderive::web::gallery_action_registry {

constexpr auto axis_metadata() {
    return structive::type_metadata(
        gallery_action("axis_probe", "读取坐标映射与刻度",
                       "coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label",
                       "坐标轴"));
}

} // namespace renderive::web::gallery_action_registry

namespace structive {

#define RENDERIVE_TYPE_ACTIONS(Type, ...) \
    template <> struct Type_Descriptor<renderive::web::Type> { \
        static auto get() { \
            using T = renderive::web::Type; \
            using renderive::web::gallery_action; \
            return object<T>(type_metadata(__VA_ARGS__)); \
        } \
    }

template <>
struct Type_Descriptor<renderive::web::Gallery_Axis> {
    static auto get() {
        return object<renderive::web::Gallery_Axis>(
            renderive::web::gallery_action_registry::axis_metadata());
    }
};

template <>
struct Type_Descriptor<renderive::web::Gallery_Frequency_Axis> {
    static auto get() {
        return object<renderive::web::Gallery_Frequency_Axis>(
            renderive::web::gallery_action_registry::axis_metadata());
    }
};

template <>
struct Type_Descriptor<renderive::web::Gallery_Time_Axis> {
    static auto get() {
        using T = renderive::web::Gallery_Time_Axis;
        using renderive::web::gallery_action;
        return object<T>(type_metadata(
            gallery_action("axis_probe", "读取坐标映射与刻度",
                           "coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label",
                           "坐标轴"),
            gallery_action("append_time", "追加时间点",
                           "Time_Axis::append_time / tick_to_time", "时间轴")));
    }
};

RENDERIVE_TYPE_ACTIONS(Gallery_Spectrum,
    gallery_action("push_samples", "推送一帧样本", "Spectrum::update_samples", "频谱"),
    gallery_action("power_at", "查询指定频率功率", "Spectrum::power_at", "频谱",
                   {}, "number", "频率", 98e6),
    gallery_action("add_marker", "添加点标记", "Spectrum::add_custom_marker", "标记",
                   {}, "number", "频率", 96e6),
    gallery_action("add_line_marker", "添加线标记", "Spectrum::add_custom_line_marker", "标记",
                   {}, "number", "频率", 100e6),
    gallery_action("remove_marker", "按频率删除标记", "Spectrum::remove_custom_marker", "标记",
                   {}, "number", "频率", 96e6),
    gallery_action("remove_selected_marker", "删除选中标记",
                   "Spectrum::remove_selected_marker", "标记"),
    gallery_action("clear_markers", "清空标记", "Spectrum::clear_custom_markers", "标记"),
    gallery_action("select_marker", "选择标记索引", "Spectrum::set_selected_marker_index", "标记",
                   {}, "number", "索引", 0.0),
    gallery_action("select_next_marker", "选择下一个标记", "Spectrum::select_next_marker", "标记"),
    gallery_action("select_previous_marker", "选择上一个标记", "Spectrum::select_previous_marker", "标记"),
    gallery_action("clear_marker_selection", "清除标记选择",
                   "Spectrum::clear_marker_selection", "标记"),
    gallery_action("set_marker_frequency", "修改选中标记频率",
                   "Spectrum::set_marker_frequency / set_current_marker_frequency", "标记",
                   {}, "number", "频率", 99e6));

RENDERIVE_TYPE_ACTIONS(Gallery_Waterfall,
    gallery_action("append_row", "追加一行", "Waterfall::append_row", "频谱瀑布"),
    gallery_action("append_tick_row", "按 tick 追加一行",
                   "Time_Axis::append_time / Waterfall::append_row(int, span)", "频谱瀑布"));

RENDERIVE_TYPE_ACTIONS(Gallery_Afterglow,
    gallery_action("append_spectrum", "追加一帧频谱", "Afterglow::append_spectrum", "余辉频谱"));

RENDERIVE_TYPE_ACTIONS(Gallery_Sweep_Spectrum,
    gallery_action("append_block", "追加一个扫频块", "Sweep_Spectrum::append_block", "扫频频谱"));

RENDERIVE_TYPE_ACTIONS(Gallery_Frequency_Trace,
    gallery_action("append_sample", "追加一个样本", "Frequency_Trace::append_sample", "频率轨迹"),
    gallery_action("append_tick_sample", "按 tick 追加样本",
                   "Time_Axis::append_time / Frequency_Trace::append_sample(int, double)",
                   "频率轨迹"));

RENDERIVE_TYPE_ACTIONS(Gallery_Selection_Rectangle_Overlay,
    gallery_action("clear_selection", "清空框选区域",
                   "Selection_Rectangle_Overlay::clear_selected_regions", "框选区域"));

RENDERIVE_TYPE_ACTIONS(Gallery_Constellation_Diagram,
    gallery_action("append_points", "追加一组 IQ 点",
                   "Constellation_Diagram::append_point", "星座图"),
    gallery_action("fit_square", "按轴拟合正方形",
                   "Constellation_Diagram::fit_square_to_axes", "星座图"));

#undef RENDERIVE_TYPE_ACTIONS

} // namespace structive

namespace renderive::web {

template <class Object>
std::size_t gallery_control_count() {
    using Json = nlohmann::json;
    const auto count_fields = [](const auto& self, const Json& fields) -> std::size_t {
        std::size_t result{};
        for (const auto& field : fields) {
            if (field.value("editable", false) && !field.contains("children"))
                ++result;
            if (field.contains("children"))
                result += self(self, field.at("children"));
        }
        return result;
    };
    const Json descriptor = adminive::to_descriptor_json<Json, Object>();
    return count_fields(count_fields, descriptor.at("fields"));
}

struct Gallery_Case_Type_Registration {
    std::string_view case_id;
    std::size_t (*control_count)();
    void (*append_actions)(std::vector<Gallery_Action_Model>&);
};

template <class... Objects>
Gallery_Case_Type_Registration gallery_case_types(std::string_view case_id) {
    return {
        case_id,
        [] { return (std::size_t{} + ... + gallery_control_count<Objects>()); },
        [](std::vector<Gallery_Action_Model>& actions) {
            (append_gallery_actions<Objects>(actions), ...);
        }
    };
}

inline const Gallery_Case_Type_Registration* gallery_case_registration(
    std::string_view case_id) {
    static const std::array registrations{
        gallery_case_types<Gallery_Frequency_Axis, Gallery_Time_Axis>("axis_lab"),
        gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Spectrum>("spectrum"),
        gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Afterglow>("afterglow"),
        gallery_case_types<Gallery_Axis, Gallery_Axis, Gallery_Sweep_Spectrum>("sweep_spectrum"),
        gallery_case_types<Gallery_Frequency_Axis, Gallery_Time_Axis, Gallery_Waterfall>("waterfall"),
        gallery_case_types<Gallery_Time_Axis, Gallery_Axis, Gallery_Frequency_Trace>("frequency_trace"),
        gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Spectrum,
                           Gallery_Selection_Rectangle_Overlay>("selection_overlay"),
        gallery_case_types<Gallery_Axis, Gallery_Axis, Gallery_Constellation_Diagram>("constellation")
    };
    const auto found = std::find_if(
        registrations.begin(), registrations.end(),
        [case_id](const auto& registration) { return registration.case_id == case_id; });
    return found == registrations.end() ? nullptr : &*found;
}

inline std::size_t gallery_renderable_control_count(std::string_view case_id) {
    const auto* registration = gallery_case_registration(case_id);
    return registration ? registration->control_count() : 0;
}

inline void append_gallery_renderable_actions(
    std::string_view case_id, std::vector<Gallery_Action_Model>& actions) {
    if (const auto* registration = gallery_case_registration(case_id))
        registration->append_actions(actions);
}

} // namespace renderive::web
