#include "Config.h"
#include "Config_Default.h"
#include <algorithm>
#include <exception>
#include <filesystem>
#include <mutex>
#include <stdexcept>
#include <string_view>
#include <utility>
#include "Global.h"
#include "Psc_Cpp_Core/Base/Check.h"
std::string default_config_directory;
namespace {
std::filesystem::path config_directory() {
    if (!default_config_directory.empty()) {
        return default_config_directory;
    }
    return std::filesystem::path(get_exe_dir()) / "config";
}
std::filesystem::path config_section_path(Config_Section section) {
    return config_directory() / (std::string(config_section_name(section)) + ".json");
}
std::array<std::mutex, static_cast<std::size_t>(Config_Section::count)> config_section_mutexes;
void write_config_section_unlocked(Config_Section section, const Psc::JSON& value) {
    const auto directory = config_directory();
    std::filesystem::create_directories(directory);
    const auto path = config_section_path(section);
    auto temporary = path;
    temporary += ".tmp";
    std::ofstream output(temporary, std::ios::binary | std::ios::trunc);
    if (!output) {
        throw std::runtime_error("cannot open config section: " + temporary.string());
    }
    const auto json = value.to_json_string();
    output.write(json.data(), static_cast<std::streamsize>(json.size()));
    output.close();
    if (output.fail()) {
        throw std::runtime_error("cannot write config section: " + temporary.string());
    }
    std::error_code error;
    std::filesystem::remove(path, error);
    std::filesystem::rename(temporary, path);
}
void write_config_section(Config_Section section, const Psc::JSON& value) {
    std::lock_guard lock(config_section_mutexes[static_cast<std::size_t>(section)]);
    write_config_section_unlocked(section, value);
}
Psc::JSON read_existing_config_section_unlocked(Config_Section section) {
    const auto path = config_section_path(section);
    std::ifstream input(path, std::ios::binary);
    if (!input) {
        throw std::runtime_error("cannot open config section: " + path.string());
    }
    std::ostringstream buffer;
    buffer << input.rdbuf();
    return Psc::parse_json(buffer.str());
}
void merge_json_object(Psc::JSON& target, const Psc::JSON& value) {
    for (const auto& child : value.children) {
        auto iterator = std::find_if(target.children.begin(), target.children.end(), [&](const Psc::JSON& current) {
            return current.key == child.key;
        });
        if (iterator == target.children.end()) {
            target.append(child);
            continue;
        }
        if (iterator->valueType == Psc::Object && child.valueType == Psc::Object)
            merge_json_object(*iterator, child);
        else
            *iterator = child;
    }
}
Psc::JSON read_config_section(Config_Section section, const Psc::JSON& defaults) {
    const auto path = config_section_path(section);
    if (!std::filesystem::exists(path)) {
        const auto name = config_section_name(section);
        const auto value = defaults.get(name);
        if (value == nullptr) {
            throw std::runtime_error("default config section does not exist: " + std::string(name));
        }
        write_config_section(section, *value);
        return *value;
    }
    std::ifstream input(path, std::ios::binary);
    if (!input) {
        throw std::runtime_error("cannot open config section: " + path.string());
    }
    std::ostringstream buffer;
    buffer << input.rdbuf();
    return Psc::parse_json(buffer.str());
}
Psc::JSON config_section_value(Global& global, Config_Section section) {
    switch (section) {
    case Config_Section::version: return Psc::JSON(global.version);
    case Config_Section::dsp: return global.dsp_config.to_base_json();
    case Config_Section::drogon: return global.drogon_config;
    case Config_Section::http_monitor: return global.http_monitor_config.to_base_json();
    case Config_Section::mlat: return global.mlat.to_base_json();
    case Config_Section::web_server: return global.web_server_config.to_base_json();
    case Config_Section::map_resources:
        return global.map_resources_config.read([](const auto& value) {
            return value.to_base_json();
        });
    case Config_Section::map_view:
        return global.map_view_config.read([](const auto& value) {
            return value.to_base_json();
        });
    case Config_Section::map_models:
        return global.map_model_config.read([](const auto& value) {
            return value.to_base_json();
        });
    case Config_Section::mode_acs: return global.mode_acs.to_base_json();
    case Config_Section::log: return global.log_config.to_base_json();
    case Config_Section::console: return global.console_config.to_base_json();
    case Config_Section::external_database: return global.external_resources_manager.to_base_json();
    case Config_Section::cesium_graphics: return global.cesium_graphics_config.to_base_json();
    case Config_Section::count: break;
    }
    std::terminate();
}
}
Psc::JSON Config::load() {
    std::filesystem::create_directories(config_directory());
    std::error_code ignored_error;
    std::filesystem::remove(config_directory() / "device.json", ignored_error);
    const auto defaults = Psc::parse_json(std::string_view(reinterpret_cast<const char*>(ecap_default_config_json), sizeof(ecap_default_config_json)));
    auto result = Psc::JSON::object();
    for (std::size_t index = 0; index < static_cast<std::size_t>(Config_Section::count); ++index) {
        const auto section = static_cast<Config_Section>(index);
        result.append({std::string(config_section_name(section)), read_config_section(section, defaults)});
    }
    std::cout << get_current_date_string() << " 启动加载配置目录: 【" << config_directory().string() << "】" << std::endl;
    return result;
}
Psc::JSON Web_Server_Config::to_base_json() const {
    return Psc::JSON::object({{"webapp", webapp}, {"listen_port", listen_port}});
}
void Web_Server_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    std::error_code error;
    auto result = Psc::assign_field_ec(webapp, that_json, "webapp", error);
    if(!not_exist_use_default_value)
        Psc::check_json_assign_field(result, error, that_json, "webapp");
    if(that_json != nullptr && that_json->get("listen_port") != nullptr) {
        error.clear();
        result = Psc::assign_field_ec(listen_port, that_json, "listen_port", error);
        Psc::check_json_assign_field(result, error, that_json, "listen_port");
    }
}
static std::vector<std::string> read_optional_string_array(const Psc::JSON* json, std::string_view key) {
    auto field = json->get(key);
    if (field == nullptr) {
        return {};
    }
    if (field->valueType != Psc::Array) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
    }
    std::vector<std::string> ret;
    ret.reserve(field->children.size());
    for (const auto& child : field->children) {
        if (child.valueType != Psc::String) {
            throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
        }
        ret.emplace_back(child.val);
    }
    return ret;
}
static Psc::JSON write_string_array(std::string_view key, const std::vector<std::string>& list) {
    auto array = Psc::JSON::array();
    for (const auto& path : list) {
        array.append(Psc::JSON(path));
    }
    return Psc::JSON(key, array);
}
static const std::pair<std::string_view, std::string_view> kDefaultAircraftModelUrls[] = {
{"no_category_information", "/ui/model/aircraft-no-category-information.glb"},
{"surface_emergency_vehicle", "/ui/model/aircraft-surface-emergency-vehicle.glb"},
{"surface_service_vehicle", "/ui/model/aircraft-surface-service-vehicle.glb"},
{"ground_obstruction_4", "/ui/model/aircraft-ground-obstruction-4.glb"},
{"ground_obstruction_5", "/ui/model/aircraft-ground-obstruction-5.glb"},
{"ground_obstruction_6", "/ui/model/aircraft-ground-obstruction-6.glb"},
{"ground_obstruction_7", "/ui/model/aircraft-ground-obstruction-7.glb"},
{"glider", "/ui/model/aircraft-glider.glb"},
{"lighter_than_air", "/ui/model/aircraft-lighter-than-air.glb"},
{"parachutist", "/ui/model/aircraft-parachutist.glb"},
{"ultralight_hangglider_paraglider", "/ui/model/aircraft-ultralight-hangglider-paraglider.glb"},
{"reserved_3_5", "/ui/model/aircraft-reserved-3-5.glb"},
{"unmanned_aerial_vehicle", "/ui/model/aircraft-unmanned-aerial-vehicle.glb"},
{"space_transatmospheric_vehicle", "/ui/model/aircraft-space-transatmospheric-vehicle.glb"},
{"light_aircraft", "/ui/model/aircraft-light-aircraft.glb"},
{"medium_1_aircraft", "/ui/model/aircraft-medium-1-aircraft.glb"},
{"medium_2_aircraft", "/ui/model/aircraft-medium-2-aircraft.glb"},
{"high_vortex_aircraft", "/ui/model/aircraft-high-vortex-aircraft.glb"},
{"heavy_aircraft", "/ui/model/aircraft-heavy-aircraft.glb"},
{"high_performance_aircraft", "/ui/model/aircraft-high-performance-aircraft.glb"},
{"rotorcraft", "/ui/model/aircraft-rotorcraft.glb"}};
static std::map<std::string, Map_Model_Item_Config> make_default_aircraft_models() {
    std::map<std::string, Map_Model_Item_Config> ret;
    for (const auto& item : kDefaultAircraftModelUrls) {
        ret.emplace(std::string(item.first), Map_Model_Item_Config{std::string(item.second), 50.0, -90.0, 0.0, 0.0});
    }
    return ret;
}
static std::map<std::string, Map_Model_Item_Config>
read_optional_model_object_field(const Psc::JSON* json, std::string_view key,
                                 std::map<std::string, Map_Model_Item_Config> fallback,
                                 bool not_exist_use_default_value) {
    auto field = json->get(key);
    if (field == nullptr) {
        return fallback;
    }
    if (field->valueType != Psc::Object) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
    }
    for (const auto& child : field->children) {
        if (child.valueType != Psc::Object) {
            throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
        }
        fallback[child.key].from_base_json(&child, not_exist_use_default_value);
    }
    return fallback;
}
static Psc::JSON write_model_object(std::string_view key, const std::map<std::string, Map_Model_Item_Config>& values) {
    auto object = Psc::JSON::object();
    for (const auto& item : values) {
        object.append({item.first, item.second.to_base_json()});
    }
    return Psc::JSON(key, object);
}
static std::string read_optional_string_field(const Psc::JSON* json, std::string_view key) {
    auto field = json->get(key);
    if (field == nullptr) {
        return {};
    }
    if (field->valueType != Psc::String) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
    }
    return field->val;
}
static std::string read_optional_string_field(const Psc::JSON* json, std::string_view key, std::string_view fallback) {
    auto field = json->get(key);
    if (field == nullptr) {
        return std::string(fallback);
    }
    if (field->valueType != Psc::String) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
    }
    return field->val;
}
static std::uint32_t read_optional_uint32_field(const Psc::JSON* json, std::string_view key, std::uint32_t fallback) {
    auto field = json->get(key);
    if (field == nullptr) {
        return fallback;
    }
    if (field->valueType != Psc::Number) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
    }
    return field->number_val<std::uint32_t>();
}
static double read_optional_double_field(const Psc::JSON* json, std::string_view key, double fallback) {
    auto field = json->get(key);
    if (field == nullptr) {
        return fallback;
    }
    if (field->valueType != Psc::Number) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
    }
    return field->number_val<double>();
}
void Map_Tile_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    if (that_json == nullptr || that_json->valueType != Psc::Object) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map tile config");
    }
    Map_Tile_Config_Data::from_base_json(that_json, not_exist_use_default_value);
    directories = read_optional_string_array(that_json, "directories");
    files = read_optional_string_array(that_json, "files");
    encoding = read_optional_string_field(that_json, "encoding");
}
Psc::JSON Map_Tile_Config::to_base_json() const {
    auto ret = Map_Tile_Config_Data::to_base_json();
    ret.append(write_string_array("directories", directories));
    ret.append(write_string_array("files", files));
    if (!encoding.empty()) {
        ret.append({"encoding", encoding});
    }
    return ret;
}
Psc::JSON Map_Tile_Config::to_public_json(std::string_view url) const {
    auto ret = Psc::JSON::object();
    ret.append({"url", std::string(url)});
    ret.append({"minimum_level", minimum_level});
    ret.append({"maximum_level", maximum_level});
    ret.append({"tile_size", tile_size});
    ret.append({"projection", Psc::to_string(projection)});
    ret.append({"y_axis", Psc::to_string(y_axis)});
    if (!encoding.empty()) {
        ret.append({"encoding", encoding});
    }
    return ret;
}
void Map_Tile_Source_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    if (that_json == nullptr || that_json->valueType != Psc::Object) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map tile source config");
    }
    Map_Tile_Source_Config_Data::from_base_json(that_json, not_exist_use_default_value);
    url = read_optional_string_field(that_json, "url");
    resource.from_base_json(that_json->get("resource"), not_exist_use_default_value);
}
Psc::JSON Map_Tile_Source_Config::to_base_json() const {
    auto ret = Map_Tile_Source_Config_Data::to_base_json();
    if (!url.empty()) {
        ret.append({"url", url});
    }
    ret.append({"resource", resource.to_base_json()});
    return ret;
}
Psc::JSON Map_Tile_Source_Config::to_public_json(std::string_view local_url) const {
    auto ret = Psc::JSON::object();
    ret.append({"key", key});
    ret.append({"name", name});
    ret.append({"tile_type", Psc::to_string(tile_type)});
    ret.append({"request_method", Psc::to_string(request_method)});
    ret.append({"resource", resource.to_public_json(local() ? local_url : url)});
    return ret;
}
static std::vector<Map_Tile_Source_Config> read_optional_tile_source_array(const Psc::JSON* json, std::string_view key,
                                                                           bool not_exist_use_default_value) {
    auto field = json->get(key);
    if (field == nullptr) {
        return {};
    }
    if (field->valueType != Psc::Array) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
    }
    std::vector<Map_Tile_Source_Config> ret;
    ret.reserve(field->children.size());
    for (const auto& child : field->children) {
        Map_Tile_Source_Config item;
        item.from_base_json(&child, not_exist_use_default_value);
        ret.push_back(std::move(item));
    }
    return ret;
}
static Psc::JSON write_tile_source_array(std::string_view key, const std::vector<Map_Tile_Source_Config>& list) {
    auto array = Psc::JSON::array();
    for (const auto& source : list) {
        array.append(source.to_base_json());
    }
    return Psc::JSON(key, array);
}
void Map_Resources_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    if (that_json == nullptr || that_json->valueType != Psc::Object) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_resources");
    }
    current_imagery_key = read_optional_string_field(that_json, "current_imagery_key");
    if (current_imagery_key.empty()) {
        current_imagery_key = "imagery";
    }
    tile_sources = read_optional_tile_source_array(that_json, "tile_sources", not_exist_use_default_value);
    if (!is_imagery_key(current_imagery_key)) {
        auto source = std::ranges::find_if(
        tile_sources, [](const Map_Tile_Source_Config& item) { return item.tile_type == Map_Tile_Type::imagery; });
        current_imagery_key = source == tile_sources.end() ? "" : source->key;
    }
}
Psc::JSON Map_Resources_Config::to_base_json() const {
    auto ret = Psc::JSON::object();
    ret.append({"current_imagery_key", current_imagery_key});
    ret.append(write_tile_source_array("tile_sources", tile_sources));
    return ret;
}
Psc::JSON Map_Resources_Config::to_public_json() const {
    auto ret = Psc::JSON::object();
    auto sources = Psc::JSON::array();
    auto imagery_sources = Psc::JSON::array();
    auto terrain_sources = Psc::JSON::array();
    auto terrain = Psc::JSON::object();
    bool has_terrain = false;
    for (const auto& source : tile_sources) {
        auto url = "/map/tile_sources/" + source.key + "/{z}/{x}/{y}";
        auto public_source = source.to_public_json(url);
        sources.append(public_source);
        if (source.tile_type == Map_Tile_Type::imagery) {
            imagery_sources.append(public_source);
        }
        else if (source.tile_type == Map_Tile_Type::terrain) {
            terrain_sources.append(public_source);
            if (!has_terrain) {
                terrain = *public_source.get("resource");
                has_terrain = true;
            }
        }
    }
    ret.append({"current_imagery_key", current_imagery_key});
    ret.append({"tile_sources", sources});
    ret.append({"imagery_sources", imagery_sources});
    ret.append({"terrain_sources", terrain_sources});
    if (has_terrain) {
        ret.append({"terrain", std::move(terrain)});
    }
    return ret;
}
bool Map_Resources_Config::is_imagery_key(std::string_view key) const {
    return std::ranges::any_of(tile_sources, [key](const Map_Tile_Source_Config& source) {
        return source.tile_type == Map_Tile_Type::imagery && source.key == key;
    });
}
bool Map_Resources_Config::is_terrain_key(std::string_view key) const {
    return std::ranges::any_of(tile_sources, [key](const Map_Tile_Source_Config& source) {
        return source.tile_type == Map_Tile_Type::terrain && source.key == key;
    });
}
bool Map_View_Config::is_scene_mode(std::string_view mode) {
    return mode == "2d" || mode == "2.5d" || mode == "3d";
}
void Map_Tile_View_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    if (that_json == nullptr) {
        return;
    }
    if (that_json->valueType != Psc::Object) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_tile_view");
    }
    current_imagery_key = read_optional_string_field(that_json, "current_imagery_key", current_imagery_key);
    current_terrain_key = read_optional_string_field(that_json, "current_terrain_key", current_terrain_key);
    tile_zoom_mode = Psc::to_enum<Map_Tile_Zoom_Mode>(
    read_optional_string_field(that_json, "tile_zoom_mode", Psc::to_string(tile_zoom_mode)));
    tile_display_maximum_level = std::clamp(
    read_optional_uint32_field(that_json, "tile_display_maximum_level", tile_display_maximum_level), 0u, 24u);
}
Psc::JSON Map_Tile_View_Config::to_base_json() const {
    auto ret = Psc::JSON::object();
    ret.append({"current_imagery_key", current_imagery_key});
    ret.append({"current_terrain_key", current_terrain_key});
    ret.append({"tile_zoom_mode", Psc::to_string(tile_zoom_mode)});
    ret.append({"tile_display_maximum_level", tile_display_maximum_level});
    return ret;
}
void Map_View_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    if (that_json == nullptr) {
        return;
    }
    if (that_json->valueType != Psc::Object) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_view");
    }
    auto next_scene_mode = read_optional_string_field(that_json, "scene_mode", scene_mode);
    if (!is_scene_mode(next_scene_mode)) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "scene_mode");
    }
    scene_mode = next_scene_mode;
    map2d.from_base_json(that_json->get("map2d"), not_exist_use_default_value);
    map3d.from_base_json(that_json->get("map3d"), not_exist_use_default_value);
    camera.from_base_json(that_json->get("camera"), not_exist_use_default_value);
    base_station_fly_to_height_offset_meters =
        std::max(100.0, read_optional_double_field(that_json, "base_station_fly_to_height_offset_meters", base_station_fly_to_height_offset_meters));
}
Psc::JSON Map_View_Config::to_base_json() const {
    auto ret = Psc::JSON::object();
    ret.append({"scene_mode", scene_mode});
    ret.append({"map2d", map2d.to_base_json()});
    ret.append({"map3d", map3d.to_base_json()});
    ret.append({"camera", camera.to_base_json()});
    ret.append({"base_station_fly_to_height_offset_meters", base_station_fly_to_height_offset_meters});
    return ret;
}
void Cesium_Graphics_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    if (that_json == nullptr)
        return;
    if (that_json->valueType != Psc::Object)
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "cesium_graphics");
    settings.write([&](auto& value) {
        value.from_base_json(that_json, not_exist_use_default_value);
        value.target_frame_rate = std::clamp(value.target_frame_rate, 1u, 120u);
        value.resolution_scale = std::clamp(value.resolution_scale, 0.2, 1.5);
        value.msaa_samples = std::clamp(value.msaa_samples, 1u, 8u);
        value.maximum_screen_space_error = std::clamp(value.maximum_screen_space_error, 1u, 16u);
        value.terrain_maximum_level = std::clamp(value.terrain_maximum_level, 0u, 24u);
        value.terrain_cache_tiles = std::clamp(value.terrain_cache_tiles, 16u, 2048u);
        value.performance_panel_x = std::max(0.0, value.performance_panel_x);
        value.performance_panel_y = std::max(0.0, value.performance_panel_y);
        value.view_axes_panel_y = std::max(0.0, value.view_axes_panel_y);
        value.view_axes_panel_size = std::clamp(value.view_axes_panel_size, 48.0, 240.0);
        value.occlusion_sample_spacing_meters = std::clamp(value.occlusion_sample_spacing_meters, 10.0, 5000.0);
    });
}
Psc::JSON Cesium_Graphics_Config::to_base_json() const {
    return settings.read([](const auto& value) {
        return value.to_base_json();
    });
}
Map_Model_Item_Config::Map_Model_Item_Config(std::string url) : url(std::move(url)) {
}
Map_Model_Item_Config::Map_Model_Item_Config(std::string url, double built_in_size, double heading, double pitch,
                                             double roll) :
    url(std::move(url)), built_in_size(built_in_size), heading_offset_degrees(heading), pitch_offset_degrees(pitch),
    roll_offset_degrees(roll) {
}
void Map_Model_Item_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    if (that_json == nullptr || that_json->valueType != Psc::Object) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_model_item");
    }
    url = read_optional_string_field(that_json, "url", url);
    built_in_size = read_optional_double_field(that_json, "built_in_size", built_in_size);
    heading_offset_degrees = read_optional_double_field(that_json, "heading_offset_degrees", heading_offset_degrees);
    pitch_offset_degrees = read_optional_double_field(that_json, "pitch_offset_degrees", pitch_offset_degrees);
    roll_offset_degrees = read_optional_double_field(that_json, "roll_offset_degrees", roll_offset_degrees);
}
Psc::JSON Map_Model_Item_Config::to_base_json() const {
    auto ret = Psc::JSON::object();
    ret.append({"url", url});
    ret.append({"built_in_size", built_in_size});
    ret.append({"heading_offset_degrees", heading_offset_degrees});
    ret.append({"pitch_offset_degrees", pitch_offset_degrees});
    ret.append({"roll_offset_degrees", roll_offset_degrees});
    return ret;
}
Map_Model_Config::Map_Model_Config() : aircraft_models(make_default_aircraft_models()) {
}
void Map_Model_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) {
    if (that_json == nullptr) {
        return;
    }
    if (that_json->valueType != Psc::Object) {
        throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_models");
    }
    aircraft_model.from_base_json(that_json->get("aircraft_model"), not_exist_use_default_value);
    aircraft_models = read_optional_model_object_field(that_json, "aircraft_models", make_default_aircraft_models(),
                                                       not_exist_use_default_value);
    base_station_model.from_base_json(that_json->get("base_station_model"), not_exist_use_default_value);
    device_model.from_base_json(that_json->get("device_model"), not_exist_use_default_value);
}
Psc::JSON Map_Model_Config::to_base_json() const {
    auto ret = Psc::JSON::object();
    ret.append({"aircraft_model", aircraft_model.to_base_json()});
    ret.append(write_model_object("aircraft_models", aircraft_models));
    ret.append({"base_station_model", base_station_model.to_base_json()});
    ret.append({"device_model", device_model.to_base_json()});
    return ret;
}
void Mode_ACS_Config::server(Global* g) {
    data_source_config.server(g);
    data_feed_config.server(g);
}
void Config::save() {
    auto& global = *Global::instance();
    for (std::size_t index = 0; index < static_cast<std::size_t>(Config_Section::count); ++index) {
        const auto section = static_cast<Config_Section>(index);
        write_config_section(section, config_section_value(global, section));
    }
}
void Config::save(Config_Section section) {
    auto& global = *Global::instance();
    write_config_section(section, config_section_value(global, section));
}
void Config::save(Config_Section section, const Psc::JSON& value) {
    write_config_section(section, value);
}
void Config::merge(Config_Section section, const Psc::JSON& value) {
    std::lock_guard lock(config_section_mutexes[static_cast<std::size_t>(section)]);
    auto current = read_existing_config_section_unlocked(section);
    if (current.valueType != Psc::Object || value.valueType != Psc::Object)
        throw std::invalid_argument("config merge requires object");
    merge_json_object(current, value);
    write_config_section_unlocked(section, current);
}
