// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef _ELILONG_H
#define _ELILONG_H

#include <efi.h>
#include <efilib.h>

// static string length constraints
#define MAX_LABEL        64
#define MAX_DESC         128
#define MAX_PATH         256
#define MAX_ROOTDEV      128
#define MAX_APPEND       512
#define MIN_SCR_WIDTH    12
#define MAX_ENTRIES      18   // for now we don't allow more than 18 boot entries as they won't fit
                              // on the screen. TODO: allow for more with a scrollable menu list

// Double-line Box Unicode Characters (v0.1.8)
#define BOX_V      0x2551  // ║ Vertical line
#define BOX_H      0x2550  // ═ Horizontal line
#define BOX_TL     0x2554  // ╔ Top-Left corner
#define BOX_TR     0x2557  // ╗ Top-Right corner
#define BOX_BL     0x255A  // ╚ Bottom-Left corner
#define BOX_BR     0x255D  // ╝ Bottom-Right corner

// read/write or unset for root partition
typedef enum {
    MOUNT_UNSET = 0,
    MOUNT_RO    = 1,
    MOUNT_RW    = 2
} MOUNT_STATE;

// individual entry
typedef struct _BOOT_ENTRY {
  CHAR16 label[MAX_LABEL];
  CHAR16 description[MAX_DESC];
  CHAR16 kernel_path[MAX_PATH];
  CHAR16 initrd_path[MAX_PATH];
  CHAR16 root_device[MAX_ROOTDEV];
  MOUNT_STATE mount_mode;
  CHAR16 append[MAX_APPEND];
  struct _BOOT_ENTRY *next;
} BOOT_ENTRY;

// Represents the entire configuration
typedef struct _BOOT_CONFIG {
    BOOT_ENTRY *entries;
    INTN timeout; // -1 represents "wait" (infinite) -2 represents don't show menu and don't wait
    CHAR16 default_label[MAX_LABEL];
    CHAR16 message_path[MAX_PATH];
    UINTN scrwidth;
    UINTN scrheight;
} BOOT_CONFIG;

// we detect the bitness of our binary, handling 32 and 64bit for various platforms
// resorting to pointer length, before giving up.
static inline const CHAR16* get_bitness_str(void) {
#if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || (defined(__riscv_xlen) && (__riscv_xlen == 64))
    return L"64";
#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86)
    return L"32";
#elif defined(__arm__) || defined(_M_ARM) || (defined(__riscv_xlen) && (__riscv_xlen == 32))
    return L"32";
#elif defined(__LP64__) || defined(_LP64) || defined(__LLP64__)
    return L"64";
#elif defined(__ILP32__) || defined(_ILP32)
    return L"32";
#else
    return L"??";
#endif
}

// Prototypes
EFI_FILE_HANDLE get_esp_root(EFI_HANDLE image_handle);
EFI_STATUS FileRead(EFI_FILE_HANDLE root, CHAR16 *path, VOID **buffer, UINTN *size);
VOID parse_config(char *buffer, UINTN size, BOOT_CONFIG *config);
EFI_STATUS boot_linux(EFI_HANDLE image_handle, EFI_FILE_HANDLE root, BOOT_ENTRY *entry);
EFI_STATUS load_config(EFI_FILE_HANDLE root, CHAR16 *path, BOOT_CONFIG *config);
BOOT_ENTRY *display_menu(BOOT_ENTRY *head, INTN config_timeout, CHAR16 *default_label, UINTN scrwidth, UINTN scrheight);
EFI_STATUS manual_pe_load_and_jump(EFI_HANDLE image_handle, EFI_FILE_HANDLE root, BOOT_ENTRY *entry);
BOOLEAN file_exists(EFI_FILE_HANDLE root, CHAR16 *path);
BOOLEAN wait_for_specific_char(CHAR16 target);
VOID process_line_content(char *src, CHAR16 *dest, UINTN max_len);
VOID FormatMiddleTruncate(CONST CHAR16 *InputStr, CHAR16 *OutputBuffer, UINTN  MaxLen);
VOID display_message_file(EFI_FILE_HANDLE root, CHAR16 *path);
#endif
