#ifdef ESP_PLATFORM

#include "tt_app.h"
#include "tt_app_alertdialog.h"
#include "tt_app_fileselection.h"
#include "tt_app_selectiondialog.h"
#include "tt_bundle.h"
#include "tt_preferences.h"
#include "tt_time.h"

#include "symbols/cplusplus.h"
#include "symbols/esp_event.h"
#include "symbols/esp_http_client.h"
#include "symbols/freertos.h"
#include "symbols/gcc_soft_float.h"
#include "symbols/mbedtls.h"
#include "symbols/pthread.h"
#include "symbols/stl.h"
#include "symbols/string.h"

#include <cassert>
#include <cmath>
#include <cstring>
#include <ctime>
#include <ctype.h>
#include <driver/ledc.h>
#include <getopt.h>
#include <dirent.h>
#include <esp_log.h>
#include <esp_random.h>
#include <esp_sntp.h>
#include <esp_netif.h>
#include <esp_heap_caps.h>
#include <esp_timer.h>
#include <esp_system.h>
#include <fcntl.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>
#include <lwip/inet.h>
#include <miniz.h>
#include <sys/select.h>
#include <locale.h>
#include <setjmp.h>
#include <strings.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <vector>

#include <Tactility/Tactility.h>

#include <driver/i2s_common.h>
#include <driver/i2s_std.h>
#include <driver/gpio.h>

#ifdef CONFIG_IDF_TARGET_ESP32P4
#include <driver/ppa.h>
#include <esp_cache.h>
#endif

extern "C" {

extern double __floatsidf(int x);
extern void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression);

const esp_elfsym main_symbols[] {
    // stdlib.h
    ESP_ELFSYM_EXPORT(malloc),
    ESP_ELFSYM_EXPORT(calloc),
    ESP_ELFSYM_EXPORT(realloc),
    ESP_ELFSYM_EXPORT(free),
    ESP_ELFSYM_EXPORT(rand),
    ESP_ELFSYM_EXPORT(srand),
    ESP_ELFSYM_EXPORT(rand_r),
    ESP_ELFSYM_EXPORT(atoi),
    ESP_ELFSYM_EXPORT(atol),
    ESP_ELFSYM_EXPORT(system),
    // esp random
    ESP_ELFSYM_EXPORT(esp_random),
    ESP_ELFSYM_EXPORT(esp_fill_random),
    // esp other
    ESP_ELFSYM_EXPORT(__floatsidf),
    ESP_ELFSYM_EXPORT(_esp_error_check_failed),
    // unistd.h
    ESP_ELFSYM_EXPORT(usleep),
    ESP_ELFSYM_EXPORT(sleep),
    ESP_ELFSYM_EXPORT(exit),
    ESP_ELFSYM_EXPORT(close),
    ESP_ELFSYM_EXPORT(rmdir),
    ESP_ELFSYM_EXPORT(unlink),
    // strings.h
    ESP_ELFSYM_EXPORT(explicit_bzero),
    ESP_ELFSYM_EXPORT(strcasecmp),
    ESP_ELFSYM_EXPORT(strncasecmp),
    // time.h
    ESP_ELFSYM_EXPORT(clock_gettime),
    ESP_ELFSYM_EXPORT(strftime),
    ESP_ELFSYM_EXPORT(time),
    ESP_ELFSYM_EXPORT(difftime),
    ESP_ELFSYM_EXPORT(localtime_r),
    ESP_ELFSYM_EXPORT(localtime),
    ESP_ELFSYM_EXPORT(mktime),
    // esp_sntp.h
    ESP_ELFSYM_EXPORT(sntp_get_sync_status),
    // math.h
    ESP_ELFSYM_EXPORT(acos),
    ESP_ELFSYM_EXPORT(acoshf),
    ESP_ELFSYM_EXPORT(acosf),
    ESP_ELFSYM_EXPORT(asin),
    ESP_ELFSYM_EXPORT(asinhf),
    ESP_ELFSYM_EXPORT(asinf),
    ESP_ELFSYM_EXPORT(atan),
    ESP_ELFSYM_EXPORT(atanhf),
    ESP_ELFSYM_EXPORT(atanf),
    ESP_ELFSYM_EXPORT(cos),
    ESP_ELFSYM_EXPORT(coshf),
    ESP_ELFSYM_EXPORT(cosf),
    ESP_ELFSYM_EXPORT(sin),
    ESP_ELFSYM_EXPORT(sinhf),
    ESP_ELFSYM_EXPORT(sinf),
    ESP_ELFSYM_EXPORT(tan),
    ESP_ELFSYM_EXPORT(tanhf),
    ESP_ELFSYM_EXPORT(tanf),
    ESP_ELFSYM_EXPORT(frexp),
    ESP_ELFSYM_EXPORT(frexpf),
    ESP_ELFSYM_EXPORT(modf),
    ESP_ELFSYM_EXPORT(modff),
    ESP_ELFSYM_EXPORT(ceil),
    ESP_ELFSYM_EXPORT(ceilf),
    ESP_ELFSYM_EXPORT(fabs),
    ESP_ELFSYM_EXPORT(fabsf),
    ESP_ELFSYM_EXPORT(floor),
    ESP_ELFSYM_EXPORT(floorf),
    ESP_ELFSYM_EXPORT(fmax),
    ESP_ELFSYM_EXPORT(fmaxf),
    ESP_ELFSYM_EXPORT(fmin),
    ESP_ELFSYM_EXPORT(fminf),
    ESP_ELFSYM_EXPORT(round),
    ESP_ELFSYM_EXPORT(roundf),
#ifndef _REENT_ONLY
    ESP_ELFSYM_EXPORT(acos),
    ESP_ELFSYM_EXPORT(acosf),
    ESP_ELFSYM_EXPORT(asin),
    ESP_ELFSYM_EXPORT(asinf),
    ESP_ELFSYM_EXPORT(atan2),
    ESP_ELFSYM_EXPORT(atan2f),
    ESP_ELFSYM_EXPORT(sinh),
    ESP_ELFSYM_EXPORT(sinhf),
    ESP_ELFSYM_EXPORT(exp),
    ESP_ELFSYM_EXPORT(expf),
    ESP_ELFSYM_EXPORT(ldexp),
    ESP_ELFSYM_EXPORT(ldexpf),
    ESP_ELFSYM_EXPORT(log),
    ESP_ELFSYM_EXPORT(logf),
    ESP_ELFSYM_EXPORT(log10),
    ESP_ELFSYM_EXPORT(log10f),
    ESP_ELFSYM_EXPORT(pow),
    ESP_ELFSYM_EXPORT(powf),
    ESP_ELFSYM_EXPORT(sqrt),
    ESP_ELFSYM_EXPORT(sqrtf),
    ESP_ELFSYM_EXPORT(fmod),
    ESP_ELFSYM_EXPORT(fmodf),
#endif
    // sys/errno.h
    ESP_ELFSYM_EXPORT(__errno),
    // freertos_tasks_c_additions.h
    ESP_ELFSYM_EXPORT(__getreent),
#ifdef __HAVE_LOCALE_INFO__
    // ctype.h
    ESP_ELFSYM_EXPORT(__locale_ctype_ptr),
#else
    ESP_ELFSYM_EXPORT(_ctype_),
#endif
    // getopt.h
    ESP_ELFSYM_EXPORT(getopt_long),
    ESP_ELFSYM_EXPORT(optind),
    ESP_ELFSYM_EXPORT(opterr),
    ESP_ELFSYM_EXPORT(optarg),
    ESP_ELFSYM_EXPORT(optopt),
    // setjmp.h
    ESP_ELFSYM_EXPORT(longjmp),
    ESP_ELFSYM_EXPORT(setjmp),
    // cassert
    ESP_ELFSYM_EXPORT(__assert_func),
    // cstdio
    ESP_ELFSYM_EXPORT(abort),
    ESP_ELFSYM_EXPORT(fclose),
    ESP_ELFSYM_EXPORT(feof),
    ESP_ELFSYM_EXPORT(ferror),
    ESP_ELFSYM_EXPORT(fflush),
    ESP_ELFSYM_EXPORT(fgetc),
    ESP_ELFSYM_EXPORT(fgetpos),
    ESP_ELFSYM_EXPORT(fgets),
    ESP_ELFSYM_EXPORT(fopen),
    ESP_ELFSYM_EXPORT(freopen),
    ESP_ELFSYM_EXPORT(fputc),
    ESP_ELFSYM_EXPORT(fputs),
    ESP_ELFSYM_EXPORT(fprintf),
    ESP_ELFSYM_EXPORT(fread),
    ESP_ELFSYM_EXPORT(fseek),
    ESP_ELFSYM_EXPORT(fsetpos),
    ESP_ELFSYM_EXPORT(fscanf),
    ESP_ELFSYM_EXPORT(ftell),
    ESP_ELFSYM_EXPORT(fwrite),
    ESP_ELFSYM_EXPORT(getc),
    ESP_ELFSYM_EXPORT(putc),
    ESP_ELFSYM_EXPORT(putchar),
    ESP_ELFSYM_EXPORT(puts),
    ESP_ELFSYM_EXPORT(printf),
    ESP_ELFSYM_EXPORT(sscanf),
    ESP_ELFSYM_EXPORT(snprintf),
    ESP_ELFSYM_EXPORT(sprintf),
    ESP_ELFSYM_EXPORT(vsprintf),
    ESP_ELFSYM_EXPORT(vsnprintf),
    ESP_ELFSYM_EXPORT(vfprintf),
    // cstring
    ESP_ELFSYM_EXPORT(strlen),
    ESP_ELFSYM_EXPORT(strcmp),
    ESP_ELFSYM_EXPORT(strncmp),
    ESP_ELFSYM_EXPORT(strncpy),
    ESP_ELFSYM_EXPORT(strcpy),
    ESP_ELFSYM_EXPORT(strcat),
    ESP_ELFSYM_EXPORT(strchr),
    ESP_ELFSYM_EXPORT(strstr),
    ESP_ELFSYM_EXPORT(strerror),
    ESP_ELFSYM_EXPORT(strtod),
    ESP_ELFSYM_EXPORT(strrchr),
    ESP_ELFSYM_EXPORT(strtol),
    ESP_ELFSYM_EXPORT(strtoul),
    ESP_ELFSYM_EXPORT(strcspn),
    ESP_ELFSYM_EXPORT(strncat),
    ESP_ELFSYM_EXPORT(strpbrk),
    ESP_ELFSYM_EXPORT(strspn),
    ESP_ELFSYM_EXPORT(strcoll),
    ESP_ELFSYM_EXPORT(memset),
    ESP_ELFSYM_EXPORT(memcpy),
    ESP_ELFSYM_EXPORT(memcmp),
    ESP_ELFSYM_EXPORT(memchr),
    ESP_ELFSYM_EXPORT(memmove),
    ESP_ELFSYM_EXPORT(strdup),

    // ctype
    ESP_ELFSYM_EXPORT(isalnum),
    ESP_ELFSYM_EXPORT(isalpha),
    ESP_ELFSYM_EXPORT(iscntrl),
    ESP_ELFSYM_EXPORT(isdigit),
    ESP_ELFSYM_EXPORT(isgraph),
    ESP_ELFSYM_EXPORT(islower),
    ESP_ELFSYM_EXPORT(isprint),
    ESP_ELFSYM_EXPORT(ispunct),
    ESP_ELFSYM_EXPORT(isspace),
    ESP_ELFSYM_EXPORT(isupper),
    ESP_ELFSYM_EXPORT(isxdigit),
    ESP_ELFSYM_EXPORT(tolower),
    ESP_ELFSYM_EXPORT(toupper),
    // ESP-IDF
    ESP_ELFSYM_EXPORT(esp_log),
    ESP_ELFSYM_EXPORT(esp_log_write),
    ESP_ELFSYM_EXPORT(esp_log_timestamp),
    ESP_ELFSYM_EXPORT(esp_err_to_name),
    // Tactility
    ESP_ELFSYM_EXPORT(tt_app_start),
    ESP_ELFSYM_EXPORT(tt_app_start_with_bundle),
    ESP_ELFSYM_EXPORT(tt_app_stop),
    ESP_ELFSYM_EXPORT(tt_app_register),
    ESP_ELFSYM_EXPORT(tt_app_get_parameters),
    ESP_ELFSYM_EXPORT(tt_app_set_result),
    ESP_ELFSYM_EXPORT(tt_app_has_result),
    ESP_ELFSYM_EXPORT(tt_app_fileselection_start_for_existing_file),
    ESP_ELFSYM_EXPORT(tt_app_fileselection_start_for_existing_or_new_file),
    ESP_ELFSYM_EXPORT(tt_app_fileselection_get_result_path),
    ESP_ELFSYM_EXPORT(tt_app_selectiondialog_start),
    ESP_ELFSYM_EXPORT(tt_app_selectiondialog_get_result_index),
    ESP_ELFSYM_EXPORT(tt_app_alertdialog_start),
    ESP_ELFSYM_EXPORT(tt_app_alertdialog_get_result_index),
    ESP_ELFSYM_EXPORT(tt_app_get_user_data_path),
    ESP_ELFSYM_EXPORT(tt_app_get_user_data_child_path),
    ESP_ELFSYM_EXPORT(tt_app_get_assets_path),
    ESP_ELFSYM_EXPORT(tt_app_get_assets_child_path),
    ESP_ELFSYM_EXPORT(tt_bundle_alloc),
    ESP_ELFSYM_EXPORT(tt_bundle_free),
    ESP_ELFSYM_EXPORT(tt_bundle_opt_bool),
    ESP_ELFSYM_EXPORT(tt_bundle_opt_int32),
    ESP_ELFSYM_EXPORT(tt_bundle_opt_string),
    ESP_ELFSYM_EXPORT(tt_bundle_put_bool),
    ESP_ELFSYM_EXPORT(tt_bundle_put_int32),
    ESP_ELFSYM_EXPORT(tt_bundle_put_string),
    ESP_ELFSYM_EXPORT(tt_preferences_alloc),
    ESP_ELFSYM_EXPORT(tt_preferences_free),
    ESP_ELFSYM_EXPORT(tt_preferences_opt_bool),
    ESP_ELFSYM_EXPORT(tt_preferences_opt_int32),
    ESP_ELFSYM_EXPORT(tt_preferences_opt_string),
    ESP_ELFSYM_EXPORT(tt_preferences_put_bool),
    ESP_ELFSYM_EXPORT(tt_preferences_put_int32),
    ESP_ELFSYM_EXPORT(tt_preferences_put_string),
    ESP_ELFSYM_EXPORT(tt_timezone_set),
    ESP_ELFSYM_EXPORT(tt_timezone_get_name),
    ESP_ELFSYM_EXPORT(tt_timezone_get_code),
    ESP_ELFSYM_EXPORT(tt_timezone_is_format_24_hour),
    ESP_ELFSYM_EXPORT(tt_timezone_set_format_24_hour),

    // stdio.h
    ESP_ELFSYM_EXPORT(rename),
    ESP_ELFSYM_EXPORT(rewind),
    ESP_ELFSYM_EXPORT(remove),
    // dirent.h
    ESP_ELFSYM_EXPORT(opendir),
    ESP_ELFSYM_EXPORT(closedir),
    ESP_ELFSYM_EXPORT(readdir),
    // fcntl.h
    ESP_ELFSYM_EXPORT(fcntl),
    // lwip/sockets.h
    ESP_ELFSYM_EXPORT(lwip_setsockopt),
    ESP_ELFSYM_EXPORT(lwip_socket),
    ESP_ELFSYM_EXPORT(lwip_recv),
    ESP_ELFSYM_EXPORT(lwip_getpeername),
    ESP_ELFSYM_EXPORT(lwip_bind),
    ESP_ELFSYM_EXPORT(lwip_listen),
    ESP_ELFSYM_EXPORT(lwip_close),
    ESP_ELFSYM_EXPORT(lwip_accept),
    ESP_ELFSYM_EXPORT(lwip_getsockname),
    ESP_ELFSYM_EXPORT(lwip_send),
    ESP_ELFSYM_EXPORT(lwip_connect),
    ESP_ELFSYM_EXPORT(lwip_select),
    ESP_ELFSYM_EXPORT(lwip_gethostbyname),
    ESP_ELFSYM_EXPORT(ipaddr_addr),
    // POSIX socket names (VFS wrappers used when apps include <sys/socket.h>)
    ESP_ELFSYM_EXPORT(select),
    // sys/stat.h
    ESP_ELFSYM_EXPORT(stat),
    ESP_ELFSYM_EXPORT(mkdir),
    // esp_netif.h
    ESP_ELFSYM_EXPORT(esp_netif_get_ip_info),
    ESP_ELFSYM_EXPORT(esp_netif_get_handle_from_ifkey),
    // Locale
    ESP_ELFSYM_EXPORT(localeconv),
    // driver/gpio.h
    ESP_ELFSYM_EXPORT(gpio_config),
    ESP_ELFSYM_EXPORT(gpio_get_level),
    ESP_ELFSYM_EXPORT(gpio_set_level),
    ESP_ELFSYM_EXPORT(gpio_reset_pin),
    // driver/i2s_common.h
    ESP_ELFSYM_EXPORT(i2s_new_channel),
    ESP_ELFSYM_EXPORT(i2s_del_channel),
    ESP_ELFSYM_EXPORT(i2s_channel_enable),
    ESP_ELFSYM_EXPORT(i2s_channel_disable),
    ESP_ELFSYM_EXPORT(i2s_channel_write),
    ESP_ELFSYM_EXPORT(i2s_channel_get_info),
    ESP_ELFSYM_EXPORT(i2s_channel_read),
    ESP_ELFSYM_EXPORT(i2s_channel_register_event_callback),
    ESP_ELFSYM_EXPORT(i2s_channel_preload_data),
    ESP_ELFSYM_EXPORT(i2s_channel_tune_rate),
    // driver/i2s_std.h
    ESP_ELFSYM_EXPORT(i2s_channel_init_std_mode),
    ESP_ELFSYM_EXPORT(i2s_channel_reconfig_std_clock),
    ESP_ELFSYM_EXPORT(i2s_channel_reconfig_std_slot),
    ESP_ELFSYM_EXPORT(i2s_channel_reconfig_std_gpio),
    // miniz.h
    ESP_ELFSYM_EXPORT(tinfl_decompress),
    ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_callback),
    ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_mem),
    // ledc
    ESP_ELFSYM_EXPORT(ledc_update_duty),
    ESP_ELFSYM_EXPORT(ledc_set_freq),
    ESP_ELFSYM_EXPORT(ledc_channel_config),
    ESP_ELFSYM_EXPORT(ledc_set_duty),
    ESP_ELFSYM_EXPORT(ledc_set_fade),
    ESP_ELFSYM_EXPORT(ledc_set_fade_with_step),
    ESP_ELFSYM_EXPORT(ledc_set_fade_with_time),
    ESP_ELFSYM_EXPORT(ledc_set_fade_step_and_start),
    ESP_ELFSYM_EXPORT(ledc_set_fade_time_and_start),
    ESP_ELFSYM_EXPORT(ledc_set_pin),
    ESP_ELFSYM_EXPORT(ledc_timer_config),
    ESP_ELFSYM_EXPORT(ledc_timer_pause),
    ESP_ELFSYM_EXPORT(ledc_timer_resume),
    ESP_ELFSYM_EXPORT(ledc_timer_rst),
    // esp_heap_caps.h
    ESP_ELFSYM_EXPORT(heap_caps_get_total_size),
    ESP_ELFSYM_EXPORT(heap_caps_get_allocated_size),
    ESP_ELFSYM_EXPORT(heap_caps_get_free_size),
    ESP_ELFSYM_EXPORT(heap_caps_get_largest_free_block),
    ESP_ELFSYM_EXPORT(heap_caps_aligned_alloc),
    ESP_ELFSYM_EXPORT(heap_caps_malloc),
    ESP_ELFSYM_EXPORT(heap_caps_calloc),
    ESP_ELFSYM_EXPORT(heap_caps_free),
    // esp_timer.h
    ESP_ELFSYM_EXPORT(esp_timer_create),
    ESP_ELFSYM_EXPORT(esp_timer_stop),
    ESP_ELFSYM_EXPORT(esp_timer_delete),
    ESP_ELFSYM_EXPORT(esp_timer_start_periodic),
    ESP_ELFSYM_EXPORT(esp_timer_start_once),
    ESP_ELFSYM_EXPORT(esp_timer_get_time),
#ifdef CONFIG_IDF_TARGET_ESP32P4
    // driver/ppa.h
    ESP_ELFSYM_EXPORT(ppa_register_client),
    ESP_ELFSYM_EXPORT(ppa_unregister_client),
    ESP_ELFSYM_EXPORT(ppa_do_scale_rotate_mirror),
    // esp_cache.h
    ESP_ELFSYM_EXPORT(esp_cache_msync),
    // esp_system.h
    ESP_ELFSYM_EXPORT(esp_restart),
#endif
    // delimiter
    ESP_ELFSYM_END
};

uintptr_t resolve_symbol(const esp_elfsym* source, const char* symbolName) {
    const esp_elfsym* symbol_iterator = source;
    while (symbol_iterator->name != nullptr) {
        if (strcmp(symbol_iterator->name, symbolName) == 0) {
            return reinterpret_cast<uintptr_t>(symbol_iterator->sym);
        }
        symbol_iterator++;
    }
    return 0;
}

uintptr_t tt_symbol_resolver(const char* symbolName) {
    static const std::vector all_symbols = {
        main_symbols,
        gcc_soft_float_symbols,
        stl_symbols,
        cplusplus_symbols,
        pthread_symbols,
        freertos_symbols,
        string_symbols,
        esp_event_symbols,
        esp_http_client_symbols,
        mbedtls_symbols,
    };

    for (const auto* symbols : all_symbols) {
        const uintptr_t address = resolve_symbol(symbols, symbolName);
        if (address != 0) {
            return address;
        }
    }

    uintptr_t symbol_address;
    if (module_resolve_symbol_global(symbolName, &symbol_address)) {
        return symbol_address;
    }

    return 0;
}

void tt_init_tactility_c() {
    elf_set_symbol_resolver(tt_symbol_resolver);
}

}

// extern "C"

#else // Simulator

extern "C" {

void tt_init_tactility_c() {
}

}

#endif // ESP_PLATFORM
