#include "RadioSet.h"
#include "Str.h"

#include <cstdio>
#include <ctype.h>
#include <tt_lvgl_toolbar.h>

#include <initializer_list>

#include "tt_app_alertdialog.h"

constexpr const char* TAG = "RadioSet";

void crash(const char* const message) {
    tt_app_alertdialog_start("RadioSet has crashed!", message, nullptr, 0);
}

// Debug function which colors all children randomly
// TODO: Remove before flight
void clownvomit(lv_obj_t *obj) {
    static auto rng = []() {
        static int color = 0xC0FE;
        const int a = 4711;
        const int m = 0x10001;
        color = (a * color) % m;
        return color;
    };

    const int darken =  0x0E0E0E;
    const int lighten = 0xFEFEFE;
    lv_obj_set_style_bg_color(obj, lv_color_hex(rng() & darken), 0);

    uint32_t i;
    for(i = 0; i < lv_obj_get_child_count(obj); i++) {
        lv_obj_t * child = lv_obj_get_child(obj, i);
        lv_obj_set_style_bg_color(child, lv_color_hex(rng() & darken), 0);
        lv_obj_set_style_border_color(child, lv_color_hex(rng() | lighten), 0);
        lv_obj_set_style_border_width(child, 1, 0);
        clownvomit(child);
    }
}
/*
void scrollbar_highlight(lv_event_t * e) {
    lv_obj_t * obj = lv_event_get_target(e);
}*/

char *const toString(Modulation m) {
    switch (m) {
        case MODULATION_NONE:
            return "None";
        case MODULATION_LORA:
            return "LoRa";
        case MODULATION_FSK:
            return "FSK";
        case MODULATION_LRFHSS:
            return "LR-FHSS";
        default:
            break;
    }
    crash("Unknown modulation passed.");
    return "Unknown";
}

char *const toString(RadioParameter p) {
    switch (p) {
        case RADIO_POWER:
            return "Power";
        case RADIO_FREQUENCY:
            return "Center Frequency";
        case RADIO_BANDWIDTH:
            return "Bandwidth";
        case RADIO_SPREADFACTOR:
            return "Spread Factor";
        case RADIO_CODINGRATE:
            return "Coding Rate";
        case RADIO_SYNCWORD:
            return "Sync Word";
        case RADIO_PREAMBLES:
            return "Preamble Length";
        case RADIO_FREQDIV:
            return "Frequency Deviation";
        case RADIO_DATARATE:
            return "Data Rate";
        case RADIO_ADDRWIDTH:
            return "Address Width";
        case RADIO_NARROWGRID:
            return "Narrow Grid";
        default:
            break;
    }
    crash("Unknown parameter passed.");
    return "Unknown";
}

class TermView {

};


static lv_obj_t* initGridDropdownInput(lv_obj_t *container, int row, const char* const label, const char* const items) {
    lv_obj_t* label_obj = lv_label_create(container);
    lv_label_set_text(label_obj, label);
    lv_obj_set_grid_cell(label_obj,
                         LV_GRID_ALIGN_STRETCH, 0, 1,
                         LV_GRID_ALIGN_CENTER, row, 1);
    lv_obj_set_size(label_obj, lv_pct(100), LV_SIZE_CONTENT);

    lv_obj_t* input = lv_dropdown_create(container);
    lv_obj_set_grid_cell(input,
                         LV_GRID_ALIGN_STRETCH, 1, 1,
                         LV_GRID_ALIGN_CENTER, row, 1);
    lv_obj_set_size(input, lv_pct(100), LV_SIZE_CONTENT);
    lv_dropdown_set_options(input, items);

    return input;
}

static lv_obj_t* initGridTextInput(lv_obj_t *container, int row, const char* const label, const char* const defval, const char* const unit) {
    const int height = LV_SIZE_CONTENT;
    lv_obj_t* label_obj = lv_label_create(container);
    lv_label_set_text(label_obj, label);
    lv_obj_set_grid_cell(label_obj,
                         LV_GRID_ALIGN_STRETCH, 0, 1,
                         LV_GRID_ALIGN_CENTER, row, 1);
    lv_obj_set_size(label_obj, lv_pct(100), height);

    lv_obj_t* input = lv_textarea_create(container);
    lv_obj_set_grid_cell(input,
                         LV_GRID_ALIGN_STRETCH, 1, 1,
                         LV_GRID_ALIGN_CENTER, row, 1);
    lv_obj_set_size(input, lv_pct(100), height);
    lv_textarea_set_text(input, defval);
    lv_textarea_set_one_line(input, true);

    lv_obj_t* unit_obj = lv_label_create(container);
    lv_label_set_text(unit_obj, unit);
    lv_obj_set_grid_cell(unit_obj,
                         LV_GRID_ALIGN_STRETCH, 2, 1,
                         LV_GRID_ALIGN_CENTER, row, 1);
    lv_obj_set_size(unit_obj, lv_pct(100), height);
    lv_obj_set_style_text_align(unit_obj , LV_TEXT_ALIGN_CENTER, 0);

    return input;
}

struct ParameterInput {
    const RadioHandle handle;
    const RadioParameter param;

    ParameterInput(RadioHandle handle, const RadioParameter param)
        : handle(handle)
        , param(param) {}
    virtual void updatePreview() = 0;
};

struct NumericParameterInput : public ParameterInput {

    lv_obj_t* label = nullptr;
    lv_obj_t* input = nullptr;
    lv_obj_t* unitlabel = nullptr;
    char* fmt;

    NumericParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, char* fmt = "%f", char* unit_override = nullptr)
    : ParameterInput(handle, param)
    , fmt(fmt) {
        initUi(container, row, unit_override);
        loadFromRadio();
    }

    void loadFromRadio() {

    }

    void initUi(lv_obj_t* container, int row, char* unit_override) {
        const int height = LV_SIZE_CONTENT;
        label = lv_label_create(container);
        lv_label_set_text(label, toString(param));
        lv_obj_set_grid_cell(label,
                             LV_GRID_ALIGN_STRETCH, 0, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(label, lv_pct(100), height);

        input = lv_textarea_create(container);
        lv_obj_set_grid_cell(input,
                             LV_GRID_ALIGN_STRETCH, 1, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(input, lv_pct(100), height);
        //TODO: LOAD VALUE
        //lv_textarea_set_text(input, defval);
        lv_textarea_set_one_line(input, true);

        unitlabel = lv_label_create(container);
        lv_obj_set_grid_cell(unitlabel,
                             LV_GRID_ALIGN_STRETCH, 2, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(unitlabel, lv_pct(100), height);
        lv_obj_set_style_text_align(unitlabel , LV_TEXT_ALIGN_CENTER, 0);

        if (unit_override) {
            lv_label_set_text(unitlabel, unit_override);
        } else {
            char unit[64] = {0};
            tt_hal_radio_get_parameter_unit_str(handle, param, unit, sizeof(unit));
            lv_label_set_text(unitlabel, unit);
        }
    }

    virtual void updatePreview() override {}
};

struct SliderParameterInput : public ParameterInput {
    char* fmt = nullptr;
    lv_obj_t* label = nullptr;
    lv_obj_t* slider = nullptr;
    lv_obj_t* preview = nullptr;

    SliderParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, int min, int max, char* fmt = "%i")
        : ParameterInput(handle, param)
        , fmt(fmt) {
        initUi(container, row, min, max);
    }

    void initUi(lv_obj_t* container, int row, int min, int max) {
        label = lv_label_create(container);
        lv_label_set_text(label, toString(param));
        lv_obj_set_grid_cell(label,
                             LV_GRID_ALIGN_STRETCH, 0, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(label, lv_pct(100), LV_SIZE_CONTENT);

        slider = lv_slider_create(container);
        lv_obj_set_grid_cell(slider,
                             LV_GRID_ALIGN_STRETCH, 1, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(slider, lv_pct(100), 10);
        lv_slider_set_range(slider, min, max);

        preview = lv_label_create(container);
        lv_obj_set_grid_cell(preview,
                             LV_GRID_ALIGN_STRETCH, 2, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(preview, lv_pct(100), LV_SIZE_CONTENT);
        lv_obj_set_style_text_align(preview , LV_TEXT_ALIGN_LEFT, 0);
        updatePreview();
        lv_obj_add_event_cb(slider, [](lv_event_t * e) {
            lv_obj_t* slider = lv_event_get_target_obj(e);
            SliderParameterInput* self = (SliderParameterInput*)lv_event_get_user_data(e);
            self->updatePreview();
        }, LV_EVENT_VALUE_CHANGED, this);

    }

    virtual void updatePreview() override {
        char buf[64] = {0};
        lv_snprintf(buf, sizeof(buf), fmt, lv_slider_get_value(slider));
        lv_label_set_text(preview, buf);
    }
};


struct SliderSelectParameterInput : public ParameterInput {
    static constexpr float SELECT_END = -1;
    const float* selections;
    const size_t selectionsSize;

    char unit[64] = {0};
    char* fmt;
    lv_obj_t* label = nullptr;
    lv_obj_t* slider = nullptr;
    lv_obj_t* preview = nullptr;

    static constexpr size_t get_selection_num(const float selections[]) {
        constexpr size_t MAX_SELECTIONS = 32;

        if (selections) {
            for (size_t i = 0; i < MAX_SELECTIONS; ++i) {
                if (selections[i] == SELECT_END) {
                    return i;
                }
            }
        }
        return 0;
    }

    SliderSelectParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, const float selections[] = nullptr, char* fmt = "%f")
    : ParameterInput(handle, param)
    , selections(selections)
    , selectionsSize(get_selection_num(selections))
    , fmt(fmt) {
        initUi(container, row);
    }

    void initUi(lv_obj_t* container, int row) {
        label = lv_label_create(container);
        lv_label_set_text(label, toString(param));
        lv_obj_set_grid_cell(label,
                             LV_GRID_ALIGN_STRETCH, 0, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(label, lv_pct(100), LV_SIZE_CONTENT);

        slider = lv_slider_create(container);
        lv_obj_set_grid_cell(slider,
                             LV_GRID_ALIGN_STRETCH, 1, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(slider, lv_pct(100), 10);
        lv_slider_set_range(slider, 0, selectionsSize - 1);

        preview = lv_label_create(container);
        lv_obj_set_grid_cell(preview,
                             LV_GRID_ALIGN_STRETCH, 2, 1,
                             LV_GRID_ALIGN_CENTER, row, 1);
        lv_obj_set_size(preview, lv_pct(100), LV_SIZE_CONTENT);
        lv_obj_set_style_text_align(preview , LV_TEXT_ALIGN_LEFT, 0);

        tt_hal_radio_get_parameter_unit_str(handle, param, unit, sizeof(unit));
        updatePreview();

        lv_obj_add_event_cb(slider, [](lv_event_t * e) {
            lv_obj_t* slider = lv_event_get_target_obj(e);
            SliderSelectParameterInput* self = (SliderSelectParameterInput*)lv_event_get_user_data(e);
            self->updatePreview();
        }, LV_EVENT_VALUE_CHANGED, this);

    }

    virtual void updatePreview() override {
        Str text;
        text.appendf(fmt, selections[lv_slider_get_value(slider)]);
        text.append(unit);
        lv_label_set_text(preview, text.c_str());
    }
};

static ParameterInput* makeLoraInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row) {
    static constexpr float bw_values[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0, 500.0, SliderSelectParameterInput::SELECT_END};
    switch (param) {
        case RADIO_POWER: //no break
        case RADIO_FREQUENCY:
            return new NumericParameterInput(handle, param, container, row);
        case RADIO_BANDWIDTH:
            return new SliderSelectParameterInput(handle, param, container, row, bw_values, "%.1f");
        case RADIO_SPREADFACTOR:
            return new SliderParameterInput(handle, param, container, row, 7, 12);
        case RADIO_CODINGRATE:
            return new SliderParameterInput(handle, param, container, row, 5, 8);
        case RADIO_SYNCWORD:
            return new SliderParameterInput(handle, param, container, row, 0, 255, "%02X");
        case RADIO_PREAMBLES:
            return new SliderParameterInput(handle, param, container, row, 0, 0xFFFF);
        default:
            return new NumericParameterInput(handle, param, container, row);
    }
}

static ParameterInput* makeParameterInput(RadioHandle handle, const RadioParameter param, const Modulation modulation, lv_obj_t* container, int row) {
    switch (modulation) {
        case MODULATION_LORA:
            return makeLoraInput(handle, param, container, row);
        default:
            return new NumericParameterInput(handle, param, container, row);
    }
}

class SettingsView {
    static constexpr size_t MAX_RADIOS = 32;
    static constexpr Modulation FIRST_MODULATION = MODULATION_NONE;
    static constexpr Modulation LAST_MODULATION = MODULATION_LRFHSS;
    static constexpr size_t MAX_MODEMS = LAST_MODULATION + 1;
    static constexpr size_t MAX_PARAMS = RADIO_NARROWGRID + 1;

    RadioHandle radios[MAX_RADIOS] = {0};
    size_t radioCount = 0;

    RadioHandle radioSelected = nullptr;
    Modulation modemsAvailable[MAX_MODEMS] = {};
    size_t modemsAvailableCount = 0;
    RadioParameter paramsAvailable[MAX_PARAMS] = {};
    ParameterInput* paramInputs[MAX_PARAMS] = {0};
    size_t paramsAvailableCount = 0;


    lv_obj_t *mainPanel = nullptr;
    lv_obj_t *deviceForm = nullptr;
    lv_obj_t *radioDropdown = nullptr;
    lv_obj_t *radioSwitch = nullptr;
    lv_obj_t *modemDropdown = nullptr;

    lv_obj_t *propertiesForm = nullptr;

public:
    void queryRadios() {
        DeviceId devices[MAX_RADIOS];
        uint16_t device_count = 0;
        if(!tt_hal_device_find(DEVICE_TYPE_RADIO, devices, &device_count, MAX_RADIOS)) {
            // TT_LOG_W(TAG, "No radios registered with the system?");
        } else {
            size_t radios_allocated = 0;
            for (size_t i = 0; (i < device_count) && (i < MAX_RADIOS); ++i) {
                auto radio = tt_hal_radio_alloc(devices[i]);
                if (radio) {
                    // TT_LOG_I(TAG, "Discovered radio \"%s\"", tt_hal_radio_get_name(radio));
                    radios[radios_allocated] = radio;
                    radios_allocated++;
                } else {
                    // TT_LOG_E(TAG, "Error allocating radio handle for id=%d", devId);
                }
            }
            radioCount = radios_allocated;
        }
    }

    void getRadioNames(Str &names, const char* const separator) {
        int count = 1;
        names.clear();
        //for (auto radio : radios) {
        for (size_t i = 0; i < radioCount; ++i) {
            Str name(tt_hal_radio_get_name(radios[i]));
            auto last = (i == (radioCount - 1));
            if (name == "") {
                name.appendf("Unknown Radio %d", count);
            }
            names.append(name.c_str());
            count++;
            if (!last) {
                names.append(separator);
            }
        }
    }

    int getModemAvailableIndex(Modulation m) {
        for (size_t i = 0; i < modemsAvailableCount; ++i) {
            if (modemsAvailable[i] == m) {
                return i;
            }
        }
        return -1;
    }

    lv_obj_t* initParameterFormGeneric(lv_obj_t *parent, const Modulation modem) {
        lv_obj_t *container = propertiesForm;
        if (container) {
            lv_obj_clean(container);
            lv_obj_del(container);
        }

        paramsAvailableCount = 0;
        container = lv_obj_create(parent);
        lv_obj_set_style_pad_all(container, 0, 0);
        lv_obj_set_layout(container, LV_LAYOUT_GRID);
        lv_obj_align(container, LV_ALIGN_TOP_MID, 0, 0);

        const int grid_row_size = 40;
        const int grid_col_size = 140;
        static constexpr size_t row_dsc_last = RADIO_NARROWGRID + 1;
        static lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), grid_col_size, LV_GRID_TEMPLATE_LAST};
        static lv_coord_t row_dsc[row_dsc_last] = {0};
        for (size_t i = 0; i < row_dsc_last; ++i) {
            row_dsc[i] =  grid_row_size; //LV_GRID_FR(1);
        }
        row_dsc[row_dsc_last - 1] = LV_GRID_TEMPLATE_LAST;
        lv_obj_set_grid_dsc_array(container, col_dsc, row_dsc);

        char unit_buffer[32] = {0};
        for (RadioParameter param = RADIO_POWER;
             param <= RADIO_NARROWGRID;
             param = static_cast<RadioParameter>((size_t)param + 1)) {
            float value = 0.0;
            Str value_buffer;
            auto status = tt_hal_radio_get_parameter(radioSelected, param, &value);
            if (status == RADIO_PARAM_SUCCESS) {
                auto input = makeParameterInput(radioSelected, param, modem, container, paramsAvailableCount);
                paramInputs[paramsAvailableCount] = input;
                //lv_group_focus_obj(input);
                paramsAvailable[paramsAvailableCount] = param;
                paramsAvailableCount++;
            }
        }
        row_dsc[paramsAvailableCount] = LV_GRID_TEMPLATE_LAST;
        lv_obj_set_grid_dsc_array(container, col_dsc, row_dsc);
        lv_obj_set_size(container, lv_pct(100), lv_pct(100));

        return container;
    }

    void selectModulation(int modemIndex) {
        lv_dropdown_set_selected(modemDropdown, modemIndex);
        if (tt_hal_radio_set_modulation(radioSelected, modemsAvailable[modemIndex])) {
            propertiesForm = initParameterFormGeneric(mainPanel, modemsAvailable[modemIndex]);
            //clownvomit(propertiesForm);
        }
    }

    void selectRadio(int index) {
        radioSelected = radios[index];
        assert(radioSelected);

        for (size_t i = 0; i < MAX_MODEMS; ++i) {
            modemsAvailable[i] = MODULATION_NONE;
        }

        Str modulation_list;
        modemsAvailableCount = 1;
        modemsAvailable[0] = MODULATION_NONE;
        modulation_list.append(LV_SYMBOL_MINUS);
        modulation_list.append(" ");
        modulation_list.append(LV_SYMBOL_MINUS);
        modulation_list.append(" ");
        modulation_list.append("Disabled\n");


        for (Modulation mod = FIRST_MODULATION;
             mod <= LAST_MODULATION;
             mod = static_cast<Modulation>((size_t)mod + 1)) {
            bool canRx = tt_hal_radio_can_receive(radioSelected, mod);
            bool canTx = tt_hal_radio_can_transmit(radioSelected, mod);
            bool place_sep = (canRx || canTx) && (mod != LAST_MODULATION);

            if (!canRx && !canTx) {
                continue;
            }
            modemsAvailable[modemsAvailableCount] = mod;
            modemsAvailableCount++;

            if (canRx) {
                modulation_list.append(LV_SYMBOL_DOWNLOAD);
                modulation_list.append(" ");
            } else {
                modulation_list.append(LV_SYMBOL_MINUS);
                modulation_list.append(" ");
            }

            if (canTx) {
                modulation_list.append(LV_SYMBOL_UPLOAD);
                modulation_list.append(" ");
            } else {
                modulation_list.append(LV_SYMBOL_MINUS);
                modulation_list.append(" ");
            }

            modulation_list.append(toString(mod));
            if (place_sep) {
                modulation_list.append("\n");
            }
        }

        lv_dropdown_set_options(modemDropdown, modulation_list.c_str());
        auto modemIndexConfigured = getModemAvailableIndex(tt_hal_radio_get_modulation(radioSelected));
        if (modemIndexConfigured > -1) {
            lv_dropdown_set_selected(modemDropdown, modemIndexConfigured);
            selectModulation(modemIndexConfigured);
        }
    }

    lv_obj_t *initDeviceForm(lv_obj_t *parent) {
        lv_obj_t *container = lv_obj_create(parent);
        lv_obj_set_size(container, lv_pct(100), LV_SIZE_CONTENT);
        lv_obj_set_style_pad_all(container, 0, 0);

        const int grid_row_size = 40;
        const int grid_col_size = 45;
        static lv_coord_t lora_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), grid_col_size, LV_GRID_TEMPLATE_LAST};
        static lv_coord_t lora_row_dsc[] = {
            grid_row_size,
            grid_row_size,
            LV_GRID_TEMPLATE_LAST};
        lv_obj_set_layout(container, LV_LAYOUT_GRID);
        lv_obj_set_grid_dsc_array(container, lora_col_dsc, lora_row_dsc);

        Str radio_names;
        getRadioNames(radio_names, "\n");
        radioDropdown = initGridDropdownInput(container, 0, "Device", radio_names.c_str());
        modemDropdown = initGridDropdownInput(container, 1, "Modulation", "none available");
        radioSwitch = lv_switch_create(container);
        lv_obj_set_grid_cell(radioSwitch,
                             LV_GRID_ALIGN_STRETCH, 2, 1,
                             LV_GRID_ALIGN_CENTER, 0, 1);
        lv_obj_set_size(radioSwitch, lv_pct(100), 20);


        lv_obj_add_event_cb(modemDropdown, [](lv_event_t * e) {
            SettingsView* self = (SettingsView*)lv_event_get_user_data(e);
            lv_obj_t* input = lv_event_get_target_obj(e);
            self->selectModulation(lv_dropdown_get_selected(input));
        }, LV_EVENT_VALUE_CHANGED, this);

        selectRadio(0);
        return container;
    }

    void initUi(lv_obj_t *parent) {
        mainPanel = lv_obj_create(parent);
        lv_obj_set_size(mainPanel, lv_pct(100), lv_pct(80));
        lv_obj_set_flex_flow(mainPanel, LV_FLEX_FLOW_COLUMN);
        lv_obj_align(mainPanel, LV_ALIGN_TOP_MID, 0, 0);
        lv_obj_set_style_border_width(mainPanel, 0, 0);
        lv_obj_set_style_pad_all(mainPanel, 0, 0);
        // Only needed if container needs to be scrollable through encoder long long press
        lv_obj_add_flag(mainPanel, (lv_obj_flag_t)(LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLL_ON_FOCUS));
        auto* group = lv_group_get_default();
        lv_group_add_obj(group, mainPanel);
        //lv_obj_add_event_cb(btn, scrollbar_highlight, LV_EVENT_FOCUS, NULL);
        //lv_obj_add_event_cb(btn, scrollbar_restore, LV_EVENT_DEFOCUSED, NULL);

        // Create once (e.g. during init)
        static lv_style_t style_scroll_focus;
        lv_style_init(&style_scroll_focus);
        lv_style_set_bg_color(&style_scroll_focus, lv_color_make(0x40,0xA0,0xFF));
        lv_style_set_bg_opa(&style_scroll_focus, LV_OPA_COVER);
        lv_style_set_border_width(&style_scroll_focus, 1);
        lv_style_set_border_color(&style_scroll_focus, lv_color_black());

        // Apply style targeted to the scrollbar part when the object is FOCUSED
        // LV_PART_SCROLLBAR | LV_STATE_FOCUSED will ensure the style is used only while focused.
        lv_obj_add_style(mainPanel, &style_scroll_focus, LV_PART_SCROLLBAR | LV_STATE_FOCUSED);

        deviceForm = initDeviceForm(mainPanel);
    }

    explicit SettingsView(lv_obj_t *parent) {
        queryRadios();
        initUi(parent);
    }
};

void RadioSet::onShow(AppHandle appHandle, lv_obj_t* parent) {
    lv_obj_remove_flag(parent, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);

    lv_obj_t* toolbar = tt_lvgl_toolbar_create_for_app(parent, appHandle);
    lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);

    uiDropDownMenu = lv_dropdown_create(toolbar);

    lv_dropdown_set_options(uiDropDownMenu, LV_SYMBOL_ENVELOPE " Terminal\n" LV_SYMBOL_SETTINGS " Settings");
    lv_dropdown_set_text(uiDropDownMenu, "Menu");
    lv_dropdown_set_symbol(uiDropDownMenu, LV_SYMBOL_DOWN);
    lv_dropdown_set_selected_highlight(uiDropDownMenu, true);
    lv_obj_set_style_border_color(uiDropDownMenu, lv_color_hex(0xFAFAFA), LV_PART_MAIN);
    lv_obj_set_style_border_width(uiDropDownMenu, 1, LV_PART_MAIN);
    lv_obj_align(uiDropDownMenu, LV_ALIGN_RIGHT_MID, 0, 0);
    lv_obj_set_width(uiDropDownMenu, 120);

    lv_obj_t* wrapper = lv_obj_create(parent);
    lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(wrapper, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
    lv_obj_set_flex_grow(wrapper, 1);
    lv_obj_set_width(wrapper, LV_PCT(100));
    lv_obj_set_height(wrapper, LV_PCT(100));
    lv_obj_set_style_pad_all(wrapper, 0, LV_PART_MAIN);
    lv_obj_set_style_pad_row(wrapper, 0, LV_PART_MAIN);
    lv_obj_set_style_border_width(wrapper, 0, 0);
    lv_obj_remove_flag(wrapper, LV_OBJ_FLAG_SCROLLABLE);

    settingsView = new SettingsView(wrapper);
}


RadioSet::~RadioSet() {
    if (termView) {
        delete termView;
    }
    if (settingsView) {
        delete settingsView;
    }
}

// ????
extern "C" void __cxa_pure_virtual() { crash("Entered the Virtual Zone..."); }
