// SPDX-License-Identifier: MPL-2.0
#include "kernel/log.h"
#include "memory/page.h"
#include "memory/ring.h"
#include "cpu/serial.h"
sh_uint8 kernel_log_level=0;
sh_bool log_disable_serial_port=SH_FALSE;
sh_bool disable_global_logging_ring=SH_FALSE;
sh_ring_RING_BUFFER_HEADER logging_ring={.head=0,.tail=0,.buffer_bytes_size=4*4096,.data_start=(sh_uint8*)SH_VMEM_LAYOUT_LOGGING_RING_BUFFER_VA,.total_bytes_written=0};
void sh_log_load_serial_setting(sh_bool is_disabled) {
  log_disable_serial_port=is_disabled;
  return;
}
sh_bool sh_log_get_serial_setting() {
  return log_disable_serial_port;
}
void sh_log_load_logging_ring_size(sh_uint16 pages_count) {
  logging_ring.buffer_bytes_size=4096*pages_count;
  return;
}
sh_uint32 sh_log_get_logging_ring_size() {
  return logging_ring.buffer_bytes_size;
}
sh_uint64 sh_log_get_total_bytes_written() {
  return logging_ring.total_bytes_written;
}
void sh_log_disable_global_logging_ring() {
  disable_global_logging_ring=SH_TRUE;
}
void sh_log_byte(sh_uint8 byte) {
  if (log_disable_serial_port) return;
  sh_serial_send_byte(byte);
  if (!disable_global_logging_ring) sh_ring_write_byte(&logging_ring,(sh_uint8)byte);
  return;
}
SH_STATUS sh_log_string(const char* str) {
  while (*str) {
    sh_log_byte((sh_uint8)*str++);
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_int8(sh_int8 n) {
  return sh_log_int64((sh_int64)n);
}
SH_STATUS sh_log_int16(sh_int16 n) {
  return sh_log_int64((sh_int64)n);
}
SH_STATUS sh_log_int32(sh_int32 n) {
  return sh_log_int64((sh_int64)n);
}
SH_STATUS sh_log_int64(sh_int64 n) {
  sh_uint64 absolute_val;
  if (n<0) {
    sh_log_byte('-');
    absolute_val=(sh_uint64)(-(n+1))+1;
  } else {
    absolute_val=(sh_uint64)n;
  }
  return sh_log_uint64(absolute_val);
}
SH_STATUS sh_log_uint8(sh_uint8 n) {
  return sh_log_uint64((sh_uint64)n);
}
SH_STATUS sh_log_uint16(sh_uint16 n) {
  return sh_log_uint64((sh_uint64)n);
}
SH_STATUS sh_log_uint32(sh_uint32 n) {
  return sh_log_uint64((sh_uint64)n);
}
SH_STATUS sh_log_uint64(sh_uint64 n) {
  char buf[20];
  sh_uint32 i=0;
  if (n==0) {
    sh_log_byte('0');
    return SH_STATUS_SUCCESS;
  }
  while (n>0) {
    buf[i++]='0'+(sh_int8)(n%10);
    n/=10;
  }
  while (i--) {
    sh_log_byte((sh_uint8)buf[i]);
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_uint8_hex(sh_uint8 n) {
  return sh_log_uint64_hex((sh_uint64)n);
}
SH_STATUS sh_log_uint16_hex(sh_uint16 n) {
  return sh_log_uint64_hex((sh_uint64)n);
}
SH_STATUS sh_log_uint32_hex(sh_uint32 n) {
  return sh_log_uint64_hex((sh_uint64)n);
}
SH_STATUS sh_log_uint64_hex(sh_uint64 n) {
  char buf[16];
  sh_uint32 i=0;
  const char hex_digits[]="0123456789ABCDEF";
  if (n==0) {
    sh_log_byte('0');
    return SH_STATUS_SUCCESS;
  }
  while (n>0) {
    buf[i++]=hex_digits[n & 0xF];
    n>>=4;
  }
  while (i--) {
    sh_log_byte((sh_uint8)buf[i]);
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_uint64_hex_fixed(sh_uint64 n) {
  const char hex_digits[]="0123456789ABCDEF";
  for (sh_int8 i=15;i>=0;i--) {
    sh_uint8 nibble=(sh_uint8)((n>>(i*4))&0xF);
    sh_log_byte((sh_uint8)hex_digits[nibble]);
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_double(double value) {
  if (value<0) {
    sh_log_byte('-');
    value=-value;
  }
  sh_uint64 integer_part=(sh_uint64)value;
  double fractional_part=value-(double)integer_part;
  SH_STATUS status=sh_log_uint64(integer_part);
  if (status!=SH_STATUS_SUCCESS) return status;
  sh_log_byte('.');
  for (sh_iter64 i=0;i<6;i++) {
    fractional_part*=10.0;
    sh_uint64 digit=(sh_uint64)fractional_part;
    sh_log_byte('0'+(sh_uint8)digit);
    fractional_part-=(double)digit;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_format(const char* format,va_list args) {
  for (const char *p=format;*p!='\0';p++) {
    if (*p!='%') {
      sh_log_byte((sh_uint8)*p);
      continue;
    }
    p++;
    switch (*p) {
      case 's': {
        char *s=va_arg(args,char *);
        sh_log_string(s);
        break;
      }
      case 'd': {
        double s=va_arg(args,double);
        sh_log_double(s);
        break;
      }
      case '1': {
        if (p[1]=='u') {
          sh_log_uint8((sh_uint8)va_arg(args,int));
          p++;
        } else if (p[1]=='s') {
          sh_log_int8((sh_int8)va_arg(args,int));
          p++;
        } else if (p[1]=='U') {
          sh_log_uint8_hex((sh_uint8)va_arg(args,int));
          p++;
        }
        break;
      }
      case '2': {
        if (p[1]=='u') {
          sh_log_uint16((sh_uint16)va_arg(args,int));
          p++;
        } else if (p[1]=='s') {
          sh_log_int16((sh_int16)va_arg(args,int));
          p++;
        } else if (p[1]=='U') {
          sh_log_uint16_hex((sh_uint16)va_arg(args,int));
          p++;
        }
        break;
      }
      case '4': {
        if (p[1]=='u') {
          sh_log_uint32((sh_uint32)va_arg(args,int));
          p++;
        } else if (p[1]=='s') {
          sh_log_int32((sh_int32)va_arg(args,int));
          p++;
        } else if (p[1]=='U') {
          sh_log_uint32_hex((sh_uint32)va_arg(args,int));
          p++;
        }
        break;
      }
      case '8': {
        if (p[1]=='u') {
          sh_log_uint64(va_arg(args,unsigned long long));
          p++;
        } else if (p[1]=='s') {
          sh_log_int64(va_arg(args,long long));
          p++;
        } else if (p[1]=='U') {
          sh_log_uint64_hex(va_arg(args,unsigned long long));
          p++;
        }
        break;
      }
      case 'x': {
        sh_uint64 x=va_arg(args,sh_uint64);
        sh_log_uint64_hex_fixed(x);
        break;
      }
      case 'c': {
        sh_log_byte((sh_uint8)va_arg(args,int));
        break;
      }
      case '%': {
        sh_log_byte('%');
        break;
      }
      default:
        sh_log_byte((sh_uint8)*p);
        break;
    }
  }
  va_end(args);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_payload(sh_log_OUTPUT_PAYLOAD *payload) {
  if (payload==SH_NULLPTR) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  if (!(sh_log_output_type_valid(payload->output_type))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  if (!(sh_log_output_source_valid(payload->output_source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  if (payload->output_source!=SH_LOG_SOURCE_TEST && payload->output_type<kernel_log_level) {
    return SH_STATUS_SUCCESS;
  }
  sh_log_string("[Shelter:");
  if (payload->output_source==SH_LOG_SOURCE_MAIN) {
    sh_log_string("Main@");
  } else if (payload->output_source==SH_LOG_SOURCE_CONF) {
    sh_log_string("Conf@");
  } else if (payload->output_source==SH_LOG_SOURCE_PAGE) {
    sh_log_string("Page@");
  } else if (payload->output_source==SH_LOG_SOURCE_SLAB) {
    sh_log_string("Slab@");
  } else if (payload->output_source==SH_LOG_SOURCE_TEST) {
    sh_log_string("Test@");
  } else if (payload->output_source==SH_LOG_SOURCE_PEZ) {
    sh_log_string("Pez @");
  } else if (payload->output_source==SH_LOG_SOURCE_PBA) {
    sh_log_string("PBA @");
  } else if (payload->output_source==SH_LOG_SOURCE_HEAP) {
    sh_log_string("Heap@");
  } else if (payload->output_source==SH_LOG_SOURCE_STD) {
    sh_log_string("STD @");
  } else if (payload->output_source==SH_LOG_SOURCE_MEMS) {
    sh_log_string("MemS@");
  } else if (payload->output_source==SH_LOG_SOURCE_CPUF) {
    sh_log_string("CpuF@");
  } else if (payload->output_source==SH_LOG_SOURCE_ACPI) {
    sh_log_string("ACPI@");
  } else if (payload->output_source==SH_LOG_SOURCE_TSC) {
    sh_log_string("TSC @");
  } else if (payload->output_source==SH_LOG_SOURCE_SMP) {
    sh_log_string("SMP @");
  }
  if (payload->output_type==SH_LOG_DEBUG) {
    sh_log_string("Debug]    ");
  } else if (payload->output_type==SH_LOG_LOG) {
    sh_log_string("Log]      ");
  } else if (payload->output_type==SH_LOG_WARNING) {
    sh_log_string("Warning]  ");
  } else if (payload->output_type==SH_LOG_ERROR) {
    sh_log_string("Error]    ");
  } else if (payload->output_type==SH_LOG_CRITICAL) {
    sh_log_string("Critical] ");
  } else if (payload->output_type==SH_LOG_FATAL) {
    sh_log_string("Fatal]    ");
  } else if (payload->output_type==SH_LOG_TEST) {
    sh_log_string("Test]     ");
  } else if (payload->output_type==SH_LOG_FAULT) {
    sh_log_string("Fault]    ");
  }
  sh_log_uint64(payload->tsc_value);
  sh_log_string(" : ");
  sh_log_string(payload->message_pointer);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_payload_format(sh_log_OUTPUT_PAYLOAD *payload,va_list args) {
  if (payload==SH_NULLPTR) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  if (!(sh_log_output_type_valid(payload->output_type))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  if (!(sh_log_output_source_valid(payload->output_source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  if (payload->output_source!=SH_LOG_SOURCE_TEST && payload->output_type<kernel_log_level) {
    return SH_STATUS_SUCCESS;
  }
  sh_log_string("[Shelter:");
  if (payload->output_source==SH_LOG_SOURCE_MAIN) {
    sh_log_string("Main@");
  } else if (payload->output_source==SH_LOG_SOURCE_CONF) {
    sh_log_string("Conf@");
  } else if (payload->output_source==SH_LOG_SOURCE_PAGE) {
    sh_log_string("Page@");
  } else if (payload->output_source==SH_LOG_SOURCE_SLAB) {
    sh_log_string("Slab@");
  } else if (payload->output_source==SH_LOG_SOURCE_TEST) {
    sh_log_string("Test@");
  } else if (payload->output_source==SH_LOG_SOURCE_PEZ) {
    sh_log_string("Pez @");
  } else if (payload->output_source==SH_LOG_SOURCE_PBA) {
    sh_log_string("PBA @");
  } else if (payload->output_source==SH_LOG_SOURCE_HEAP) {
    sh_log_string("Heap@");
  } else if (payload->output_source==SH_LOG_SOURCE_STD) {
    sh_log_string("STD @");
  } else if (payload->output_source==SH_LOG_SOURCE_MEMS) {
    sh_log_string("MemS@");
  } else if (payload->output_source==SH_LOG_SOURCE_CPUF) {
    sh_log_string("CpuF@");
  } else if (payload->output_source==SH_LOG_SOURCE_ACPI) {
    sh_log_string("ACPI@");
  } else if (payload->output_source==SH_LOG_SOURCE_TSC) {
    sh_log_string("TSC @");
  } else if (payload->output_source==SH_LOG_SOURCE_SMP) {
    sh_log_string("SMP @");
  }
  if (payload->output_type==SH_LOG_DEBUG) {
    sh_log_string("Debug]    ");
  } else if (payload->output_type==SH_LOG_LOG) {
    sh_log_string("Log]      ");
  } else if (payload->output_type==SH_LOG_WARNING) {
    sh_log_string("Warning]  ");
  } else if (payload->output_type==SH_LOG_ERROR) {
    sh_log_string("Error]    ");
  } else if (payload->output_type==SH_LOG_CRITICAL) {
    sh_log_string("Critical] ");
  } else if (payload->output_type==SH_LOG_FATAL) {
    sh_log_string("Fatal]    ");
  } else if (payload->output_type==SH_LOG_TEST) {
    sh_log_string("Test]     ");
  } else if (payload->output_type==SH_LOG_FAULT) {
    sh_log_string("Fault]    ");
  }
  sh_log_uint64(payload->tsc_value);
  sh_log_string(" : ");
  sh_log_format(payload->message_pointer,args);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_load_log_level(sh_uint8 log_level) {
  kernel_log_level=log_level;
  return SH_STATUS_SUCCESS;
}
sh_uint8 sh_log_get_log_level() {
  return kernel_log_level;
}
SH_STATUS sh_log_test(const char* str) {
  if (str==SH_NULLPTR) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_TEST,
    .output_source=SH_LOG_SOURCE_TEST,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  sh_log_string("\n");
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_debug(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_DEBUG,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  sh_log_string("\n");
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_log(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_LOG,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  sh_log_string("\n");
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_warning(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_WARNING,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  sh_log_string("\n");
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_error(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_ERROR,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  sh_log_string("\n");
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_critical(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_CRITICAL,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  sh_log_string("\n");
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_fatal(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_FATAL,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  sh_log_string("\n");
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_fault(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_FAULT,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  sh_log_string("\n");
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_ltest(const char* str) {
  if (str==SH_NULLPTR) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_TEST,
    .output_source=SH_LOG_SOURCE_TEST,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_ldebug(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_DEBUG,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_llog(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_LOG,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_lwarning(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_WARNING,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_lerror(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_ERROR,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_lcritical(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_CRITICAL,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_lfatal(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_FATAL,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_lfault(const char* str,sh_log_OUTPUT_SOURCE source) {
  if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_FAULT,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=str
  };
  SH_STATUS status=sh_log_payload(&payload);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_ftest(const char* format,...) {
  if (format==SH_NULLPTR) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_TEST,
    .output_source=SH_LOG_SOURCE_TEST,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=format
  };
  va_list args;
  va_start(args,format);
  SH_STATUS status=sh_log_payload_format(&payload,args);
  va_end(args);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_fdebug(const sh_log_OUTPUT_SOURCE source,const char* format,...) {
  if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_DEBUG,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=format
  };
  va_list args;
  va_start(args,format);
  SH_STATUS status=sh_log_payload_format(&payload,args);
  va_end(args);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_flog(const sh_log_OUTPUT_SOURCE source,const char* format,...) {
  if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_LOG,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=format
  };
  va_list args;
  va_start(args,format);
  SH_STATUS status=sh_log_payload_format(&payload,args);
  va_end(args);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_fwarning(const sh_log_OUTPUT_SOURCE source,const char* format,...) {
  if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_WARNING,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=format
  };
  va_list args;
  va_start(args,format);
  SH_STATUS status=sh_log_payload_format(&payload,args);
  va_end(args);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_ferror(const sh_log_OUTPUT_SOURCE source,const char* format,...) {
  if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_ERROR,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=format
  };
  va_list args;
  va_start(args,format);
  SH_STATUS status=sh_log_payload_format(&payload,args);
  va_end(args);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_fcritical(const sh_log_OUTPUT_SOURCE source,const char* format,...) {
  if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_CRITICAL,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=format
  };
  va_list args;
  va_start(args,format);
  SH_STATUS status=sh_log_payload_format(&payload,args);
  va_end(args);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_ffatal(const sh_log_OUTPUT_SOURCE source,const char* format,...) {
  if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_FATAL,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=format
  };
  va_list args;
  va_start(args,format);
  SH_STATUS status=sh_log_payload_format(&payload,args);
  va_end(args);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_ffault(const sh_log_OUTPUT_SOURCE source,const char* format,...) {
  if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) {
    return SH_STATUS_INVALID_PARAMETER;
  }
  sh_log_OUTPUT_PAYLOAD payload={
    .output_type=SH_LOG_FAULT,
    .output_source=source,
    .tsc_value=sh_tsc_get_kernel_current_tsc(),
    .message_pointer=format
  };
  va_list args;
  va_start(args,format);
  SH_STATUS status=sh_log_payload_format(&payload,args);
  va_end(args);
  if (sh_status_error(status)) {
    return status;
  }
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_log_mem_stats(sh_log_OUTPUT_SOURCE source) {
  sh_page_MEM_STATS mem_stats;
  sh_page_get_memory_stats(&mem_stats);
  sh_log_flog(source,"Total physical memory (bytes) : %8u / 0x%8U\n",mem_stats.memory_total_bytes,mem_stats.memory_total_bytes);
  sh_log_flog(source,"Total physical memory (pages) : %8u / 0x%8U\n",mem_stats.memory_total_pages,mem_stats.memory_total_pages);
  sh_log_flog(source,"Total installed memory (bytes) : %8u / 0x%8U\n",mem_stats.memory_installed_bytes,mem_stats.memory_installed_bytes);
  sh_log_flog(source,"Total installed memory (pages) : %8u / 0x%8U\n",mem_stats.memory_installed_pages,mem_stats.memory_installed_pages);
  sh_log_flog(source,"Free memory : %d%%\n",mem_stats.free_ratio*100);
  sh_log_flog(source,"Used memory : %d%%\n",mem_stats.used_ratio*100);
  sh_log_flog(source,"Free pages : %8u\n",mem_stats.free_pages);
  sh_log_flog(source,"Used pages : %8u\n",mem_stats.used_pages);
  sh_log_flog(source,"Largest free block (pages) : %8u\n",mem_stats.largest_free_block);
  sh_log_flog(source,"Largest used block (pages) : %8u\n",mem_stats.largest_used_block);
  sh_log_flog(source,"Free block count : %8u\n",mem_stats.free_blocks_count);
  sh_log_flog(source,"Used block count : %8u\n",mem_stats.used_blocks_count);
  sh_log_flog(source,"Total memory taken by physical bitmap (bytes) : %8u / 0x%8U\n",mem_stats.physical_bitmap_size_bytes,mem_stats.physical_bitmap_size_bytes);
  sh_log_flog(source,"Total memory taken by physical bitmap (pages) : %8u / 0x%8U\n",mem_stats.physical_bitmap_size_pages,mem_stats.physical_bitmap_size_pages);
  return SH_STATUS_SUCCESS;
}
