#pragma once
#include <fstream>
#include <map>
#include <memory>
#include <unistd.h>
#include <unordered_map>
#include <vector>

#include "LR11x0Interface.h"
#include "Module.h"
#include "mesh/generated/meshtastic/mesh.pb.h"
#ifndef ARCH_PORTDUINO_WASM
// The browser (WASM) node configures the radio via the wasm_set_lora_* setters
// instead of a YAML file, so it has no yaml-cpp dependency. Everything that uses
// YAML below (emit_yaml / loadConfig / readGPIOFromYaml) is likewise guarded.
#include "yaml-cpp/yaml.h"
#endif

extern struct portduino_status_struct {
    bool LoRa_in_error = false;
    _meshtastic_HardwareModel hardwareModel = meshtastic_HardwareModel_PORTDUINO;
} portduino_status;

#include "platform/portduino/USBHal.h"

// Product strings for auto-configuration
// {"PRODUCT_STRING", "CONFIG.YAML"}
// YAML paths are relative to `meshtastic/available.d`
inline const std::unordered_map<std::string, std::string> configProducts = {
    {"MESHTOAD", "lora-usb-meshtoad-e22.yaml"},
    {"MESHSTICK", "lora-meshstick-1262.yaml"},
    {"MESHADV-PI", "lora-MeshAdv-900M30S.yaml"},
    {"MeshAdv Mini", "lora-MeshAdv-Mini-900M22S.yaml"},
    {"POWERPI", "lora-MeshAdv-900M30S.yaml"},
    {"RAK6421-13300-S1", "lora-RAK6421-13300-slot1.yaml"},
    {"RAK6421-13300-S2", "lora-RAK6421-13300-slot2.yaml"}};

enum screen_modules { no_screen, x11, fb, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9486, ili9488, hx8357d, hub75 };
enum touchscreen_modules { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 };
enum portduino_log_level { level_error, level_warn, level_info, level_debug, level_trace };
enum lora_module_enum {
    use_simradio,
    use_autoconf,
    use_rf95,
    use_sx1262,
    use_sx1268,
    use_sx1280,
    use_lr1110,
    use_lr1120,
    use_lr1121,
    use_llcc68,
    use_lr2021
};

struct pinMapping {
    std::string config_section;
    std::string config_name;
    int pin = RADIOLIB_NC;
    int gpiochip;
    int line;
    bool enabled = false;
    bool default_high = false;
};

extern std::ofstream traceFile;
extern std::ofstream JSONFile;

extern std::unique_ptr<Ch341Hal> ch341Hal;
int initGPIOPin(int pinNum, const std::string &gpioChipname, int line);
bool loadConfig(const char *configPath);
static bool ends_with(std::string_view str, std::string_view suffix);
void getMacAddr(uint8_t *dmac);
bool MAC_from_string(std::string mac_str, uint8_t *dmac);
#ifndef ARCH_PORTDUINO_WASM
void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault = RADIOLIB_NC);
#endif
std::string exec(const char *cmd);

extern struct portduino_config_struct {
    // Lora
    std::map<lora_module_enum, std::string> loraModules = {{use_simradio, "sim"},  {use_autoconf, "auto"}, {use_rf95, "RF95"},
                                                           {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"}, {use_sx1280, "sx1280"},
                                                           {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"},
                                                           {use_llcc68, "LLCC68"}, {use_lr2021, "lr2021"}};

    std::map<screen_modules, std::string> screen_names = {{x11, "X11"},         {fb, "FB"},           {st7789, "ST7789"},
                                                          {st7735, "ST7735"},   {st7735s, "ST7735S"}, {st7796, "ST7796"},
                                                          {ili9341, "ILI9341"}, {ili9342, "ILI9342"}, {ili9486, "ILI9486"},
                                                          {ili9488, "ILI9488"}, {hx8357d, "HX8357D"}, {hub75, "HUB75"}};

    lora_module_enum lora_module;
    bool has_rfswitch_table = false;
    uint32_t rfswitch_dio_pins[5] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
    Module::RfSwitchMode_t rfswitch_table[8];
    bool force_simradio = false;
    bool has_device_id = false;
    uint8_t device_id[16] = {0};
    std::string lora_spi_dev = "";
    std::string lora_usb_serial_num = "";
    int lora_spi_dev_int = 0;
    int lora_default_gpiochip = 0;
    int sx126x_max_power = 22;
    int sx128x_max_power = 13;
    int lr1110_max_power = 22;
    int lr1120_max_power = 13;
    int lr2021_max_power = 22;
    int lr2021_max_power_hf = 12;
    int rf95_max_power = 20;
    bool dio2_as_rf_switch = false;
    int dio3_tcxo_voltage = 0;
    int lora_usb_pid = 0x5512;
    int lora_usb_vid = 0x1A86;
    int spiSpeed = 2000000;
    int num_pa_points = 1; // default to 1 point, with 0 gain
    uint16_t tx_gain_lora[22] = {0};
    pinMapping lora_cs_pin = {"Lora", "CS"};
    pinMapping lora_irq_pin = {"Lora", "IRQ"};
    pinMapping lora_busy_pin = {"Lora", "Busy"};
    pinMapping lora_reset_pin = {"Lora", "Reset"};
    pinMapping lora_txen_pin = {"Lora", "TXen"};
    pinMapping lora_rxen_pin = {"Lora", "RXen"};
    pinMapping lora_sx126x_ant_sw_pin = {"Lora", "SX126X_ANT_SW"};
    pinMapping lora_pa_detect_pin = {"Lora", "GPIO_DETECT_PA"};
    std::vector<pinMapping> extra_pins = {};

    // GPS
    bool has_gps = false;
    std::string gps_serial_path = "";
    std::string gpsd_host = "";
    int gpsd_port = 2947;

    // I2C
    std::string i2cdev = "";

    // Display
    std::string display_spi_dev = "";
    int display_spi_dev_int = 0;
    int displayBusFrequency = 40000000;
    screen_modules displayPanel = no_screen;
    int displayWidth = 0;
    int displayHeight = 0;
    bool displayRGBOrder = false;
    bool displayBacklightInvert = false;
    bool displayRotate = false;
    int displayOffsetRotate = 1;
    bool displayInvert = false;
    int displayOffsetX = 0;
    int displayOffsetY = 0;
    pinMapping displayDC = {"Display", "DC"};
    pinMapping displayCS = {"Display", "CS"};
    pinMapping displayBacklight = {"Display", "Backlight"};
    pinMapping displayBacklightPWMChannel = {"Display", "BacklightPWMChannel"};
    pinMapping displayReset = {"Display", "Reset"};

    // Display -> HUB75 (Raspberry Pi RGB matrix via hzeller/rpi-rgb-led-matrix).
    // These mirror rgb_matrix::RGBMatrix::Options + RuntimeOptions; the library owns the GPIO
    // pins (chosen by hub75_hardware_mapping), so there are no per-pin mappings here.
    std::string hub75_hardware_mapping = "regular";
    int hub75_rows = 64;
    int hub75_cols = 64;
    int hub75_chain_length = 1;
    int hub75_parallel = 1;
    int hub75_pwm_bits = 11;
    int hub75_pwm_lsb_nanoseconds = 130;
    int hub75_brightness = 100; // percent, 1..100
    int hub75_scan_mode = 0;
    int hub75_row_address_type = 0;
    int hub75_multiplexing = 0;
    bool hub75_disable_hardware_pulsing = false;
    bool hub75_show_refresh_rate = false;
    bool hub75_inverse_colors = false;
    std::string hub75_led_rgb_sequence = "RGB";
    std::string hub75_pixel_mapper_config = "";
    std::string hub75_panel_type = "";
    int hub75_limit_refresh_rate_hz = 0;
    int hub75_gpio_slowdown = 1; // RuntimeOptions; higher for faster Pis / long cables

    // Touchscreen
    std::string touchscreen_spi_dev = "";
    int touchscreen_spi_dev_int = 0;
    touchscreen_modules touchscreenModule = no_touchscreen;
    int touchscreenI2CAddr = -1;
    int touchscreenBusFrequency = 1000000;
    int touchscreenRotate = -1;
    pinMapping touchscreenCS = {"Touchscreen", "CS"};
    pinMapping touchscreenIRQ = {"Touchscreen", "IRQ"};

    // Input
    std::string keyboardDevice = "";
    std::string pointerDevice = "";
    std::string joystickDevice = "";
    // Joystick/gamepad button map: evdev button code -> lowercase action name
    // ("select", "cancel", "back", "up", "down", "left", "right", "user").
    // Empty means the LinuxJoystick driver uses its built-in defaults.
    std::map<int, std::string> joystickButtons;
    int tbDirection;
    pinMapping userButtonPin = {"Input", "User"};
    pinMapping tbUpPin = {"Input", "TrackballUp"};
    pinMapping tbDownPin = {"Input", "TrackballDown"};
    pinMapping tbLeftPin = {"Input", "TrackballLwft"};
    pinMapping tbRightPin = {"Input", "TrackballRight"};
    pinMapping tbPressPin = {"Input", "TrackballPress"};

    // Logging
    portduino_log_level logoutputlevel = level_debug;
    std::string traceFilename;
    bool ascii_logs = !isatty(1);
    bool ascii_logs_explicit = false;

    std::string JSONFilename;
    int JSONFileRotate = 0;
    meshtastic_PortNum JSONFilter = (_meshtastic_PortNum)0;

    // Webserver
    std::string webserver_root_path = "";
    std::string webserver_ssl_key_path = "/etc/meshtasticd/ssl/private_key.pem";
    std::string webserver_ssl_cert_path = "/etc/meshtasticd/ssl/certificate.pem";
    int webserverport = -1;

    // HostMetrics
    std::string hostMetrics_user_command = "";
    int hostMetrics_interval = 0;
    int hostMetrics_channel = 0;

    // config
    bool has_config_overrides = false;
    int configDisplayMode = 0;
    bool has_configDisplayMode = false;
    std::string statusMessage = "";
    bool has_statusMessage = false;
    bool enable_UDP = false;

    // General
    std::string mac_address = "";
    bool mac_address_explicit = false;
    std::string mac_address_source = "";
    int api_port = -1;
    std::string config_directory = "";
    std::string available_directory = "/etc/meshtasticd/available.d/";
    int maxtophone = 100;
    int MaxNodes = 200;

    std::unordered_map<std::string, std::string> hat_plus_custom_fields;

    pinMapping *all_pins[21] = {&lora_cs_pin,
                                &lora_irq_pin,
                                &lora_busy_pin,
                                &lora_reset_pin,
                                &lora_txen_pin,
                                &lora_rxen_pin,
                                &lora_sx126x_ant_sw_pin,
                                &lora_pa_detect_pin,
                                &displayDC,
                                &displayCS,
                                &displayBacklight,
                                &displayBacklightPWMChannel,
                                &displayReset,
                                &touchscreenCS,
                                &touchscreenIRQ,
                                &userButtonPin,
                                &tbUpPin,
                                &tbDownPin,
                                &tbLeftPin,
                                &tbRightPin,
                                &tbPressPin};

#ifndef ARCH_PORTDUINO_WASM
    std::string emit_yaml()
    {
        YAML::Emitter out;
        out << YAML::BeginMap;

        // Lora
        out << YAML::Key << "Lora" << YAML::Value << YAML::BeginMap;
        out << YAML::Key << "Module" << YAML::Value << loraModules[lora_module];

        for (const auto *lora_pin : all_pins) {
            if (lora_pin->config_section == "Lora" && lora_pin->enabled) {
                out << YAML::Key << lora_pin->config_name << YAML::Value << YAML::BeginMap;
                out << YAML::Key << "pin" << YAML::Value << lora_pin->pin;
                out << YAML::Key << "line" << YAML::Value << lora_pin->line;
                out << YAML::Key << "gpiochip" << YAML::Value << lora_pin->gpiochip;
                out << YAML::EndMap; // User
            }
        }

        if (sx126x_max_power != 22)
            out << YAML::Key << "SX126X_MAX_POWER" << YAML::Value << sx126x_max_power;
        if (sx128x_max_power != 13)
            out << YAML::Key << "SX128X_MAX_POWER" << YAML::Value << sx128x_max_power;
        if (lr1110_max_power != 22)
            out << YAML::Key << "LR1110_MAX_POWER" << YAML::Value << lr1110_max_power;
        if (lr1120_max_power != 13)
            out << YAML::Key << "LR1120_MAX_POWER" << YAML::Value << lr1120_max_power;
        if (lr2021_max_power != 22)
            out << YAML::Key << "LR2021_MAX_POWER" << YAML::Value << lr2021_max_power;
        if (lr2021_max_power_hf != 12)
            out << YAML::Key << "LR2021_MAX_POWER_HF" << YAML::Value << lr2021_max_power_hf;
        if (rf95_max_power != 20)
            out << YAML::Key << "RF95_MAX_POWER" << YAML::Value << rf95_max_power;

        if (num_pa_points > 1) {
            out << YAML::Key << "TX_GAIN_LORA" << YAML::Value << YAML::Flow << YAML::BeginSeq;
            for (int i = 0; i < num_pa_points; i++) {
                out << YAML::Value << tx_gain_lora[i];
            }
            out << YAML::EndSeq;
        } else if (tx_gain_lora[0] != 0) {
            out << YAML::Key << "TX_GAIN_LORA" << YAML::Value << tx_gain_lora[0];
        }

        if (dio2_as_rf_switch)
            out << YAML::Key << "DIO2_AS_RF_SWITCH" << YAML::Value << dio2_as_rf_switch;
        if (dio3_tcxo_voltage != 0)
            out << YAML::Key << "DIO3_TCXO_VOLTAGE" << YAML::Value << YAML::Precision(3) << (float)dio3_tcxo_voltage / 1000;
        if (lora_usb_pid != 0x5512)
            out << YAML::Key << "USB_PID" << YAML::Value << YAML::Hex << lora_usb_pid;
        if (lora_usb_vid != 0x1A86)
            out << YAML::Key << "USB_VID" << YAML::Value << YAML::Hex << lora_usb_vid;
        if (lora_spi_dev != "" && !(lora_spi_dev == "/dev/spidev0.0" && lora_module == use_autoconf)) {
            if (lora_spi_dev.find("/dev/") != std::string::npos)
                lora_spi_dev = lora_spi_dev.substr(5);
            out << YAML::Key << "spidev" << YAML::Value << lora_spi_dev;
        }
        if (lora_usb_serial_num != "")
            out << YAML::Key << "USB_Serialnum" << YAML::Value << lora_usb_serial_num;
        if (spiSpeed != 2000000)
            out << YAML::Key << "spiSpeed" << YAML::Value << spiSpeed;
        if (rfswitch_dio_pins[0] != RADIOLIB_NC) {
            out << YAML::Key << "rfswitch_table" << YAML::Value << YAML::BeginMap;

            out << YAML::Key << "pins";
            out << YAML::Value << YAML::Flow << YAML::BeginSeq;

            for (int i = 0; i < 5; i++) {
                // set up the pin array first
                if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO5)
                    out << "DIO5";
                if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO6)
                    out << "DIO6";
                if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO7)
                    out << "DIO7";
                if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO8)
                    out << "DIO8";
                if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO10)
                    out << "DIO10";
            }
            out << YAML::EndSeq;

            for (int i = 0; i < 7; i++) {
                switch (i) {
                case 0:
                    out << YAML::Key << "MODE_STBY";
                    break;
                case 1:
                    out << YAML::Key << "MODE_RX";
                    break;
                case 2:
                    out << YAML::Key << "MODE_TX";
                    break;
                case 3:
                    out << YAML::Key << "MODE_TX_HP";
                    break;
                case 4:
                    out << YAML::Key << "MODE_TX_HF";
                    break;
                case 5:
                    out << YAML::Key << "MODE_GNSS";
                    break;
                case 6:
                    out << YAML::Key << "MODE_WIFI";
                    break;
                }

                out << YAML::Value << YAML::Flow << YAML::BeginSeq;
                for (int j = 0; j < 5; j++) {
                    if (rfswitch_table[i].values[j] == HIGH) {
                        out << "HIGH";
                    } else {
                        out << "LOW";
                    }
                }
                out << YAML::EndSeq;
            }
            out << YAML::EndMap; // rfswitch_table
        }
        out << YAML::EndMap; // Lora

        if (!extra_pins.empty()) {
            out << YAML::Key << "GPIO" << YAML::Value << YAML::BeginMap;
            out << YAML::Key << "ExtraPins" << YAML::Value << YAML::BeginSeq;
            for (auto extra : extra_pins) {
                out << YAML::BeginMap;
                out << YAML::Key << "pin" << YAML::Value << extra.pin;
                out << YAML::Key << "line" << YAML::Value << extra.line;
                out << YAML::Key << "gpiochip" << YAML::Value << extra.gpiochip;
                out << YAML::EndMap;
            }
            out << YAML::EndSeq;
            out << YAML::EndMap; // GPIO
        }

        if (has_gps) {
            out << YAML::Key << "GPS" << YAML::Value << YAML::BeginMap;
            if (!gpsd_host.empty()) {
                out << YAML::Key << "GpsdHost" << YAML::Value << gpsd_host;
                if (gpsd_port != 2947)
                    out << YAML::Key << "GpsdPort" << YAML::Value << gpsd_port;
            } else if (!gps_serial_path.empty()) {
                out << YAML::Key << "SerialPath" << YAML::Value << gps_serial_path;
            }
            out << YAML::EndMap; // GPS
        }

        if (i2cdev != "") {
            out << YAML::Key << "I2C" << YAML::Value << YAML::BeginMap;
            out << YAML::Key << "I2CDevice" << YAML::Value << i2cdev;
            out << YAML::EndMap; // I2C
        }

        // Display
        if (displayPanel != no_screen) {
            out << YAML::Key << "Display" << YAML::Value << YAML::BeginMap;
            for (const auto &screen_name : screen_names) {
                if (displayPanel == screen_name.first)
                    out << YAML::Key << "Module" << YAML::Value << screen_name.second;
            }
            for (const auto *display_pin : all_pins) {
                if (display_pin->config_section == "Display" && display_pin->enabled) {
                    out << YAML::Key << display_pin->config_name << YAML::Value << YAML::BeginMap;
                    out << YAML::Key << "pin" << YAML::Value << display_pin->pin;
                    out << YAML::Key << "line" << YAML::Value << display_pin->line;
                    out << YAML::Key << "gpiochip" << YAML::Value << display_pin->gpiochip;
                    out << YAML::EndMap;
                }
            }
            out << YAML::Key << "spidev" << YAML::Value << display_spi_dev;
            out << YAML::Key << "BusFrequency" << YAML::Value << displayBusFrequency;
            if (displayWidth)
                out << YAML::Key << "Width" << YAML::Value << displayWidth;
            if (displayHeight)
                out << YAML::Key << "Height" << YAML::Value << displayHeight;
            if (displayRGBOrder)
                out << YAML::Key << "RGBOrder" << YAML::Value << true;
            if (displayBacklightInvert)
                out << YAML::Key << "BacklightInvert" << YAML::Value << true;
            if (displayRotate)
                out << YAML::Key << "Rotate" << YAML::Value << true;
            if (displayInvert)
                out << YAML::Key << "Invert" << YAML::Value << true;
            if (displayOffsetX)
                out << YAML::Key << "OffsetX" << YAML::Value << displayOffsetX;
            if (displayOffsetY)
                out << YAML::Key << "OffsetY" << YAML::Value << displayOffsetY;

            out << YAML::Key << "OffsetRotate" << YAML::Value << displayOffsetRotate;

            if (displayPanel == hub75) {
                out << YAML::Key << "HUB75" << YAML::Value << YAML::BeginMap;
                out << YAML::Key << "HardwareMapping" << YAML::Value << hub75_hardware_mapping;
                out << YAML::Key << "Rows" << YAML::Value << hub75_rows;
                out << YAML::Key << "Cols" << YAML::Value << hub75_cols;
                out << YAML::Key << "ChainLength" << YAML::Value << hub75_chain_length;
                out << YAML::Key << "Parallel" << YAML::Value << hub75_parallel;
                out << YAML::Key << "PWMBits" << YAML::Value << hub75_pwm_bits;
                out << YAML::Key << "PWMLSBNanoseconds" << YAML::Value << hub75_pwm_lsb_nanoseconds;
                out << YAML::Key << "Brightness" << YAML::Value << hub75_brightness;
                out << YAML::Key << "ScanMode" << YAML::Value << hub75_scan_mode;
                out << YAML::Key << "RowAddressType" << YAML::Value << hub75_row_address_type;
                out << YAML::Key << "Multiplexing" << YAML::Value << hub75_multiplexing;
                out << YAML::Key << "DisableHardwarePulsing" << YAML::Value << hub75_disable_hardware_pulsing;
                out << YAML::Key << "ShowRefreshRate" << YAML::Value << hub75_show_refresh_rate;
                out << YAML::Key << "InverseColors" << YAML::Value << hub75_inverse_colors;
                out << YAML::Key << "RGBSequence" << YAML::Value << hub75_led_rgb_sequence;
                out << YAML::Key << "PixelMapper" << YAML::Value << hub75_pixel_mapper_config;
                out << YAML::Key << "PanelType" << YAML::Value << hub75_panel_type;
                out << YAML::Key << "LimitRefreshRateHz" << YAML::Value << hub75_limit_refresh_rate_hz;
                out << YAML::Key << "GPIOSlowdown" << YAML::Value << hub75_gpio_slowdown;
                out << YAML::EndMap; // HUB75
            }

            out << YAML::EndMap; // Display
        }

        // Touchscreen
        if (touchscreen_spi_dev != "") {
            out << YAML::Key << "Touchscreen" << YAML::Value << YAML::BeginMap;
            out << YAML::Key << "spidev" << YAML::Value << touchscreen_spi_dev;
            out << YAML::Key << "BusFrequency" << YAML::Value << touchscreenBusFrequency;
            switch (touchscreenModule) {
            case xpt2046:
                out << YAML::Key << "Module" << YAML::Value << "XPT2046";
            case stmpe610:
                out << YAML::Key << "Module" << YAML::Value << "STMPE610";
            case gt911:
                out << YAML::Key << "Module" << YAML::Value << "GT911";
            case ft5x06:
                out << YAML::Key << "Module" << YAML::Value << "FT5x06";
            }
            for (const auto *touchscreen_pin : all_pins) {
                if (touchscreen_pin->config_section == "Touchscreen" && touchscreen_pin->enabled) {
                    out << YAML::Key << touchscreen_pin->config_name << YAML::Value << YAML::BeginMap;
                    out << YAML::Key << "pin" << YAML::Value << touchscreen_pin->pin;
                    out << YAML::Key << "line" << YAML::Value << touchscreen_pin->line;
                    out << YAML::Key << "gpiochip" << YAML::Value << touchscreen_pin->gpiochip;
                    out << YAML::EndMap;
                }
            }
            if (touchscreenRotate != -1)
                out << YAML::Key << "Rotate" << YAML::Value << touchscreenRotate;
            if (touchscreenI2CAddr != -1)
                out << YAML::Key << "I2CAddr" << YAML::Value << touchscreenI2CAddr;
            out << YAML::EndMap; // Touchscreen
        }

        // Input
        out << YAML::Key << "Input" << YAML::Value << YAML::BeginMap;
        if (keyboardDevice != "")
            out << YAML::Key << "KeyboardDevice" << YAML::Value << keyboardDevice;
        if (pointerDevice != "")
            out << YAML::Key << "PointerDevice" << YAML::Value << pointerDevice;
        if (joystickDevice != "")
            out << YAML::Key << "JoystickDevice" << YAML::Value << joystickDevice;
        if (!joystickButtons.empty()) {
            out << YAML::Key << "JoystickButtons" << YAML::Value << YAML::BeginMap;
            for (const auto &button : joystickButtons)
                out << YAML::Key << button.second << YAML::Value << button.first;
            out << YAML::EndMap;
        }

        for (const auto *input_pin : all_pins) {
            if (input_pin->config_section == "Input" && input_pin->enabled) {
                out << YAML::Key << input_pin->config_name << YAML::Value << YAML::BeginMap;
                out << YAML::Key << "pin" << YAML::Value << input_pin->pin;
                out << YAML::Key << "line" << YAML::Value << input_pin->line;
                out << YAML::Key << "gpiochip" << YAML::Value << input_pin->gpiochip;
                out << YAML::EndMap;
            }
        }
        if (tbDirection == 3)
            out << YAML::Key << "TrackballDirection" << YAML::Value << "FALLING";

        out << YAML::EndMap; // Input

        out << YAML::Key << "Logging" << YAML::Value << YAML::BeginMap;
        out << YAML::Key << "LogLevel" << YAML::Value;
        switch (logoutputlevel) {
        case level_error:
            out << "error";
            break;
        case level_warn:
            out << "warn";
            break;
        case level_info:
            out << "info";
            break;
        case level_debug:
            out << "debug";
            break;
        case level_trace:
            out << "trace";
            break;
        }
        if (traceFilename != "")
            out << YAML::Key << "TraceFile" << YAML::Value << traceFilename;
        if (JSONFilename != "") {
            out << YAML::Key << "JSONFile" << YAML::Value << JSONFilename;
            if (JSONFileRotate != 0)
                out << YAML::Key << "JSONFileRotate" << YAML::Value << JSONFileRotate;

            if (JSONFilter == meshtastic_PortNum_TEXT_MESSAGE_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "textmessage";
            else if (JSONFilter == meshtastic_PortNum_TELEMETRY_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "telemetry";
            else if (JSONFilter == meshtastic_PortNum_NODEINFO_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "nodeinfo";
            else if (JSONFilter == meshtastic_PortNum_POSITION_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "position";
            else if (JSONFilter == meshtastic_PortNum_WAYPOINT_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "waypoint";
            else if (JSONFilter == meshtastic_PortNum_NEIGHBORINFO_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "neighborinfo";
            else if (JSONFilter == meshtastic_PortNum_TRACEROUTE_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "traceroute";
            else if (JSONFilter == meshtastic_PortNum_DETECTION_SENSOR_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "detection";
            else if (JSONFilter == meshtastic_PortNum_PAXCOUNTER_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "paxcounter";
            else if (JSONFilter == meshtastic_PortNum_REMOTE_HARDWARE_APP)
                out << YAML::Key << "JSONFilter" << YAML::Value << "remotehardware";
        }
        if (ascii_logs_explicit) {
            out << YAML::Key << "AsciiLogs" << YAML::Value << ascii_logs;
        }
        out << YAML::EndMap; // Logging

        // Webserver
        if (webserver_root_path != "") {
            out << YAML::Key << "Webserver" << YAML::Value << YAML::BeginMap;
            out << YAML::Key << "RootPath" << YAML::Value << webserver_root_path;
            out << YAML::Key << "SSLKey" << YAML::Value << webserver_ssl_key_path;
            out << YAML::Key << "SSLCert" << YAML::Value << webserver_ssl_cert_path;
            out << YAML::Key << "Port" << YAML::Value << webserverport;
            out << YAML::EndMap; // Webserver
        }

        // HostMetrics
        if (hostMetrics_user_command != "") {
            out << YAML::Key << "HostMetrics" << YAML::Value << YAML::BeginMap;
            out << YAML::Key << "UserStringCommand" << YAML::Value << hostMetrics_user_command;
            out << YAML::Key << "ReportInterval" << YAML::Value << hostMetrics_interval;
            out << YAML::Key << "Channel" << YAML::Value << hostMetrics_channel;

            out << YAML::EndMap; // HostMetrics
        }

        // config
        if (has_config_overrides) {
            out << YAML::Key << "Config" << YAML::Value << YAML::BeginMap;
            if (has_configDisplayMode) {

                switch (configDisplayMode) {
                case meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR:
                    out << YAML::Key << "DisplayMode" << YAML::Value << "TWOCOLOR";
                    break;
                case meshtastic_Config_DisplayConfig_DisplayMode_INVERTED:
                    out << YAML::Key << "DisplayMode" << YAML::Value << "INVERTED";
                    break;
                case meshtastic_Config_DisplayConfig_DisplayMode_COLOR:
                    out << YAML::Key << "DisplayMode" << YAML::Value << "COLOR";
                    break;
                case meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT:
                    out << YAML::Key << "DisplayMode" << YAML::Value << "DEFAULT";
                    break;
                }
            }
            if (has_statusMessage) {
                out << YAML::Key << "StatusMessage" << YAML::Value << statusMessage;
            }
            if (enable_UDP) {
                out << YAML::Key << "EnableUDP" << YAML::Value << true;
            }

            out << YAML::EndMap; // Config
        }

        // General
        out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
        if (config_directory != "")
            out << YAML::Key << "ConfigDirectory" << YAML::Value << config_directory;
        if (api_port != -1)
            out << YAML::Key << "TCPPort" << YAML::Value << api_port;
        if (mac_address_explicit)
            out << YAML::Key << "MACAddress" << YAML::Value << mac_address;
        if (mac_address_source != "")
            out << YAML::Key << "MACAddressSource" << YAML::Value << mac_address_source;
        if (available_directory != "")
            out << YAML::Key << "AvailableDirectory" << YAML::Value << available_directory;
        out << YAML::Key << "MaxMessageQueue" << YAML::Value << maxtophone;
        out << YAML::Key << "MaxNodes" << YAML::Value << MaxNodes;
        out << YAML::EndMap; // General
        return out.c_str();
    }
#endif // !ARCH_PORTDUINO_WASM
} portduino_config;
