// SPDX-License-Identifier: GPL-3.0-or-later
#include "elilong.h"

// Helper for getting delay/timeout number from ascii strings
static INTN efi_atoi(char *ptr) {
  INTN res = 0;
  while (*ptr >= '0' && *ptr <= '9') {
    res = res * 10 + (*ptr - '0');
    ptr++;
  }
  return res;
}

// Helper to convert ASCII config lines to UEFI Unicode strings
void process_line_content(char *src, CHAR16 *dest, UINTN max_len) {
  UINTN i = 0;
  // Skip leading whitespace or quotes
  while (*src == ' ' || *src =='\t' || *src == '=' || *src == '\"') {
    src++;
  }
  // Copy until newline, quote, or we hit our buffer limit (minus null terminator)
  while (*src && *src != '\n' && *src != '\r' && *src != '\"' && i < (max_len - 1)) {
    dest[i++] = (CHAR16)*src++;
  }
  dest[i] = L'\0';

  // Trim trailing spaces
  dest[i] = L'\0';
  while (i > 0 && dest[i-1] == L' ') {
    dest[--i] = L'\0';
  }
}

VOID parse_config(char *buffer, UINTN size, BOOT_CONFIG *config) {
  BOOT_ENTRY *head = NULL, *current = NULL;
  char *ptr = buffer;
  char *end = buffer + size;
  INTN d_val = -2, t_val = -2; // -2 means keyword not found delay/timeout
  config->timeout = 50; // default 5s
  BOOLEAN prompt=FALSE;

  while (ptr < end) {
    // Skip leading whitespace/newlines/comments
    while (ptr < end && (*ptr == ' ' || *ptr == '\t')) ptr++;
    if (ptr >= end || *ptr == '\n' || *ptr == '\r' || *ptr == '#') {
      while (ptr < end && *ptr != '\n') ptr++;
      if (ptr < end) ptr++;
      continue;
    }
    if (CompareMem(ptr, "default", 7) == 0) {
      process_line_content(ptr + 7, config->default_label, MAX_LABEL);
    }
    // Check for delay/timeout keywords
    else if (CompareMem(ptr, "delay", 5) == 0 || CompareMem(ptr, "timeout", 7) == 0) {
      BOOLEAN is_d = (CompareMem(ptr, "delay", 5) == 0);
      char *v = ptr + (is_d ? 5 : 7);
      while (*v == ' ' || *v == '=' || *v == '\t') v++;  //strip separating whitespace and =

      if (CompareMem(v, "wait", 4) == 0) {
        if (is_d) d_val = -1; else t_val = -1;
      } else {
        INTN n = efi_atoi(v);
        if (n < 0) n = 0;
        if (n > 1200) n = 1200;
        if (is_d) d_val = n; else t_val = n;
      }
    }
    // show the menu without a timeout/delay (unless one is specified)
    else if (CompareMem(ptr, "prompt", 6) == 0) {
      prompt=TRUE;
    }
    else if (CompareMem(ptr, "message",7) == 0) {
      process_line_content(ptr + 7, config->message_path,MAX_PATH);
    }
    // Detect a new boot entry
    else if (CompareMem(ptr, "image", 5) == 0) {
      BOOT_ENTRY *new_entry;
      uefi_call_wrapper(ST->BootServices->AllocatePool, 3, EfiLoaderData, sizeof(BOOT_ENTRY), (void**)&new_entry);
      ZeroMem(new_entry, sizeof(BOOT_ENTRY));
      process_line_content(ptr + 5, new_entry->kernel_path, MAX_PATH);
      if (head == NULL) head = new_entry;
      else current->next = new_entry;
      current = new_entry;
    }
    // Parse attributes for the current entry
    else if (current != NULL) {
      if (CompareMem(ptr, "label", 5) == 0)
        process_line_content(ptr+5, current->label, MAX_LABEL);
      else if (CompareMem(ptr, "description", 11) == 0) // Added for v0.1.8 description tracking
        process_line_content(ptr+11, current->description, MAX_DESC);
      else if (CompareMem(ptr, "initrd", 6) == 0)
        process_line_content(ptr+6, current->initrd_path, MAX_PATH);
      else if (CompareMem(ptr, "root", 4) == 0)
        process_line_content(ptr+4, current->root_device, MAX_ROOTDEV);
      else if (CompareMem(ptr, "read-only", 9) == 0)
        current->mount_mode = MOUNT_RO;
      else if (CompareMem(ptr, "read-write", 10) == 0)
        current->mount_mode = MOUNT_RW;
      else if (CompareMem(ptr, "append", 6) == 0)
        process_line_content(ptr+6, current->append, MAX_APPEND);
    }

    // Move to the next line
    while (ptr < end && *ptr != '\n') ptr++;
    if (ptr < end) ptr++;
  }
  /* Determine final timeout: If wait then always wait. if prompt and neither delay
     or timeout set then wait, otherwise timeout is biggest of relay or timeout */
  if (d_val == -1 || t_val == -1) {
    config->timeout = -1; //explicit wait used
  } else {
    INTN max = (d_val > t_val) ? d_val : t_val;
    if (prompt) {
      if ( d_val == -2 && t_val == -2) {
        config->timeout = -1;   //pompt specified but not timeout or delay, so wait
      } else {
        config->timeout = max;    //prompt specified, but timeout or delay set, so use it.
      }
    } else { //prompt not set, and neither timeout nor delay set to wait
      if ( d_val == -2 && t_val == -2 ) {
        config->timeout = -2; //if prompt is not set and neither timeout or delay are set
                              //then use this value to tell menu not to show, just boot default.
      } else {
        config->timeout = max; // Use max if prompt is not set and there is a timeout or delay.
      }
    }
  }
  config->entries = head;
}

