// SPDX-License-Identifier: MPL-2.0
#include "../include/conf.h"
#include "../include/default.h"
#include <Uefi.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
UINT8 bp_conf_hex_char_to_uint8(CHAR16 c) {
  if (c>=L'0' && c<=L'9') return c-L'0';
  if (c>=L'a' && c<=L'f') return c-L'a'+10;
  if (c>=L'A' && c<=L'F') return c-L'A'+10;
  return 0xFF;
}
EFI_STATUS bp_conf_get_lines_number(bp_disk_FILE *file,UINTN *line_number) {
  if (file==NULL || line_number==NULL) {
    return EFI_INVALID_PARAMETER;
  }
  *line_number=0;
  for (UINTN i=0;i<file->file_size_bytes;++i) {
    if (file->content[i]=='\n') {
      (*line_number)++;
    }
  }
  if (file->file_size_bytes>0 && file->content[file->file_size_bytes-1]!='\n') {
      (*line_number)++;
  }
  return EFI_SUCCESS;
}
EFI_STATUS bp_conf_parse_file_lines(bp_disk_FILE *file,bp_conf_LINED_FILE **lined_file) {
  if (file==NULL || lined_file==NULL) {
    return EFI_INVALID_PARAMETER;
  }
  EFI_STATUS status;
  UINTN line_count=0;
  status=bp_conf_get_lines_number(file,&line_count);
  if (EFI_ERROR(status)) {
    return status;
  }
  *lined_file=AllocateZeroPool(sizeof(bp_conf_LINED_FILE));
  if (*lined_file==NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  (*lined_file)->count=0;
  (*lined_file)->capacity=line_count;
  (*lined_file)->lines=AllocateZeroPool(sizeof(bp_conf_LINE)*line_count);
  if ((*lined_file)->lines==NULL) {
    FreePool(*lined_file);
    return EFI_OUT_OF_RESOURCES;
  }
  UINTN start=0;
  for (UINTN i=0;i<file->file_size_bytes;i++) {
    if (i==file->file_size_bytes || file->content[i]=='\n') {
      UINTN len=i-start;
      CHAR16 *line_buf=AllocateZeroPool(sizeof(CHAR16)*(len+1));
      if (line_buf==NULL) {
        for (UINTN j=0;j<(*lined_file)->count;j++) {
          FreePool((*lined_file)->lines[j].line_content);
        }
        FreePool((*lined_file)->lines);
        FreePool(*lined_file);
        return EFI_OUT_OF_RESOURCES;
      }
      for (UINTN j=0;j<len;j++) {
        line_buf[j]=(CHAR16)file->content[start + j];
      }
      (*lined_file)->lines[(*lined_file)->count].line_content=line_buf;
      (*lined_file)->lines[(*lined_file)->count].length=len;
      (*lined_file)->count++;
      start=i+1;
    }
  }
  return EFI_SUCCESS;
}
EFI_STATUS bp_conf_parse_conf(bp_conf_LINED_FILE *lined_file,bp_conf_CONF **conf) {
  EFI_STATUS status=EFI_SUCCESS;
  *conf=AllocateZeroPool(sizeof(bp_conf_CONF));
  if (*conf==NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  UINTN count_of_pairs=0;
  for (UINTN i=0;i<lined_file->count;++i) {
    if (lined_file->lines[i].length>1) {
      if (lined_file->lines[i].line_content[0]==L'#') {
        continue;
      } else {
        count_of_pairs=count_of_pairs+1;
      }
    } else {
      continue;
    }
  }
  (*conf)->count=0;
  (*conf)->capacity=count_of_pairs;
  (*conf)->pairs=AllocateZeroPool(sizeof(bp_conf_PAIR)*count_of_pairs);
  if ((*conf)->pairs==NULL) {
    FreePool(*conf);
    return EFI_OUT_OF_RESOURCES;
  }
  for (UINTN i=0;i<lined_file->count;++i) {
    if (lined_file->lines[i].length>1) {
      if (lined_file->lines[i].line_content[0]==L'#') {
        continue;
      } else {
        UINTN egal_index=0;
        for (UINTN y=0;y<lined_file->lines[i].length;++y) {
          if (lined_file->lines[i].line_content[y]==L'=') {
            egal_index=y;
            break;
          }
        }
        if (egal_index==lined_file->lines[i].length-1 || egal_index==0) {
          return EFI_INVALID_PARAMETER;
        }
        CHAR16 *key=AllocateZeroPool(sizeof(CHAR16)*(egal_index+1));
        CHAR16 *value=AllocateZeroPool(sizeof(CHAR16)*(lined_file->lines[i].length-egal_index));
        if (key==NULL || value==NULL) {
          return EFI_OUT_OF_RESOURCES;
        }
        for (UINTN k=0;k<egal_index;k++) key[k]=lined_file->lines[i].line_content[k];
        for (UINTN k=egal_index+1;k<lined_file->lines[i].length;k++) value[k-egal_index-1]=lined_file->lines[i].line_content[k];
        key[egal_index]=0;
        value[lined_file->lines[i].length-egal_index-1]=0;
        (*conf)->pairs[(*conf)->count].key=key;
        (*conf)->pairs[(*conf)->count].value=value;
        (*conf)->count++;
      }
    } else {
      continue;
    }
  }
  return status;
}
EFI_STATUS bp_conf_fill_default_boot_conf(bp_conf_BOOT_CONF **boot_conf) {
  EFI_STATUS status=EFI_SUCCESS;
  *boot_conf=AllocateZeroPool(sizeof(bp_conf_BOOT_CONF));
  if (*boot_conf==NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  (*boot_conf)->serial_port_enabled=BP_DEFAULT_CONF_SERIAL_PORT_ENABLED;
  (*boot_conf)->serial_port_debuging=BP_DEFAULT_CONF_SERIAL_PORT_DEBUGING;
  (*boot_conf)->serial_port_erroring=BP_DEFAULT_CONF_SERIAL_PORT_ERRORING;
  (*boot_conf)->font=BP_DEFAULT_CONF_FONT;
  (*boot_conf)->disable_boot_animation=BP_DEFAULT_CONF_DISABLE_BOOT_ANIMATION;
  (*boot_conf)->default_horizontal_resolution=BP_DEFAULT_CONF_DEFAULT_HORIZONTAL_RESOLUTION;
  (*boot_conf)->default_vertical_resolution=BP_DEFAULT_CONF_DEFAULT_VERTICAL_RESOLUTION;
  UINT8 temp1[16]=BP_DEFAULT_CONF_INITFS_PARTITION_TYPE_GUID;
  CopyMem((*boot_conf)->initfs_partition_type_guid,temp1,16);
  UINT8 temp2[16]=BP_DEFAULT_CONF_SIGNSYST_PARTITION_TYPE_GUID;
  CopyMem((*boot_conf)->initfs_partition_type_guid,temp2,16);
  UINT8 temp3[16]=BP_DEFAULT_CONF_PARTITION_GUID;
  CopyMem((*boot_conf)->initfs_partition_guid,temp3,16);
  CopyMem((*boot_conf)->signsyst_partition_guid,temp3,16);
  (*boot_conf)->kernel_log_level=BP_DEFAULT_CONF_KERNEL_LOG_LEVEL;
  (*boot_conf)->kernel_test_benchmark=BP_DEFAULT_CONF_KERNEL_TEST_BENCHMARK;
  (*boot_conf)->kernel_bench_iterations=BP_DEFAULT_CONF_KERNEL_BENCH_ITERATIONS;
  (*boot_conf)->kernel_log_disable_serial_port=BP_DEFAULT_CONF_KERNEL_LOG_DISABLE_SERIAL_PORT;
  (*boot_conf)->kernel_disable_serial_port=BP_DEFAULT_CONF_KERNEL_DISABLE_SERIAL_PORT;
  (*boot_conf)->kernel_log_ring_size=BP_DEFAULT_CONF_KERNEL_LOG_RING_SIZE;
  (*boot_conf)->kernel_kbd_events_queue_capacity=BP_DEFAULT_CONF_KERNEL_KBD_EVENTS_QUEUE_CAPACITY;
  return status;
}
EFI_STATUS bp_conf_fill_boot_conf(bp_conf_CONF *conf,bp_conf_BOOT_CONF **boot_conf) {
  EFI_STATUS status=EFI_SUCCESS;
  for (UINTN i=0;i<conf->count;++i) {
    BOOLEAN found_key=FALSE;
    for (UINTN y=0;y<BP_CONF_BOOT_CONF_PARAMS_SIZE;++y) {
      if (StrCmp(conf->pairs[i].key,bp_conf_boot_conf_params[y])==0) {
        found_key=TRUE;
        break;
      }
    }
    if (!found_key) {
      Print(L"[BlastConf] Warning: found '%s' which isn't an expected key. Skipping.\n",conf->pairs[i].key);
      continue;
    }
    if (StrCmp(conf->pairs[i].key,L"serial_port_enabled")==0) {
      if (StrCmp(conf->pairs[i].value,L"false")==0) {
        (*boot_conf)->serial_port_enabled=FALSE;
      } else if (StrCmp(conf->pairs[i].value,L"true")==0) {
        (*boot_conf)->serial_port_enabled=TRUE;
      } else {
        Print(L"[BlastConf] Error: value given for serial_port_enabled isn't a boolean. Using default value.");
        continue;
      }
    } else if (StrCmp(conf->pairs[i].key,L"serial_port_debuging")==0) {
      if (StrCmp(conf->pairs[i].value,L"false")==0) {
        (*boot_conf)->serial_port_debuging=FALSE;
      } else if (StrCmp(conf->pairs[i].value,L"true")==0) {
        (*boot_conf)->serial_port_debuging=TRUE;
      } else {
        Print(L"[BlastConf] Error: value given for serial_port_debuging isn't a boolean. Using default value.");
        continue;
      }
    } else if (StrCmp(conf->pairs[i].key,L"serial_port_erroring")==0) {
      if (StrCmp(conf->pairs[i].value,L"false")==0) {
        (*boot_conf)->serial_port_erroring=FALSE;
      } else if (StrCmp(conf->pairs[i].value,L"true")==0) {
        (*boot_conf)->serial_port_erroring=TRUE;
      } else {
        Print(L"[BlastConf] Error: value given for serial_port_erroring isn't a boolean. Using default value.");
        continue;
      }
    } else if (StrCmp(conf->pairs[i].key,L"font")==0) {
      (*boot_conf)->font=conf->pairs[i].value;
    } else if (StrCmp(conf->pairs[i].key,L"disable_boot_animation")==0) {
      if (StrCmp(conf->pairs[i].value,L"false")==0) {
        (*boot_conf)->disable_boot_animation=FALSE;
      } else if (StrCmp(conf->pairs[i].value,L"true")==0) {
        (*boot_conf)->disable_boot_animation=TRUE;
      } else {
        Print(L"[BlastConf] Error: value given for disable_boot_animation isn't a boolean. Using default value.");
        continue;
      }
    } else if (StrCmp(conf->pairs[i].key,L"default_horizontal_resolution")==0) {
      if (StrCmp(conf->pairs[i].value,L"0")==0) {
        (*boot_conf)->default_horizontal_resolution=0;
      } else {
        UINT64 hor=0;
        if (conf->pairs[i].value==NULL) {
          continue;
        }
        for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) {
          CHAR16 c=conf->pairs[i].value[j];
          if (c>=L'0' && c<=L'9') {
            hor=hor*10+(c-L'0');
          } else {
            Print(L"[BlastConf] Error for key 'default_horizontal_resolution': %s isn't a valid integer value.\n",conf->pairs[i].value);
            return EFI_INVALID_PARAMETER;
          }
        }
        (*boot_conf)->default_horizontal_resolution=hor;
      }
    } else if (StrCmp(conf->pairs[i].key,L"default_vertical_resolution")==0) {
      if (StrCmp(conf->pairs[i].value,L"0")==0) {
        (*boot_conf)->default_vertical_resolution=0;
      } else {
        UINT64 vert=0;
        if (conf->pairs[i].value==NULL) {
          continue;
        }
        for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) {
          CHAR16 c=conf->pairs[i].value[j];
          if (c>=L'0' && c<=L'9') {
            vert=vert*10+(c-L'0');
          } else {
            Print(L"[BlastConf] Error for key 'default_vertical_resolution': %s isn't a valid integer value.\n",conf->pairs[i].value);
            return EFI_INVALID_PARAMETER;
          }
        }
        (*boot_conf)->default_vertical_resolution=vert;
      }
    } else if (StrCmp(conf->pairs[i].key,L"initfs_partition_type_guid")==0) {
      UINT8 guid[16];
      if (StrnLenS(conf->pairs[i].value,37)!=36) {
        Print(L"[BlastConf] Error for key 'initfs_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      }
      UINTN a,b=0;
      for (a=0;a<36;a++) {
        if (conf->pairs[i].value[a]==L'-') continue;
        UINT8 high=bp_conf_hex_char_to_uint8(conf->pairs[i].value[a]);
        UINT8 low=bp_conf_hex_char_to_uint8(conf->pairs[i].value[++a]);
        if (high==0xFF || low==0xFF) {
          Print(L"[BlastConf] Error for key 'initfs_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
          return EFI_INVALID_PARAMETER;
        }
        guid[b++]=(high<<4)|low;
      }
      if (b!=16) {
        Print(L"[BlastConf] Error for key 'initfs_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      }
      CopyMem((*boot_conf)->initfs_partition_type_guid,guid,16);
    } else if (StrCmp(conf->pairs[i].key,L"signsyst_partition_type_guid")==0) {
      UINT8 guid[16];
      if (StrnLenS(conf->pairs[i].value,37)!=36) {
        Print(L"[BlastConf] Error for key 'signsyst_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      }
      UINTN a,b=0;
      for (a=0;a<36;a++) {
        if (conf->pairs[i].value[a]==L'-') continue;
        UINT8 high=bp_conf_hex_char_to_uint8(conf->pairs[i].value[a]);
        UINT8 low=bp_conf_hex_char_to_uint8(conf->pairs[i].value[++a]);
        if (high==0xFF || low==0xFF) {
          Print(L"[BlastConf] Error for key 'signsyst_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
          return EFI_INVALID_PARAMETER;
        }
        guid[b++]=(high<<4)|low;
      }
      if (b!=16) {
        Print(L"[BlastConf] Error for key 'signsyst_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      }
      CopyMem((*boot_conf)->signsyst_partition_type_guid,guid,16);
    } else if (StrCmp(conf->pairs[i].key,L"initfs_partition_guid")==0) {
      UINT8 guid[16];
      if (StrnLenS(conf->pairs[i].value,37)!=36) {
        Print(L"[BlastConf] Error for key 'initfs_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      }
      UINTN a,b=0;
      for (a=0;a<36;a++) {
        if (conf->pairs[i].value[a]==L'-') continue;
        UINT8 high=bp_conf_hex_char_to_uint8(conf->pairs[i].value[a]);
        UINT8 low=bp_conf_hex_char_to_uint8(conf->pairs[i].value[++a]);
        if (high==0xFF || low==0xFF) {
          Print(L"[BlastConf] Error for key 'initfs_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
          return EFI_INVALID_PARAMETER;
        }
        guid[b++]=(high<<4)|low;
      }
      if (b!=16) {
        Print(L"[BlastConf] Error for key 'initfs_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      }
      CopyMem((*boot_conf)->initfs_partition_guid,guid,16);
    } else if (StrCmp(conf->pairs[i].key,L"signsyst_partition_guid")==0) {
      UINT8 guid[16];
      if (StrnLenS(conf->pairs[i].value,37)!=36) {
        Print(L"[BlastConf] Error for key 'signsyst_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      }
      UINTN a,b=0;
      for (a=0;a<36;a++) {
        if (conf->pairs[i].value[a]==L'-') continue;
        UINT8 high=bp_conf_hex_char_to_uint8(conf->pairs[i].value[a]);
        UINT8 low=bp_conf_hex_char_to_uint8(conf->pairs[i].value[++a]);
        if (high==0xFF || low==0xFF) {
          Print(L"[BlastConf] Error for key 'signsyst_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
          return EFI_INVALID_PARAMETER;
        }
        guid[b++]=(high<<4)|low;
      }
      if (b!=16) {
        Print(L"[BlastConf] Error for key 'signsyst_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      }
      CopyMem((*boot_conf)->signsyst_partition_guid,guid,16);
    } else if (StrCmp(conf->pairs[i].key,L"kernel_log_level")==0) {
      if (StrCmp(conf->pairs[i].value,L"0")==0) {
        (*boot_conf)->kernel_log_level=0;
      } else if (StrCmp(conf->pairs[i].value,L"1")==0) {
        (*boot_conf)->kernel_log_level=1;
      } else if (StrCmp(conf->pairs[i].value,L"2")==0) {
        (*boot_conf)->kernel_log_level=2;
      } else if (StrCmp(conf->pairs[i].value,L"3")==0) {
        (*boot_conf)->kernel_log_level=4;
      } else if (StrCmp(conf->pairs[i].value,L"4")==0) {
        (*boot_conf)->kernel_log_level=4;
      } else if (StrCmp(conf->pairs[i].value,L"5")==0) {
        (*boot_conf)->kernel_log_level=5;
      } else if (StrCmp(conf->pairs[i].value,L"6")==0) {
        (*boot_conf)->kernel_log_level=6;
      } else {
        Print(L"[BlastConf] Error: value given for kernel_log_level isn't a log level. Using default value.");
        continue;
      }
    } else if (StrCmp(conf->pairs[i].key,L"kernel_test_benchmark")==0) {
      if (StrCmp(conf->pairs[i].value,L"false")==0) {
        (*boot_conf)->kernel_test_benchmark=FALSE;
      } else if (StrCmp(conf->pairs[i].value,L"true")==0) {
        (*boot_conf)->kernel_test_benchmark=TRUE;
      } else {
        Print(L"[BlastConf] Error: value given for kernel_test_benchmark isn't a boolean. Using default value.");
        continue;
      }
    } else if (StrCmp(conf->pairs[i].key,L"kernel_bench_iterations")==0) {
      if (StrCmp(conf->pairs[i].value,L"0")==0) {
        (*boot_conf)->kernel_bench_iterations=0;
      } else {
        UINT64 vert=0;
        if (conf->pairs[i].value==NULL) {
          continue;
        }
        for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) {
          CHAR16 c=conf->pairs[i].value[j];
          if (c>=L'0' && c<=L'9') {
            vert=vert*10+(c-L'0');
          } else {
            Print(L"[BlastConf] Error for key 'kernel_bench_iterations': %s isn't a valid integer value.\n",conf->pairs[i].value);
            return EFI_INVALID_PARAMETER;
          }
        }
        if (vert>10000) {
          Print(L"[BlastConf] Error for key 'kernel_bench_iterations': %s shouldn't be above 10000.\n",conf->pairs[i].value);
          return EFI_INVALID_PARAMETER;
        }
        (*boot_conf)->kernel_bench_iterations=vert;
      }
    } else if (StrCmp(conf->pairs[i].key,L"kernel_log_disable_serial_port")==0) {
      if (StrCmp(conf->pairs[i].value,L"false")==0) {
        (*boot_conf)->kernel_log_disable_serial_port=FALSE;
      } else if (StrCmp(conf->pairs[i].value,L"true")==0) {
        (*boot_conf)->kernel_log_disable_serial_port=TRUE;
      } else {
        Print(L"[BlastConf] Error: value given for kernel_log_disable_serial_port isn't a boolean. Using default value.");
        continue;
      }
    } else if (StrCmp(conf->pairs[i].key,L"kernel_disable_serial_port")==0) {
      if (StrCmp(conf->pairs[i].value,L"false")==0) {
        (*boot_conf)->kernel_disable_serial_port=FALSE;
      } else if (StrCmp(conf->pairs[i].value,L"true")==0) {
        (*boot_conf)->kernel_disable_serial_port=TRUE;
      } else {
        Print(L"[BlastConf] Error: value given for kernel_disable_serial_port isn't a boolean. Using default value.");
        continue;
      }
    } else if (StrCmp(conf->pairs[i].key,L"kernel_log_ring_size")==0) {
      if (StrCmp(conf->pairs[i].value,L"0")==0) {
        (*boot_conf)->kernel_bench_iterations=0;
      } else {
        UINT64 vert=0;
        if (conf->pairs[i].value==NULL) {
          continue;
        }
        for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) {
          CHAR16 c=conf->pairs[i].value[j];
          if (c>=L'0' && c<=L'9') {
            vert=vert*10+(c-L'0');
          } else {
            Print(L"[BlastConf] Error for key 'kernel_log_ring_size': %s isn't a valid integer value.\n",conf->pairs[i].value);
            return EFI_INVALID_PARAMETER;
          }
        }
        if (vert>0xFFFF) {
          Print(L"[BlastConf] Error for key 'kernel_log_ring_size': %s shouldn't be above 65535.\n",conf->pairs[i].value);
          return EFI_INVALID_PARAMETER;
        }
        (*boot_conf)->kernel_log_ring_size=vert;
      }
    } else if (StrCmp(conf->pairs[i].key,L"kernel_kbd_events_queue_capacity")==0) {
      if (StrCmp(conf->pairs[i].value,L"0")==0) {
        Print(L"[BlastConf] Error for key 'kernel_kbd_events_queue_capacity': %s shouldn't be below 64.\n",conf->pairs[i].value);
        return EFI_INVALID_PARAMETER;
      } else {
        UINT64 cap=0;
        if (conf->pairs[i].value==NULL) {
          continue;
        }
        for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) {
          CHAR16 c=conf->pairs[i].value[j];
          if (c>=L'0' && c<=L'9') {
            cap=cap*10+(c-L'0');
          } else {
            Print(L"[BlastConf] Error for key 'kernel_kbd_events_queue_capacity': %s isn't a valid integer value.\n",conf->pairs[i].value);
            return EFI_INVALID_PARAMETER;
          }
        }
        if (cap>65535) {
          Print(L"[BlastConf] Error for key 'kernel_kbd_events_queue_capacity': %s shouldn't be above 65535.\n",conf->pairs[i].value);
          return EFI_INVALID_PARAMETER;
        } else if (cap<64) {
          Print(L"[BlastConf] Error for key 'kernel_kbd_events_queue_capacity': %s shouldn't be below 64.\n",conf->pairs[i].value);
          return EFI_INVALID_PARAMETER;
        }
        (*boot_conf)->kernel_kbd_events_queue_capacity=cap;
      }
    }
  }
  return status;
}
