#ifndef __ERRORF_C__
#define __ERRORF_C__ 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <perms.h>
#include "errorf.h"

// TO DO !!!!!

// Best Practices for va_list:  When intercepting variadic functions like vprintf,
// remember that va_list is a pointer-like object. If you need to read the arguments
// (e.g., to log them) and pass them to the real vprintf, you must first duplicate
// the argument list using va_copy and clean up with va_end to prevent memory corruption:

// https://stackoverflow.com/questions/10807310/platform-inconsistencies-with-vsprintf-and-va-list
// https://onlinedocs.microchip.com/oxy/GUID-07E1F2AB-C1A0-411C-966F-8738802C42B9-en-US-4/GUID-24E22499-EAEA-4FB2-A99D-739C4A28D618.html
// https://bumbershootsoft.wordpress.com/2022/07/24/custom-printf-wrapping-variadic-functions-in-c/

//va_list ap_copy;
//va_copy(ap_copy, ap);
// Now you can safely use ap_copy with va_arg() or similar,
// while ap goes to the original vprintf.
//va_end(ap_copy);

int fatalf(char *s, ...) {
  int string_length;
  va_list ap;          

  //fflush(stderr);
  //fflush(stdout);
  
  fprintf(stderr, "* Fatal error: ");
  va_start(ap, s);
  string_length = vfprintf(stderr, s, ap);
  va_end(ap);

  //fflush(stderr);

#ifdef NEVER
  fflush(stdout);
  
  fprintf(stdout, "* Fatal error: ");
  va_start(ap, s);
  string_length = vfprintf(stdout, s, ap);
  va_end(ap);

  //fflush(stderr);
  fflush(stdout);
#endif
  
  exit(EXIT_FAILURE);
  /* NOT REACHED */
  return string_length;
}

int errorf(char *s, ...) {
  int string_length;
  va_list ap;          

  //fflush(stdout);
  fprintf(stderr, "* Error: ");
  va_start(ap, s);
  string_length = vfprintf(stderr, s, ap);
  va_end(ap);

  //fflush(stderr); // although should be flushed already!
#ifdef NEVER
  fprintf(stdout, "* Error: ");
  va_start(ap, s);
  string_length = vfprintf(stdout, s, ap);
  va_end(ap);
#endif
  return string_length;
}

int warnf(char *s, ...) {
  int string_length;
  va_list ap;          

  //fflush(stdout);
  
  fprintf(stderr, "? Warning: ");
  va_start(ap, s);
  string_length = vfprintf(stderr, s, ap);
  va_end(ap);

  //fflush(stderr); // although should be flushed already!

#ifdef NEVER
  // Written to file.  I'll put this in a comment to make formatting the file through clang-format simpler ...
  fprintf(stdout, "\n/* Warning: ");
  va_start(ap, s);
  string_length = vfprintf(stdout, s, ap);
  va_end(ap);
  fprintf(stdout, " */\n");
#endif
  
  return string_length;
}

int debugf(char *s, ...) {
  int string_length;
  va_list ap;          

  //fflush(stdout);
  
  fprintf(stderr, "! ");
  va_start(ap, s);
  string_length = vfprintf(stderr, s, ap);
  va_end(ap);

  //fflush(stderr); // although should be flushed already!

#ifdef NEVER
  fprintf(stdout, "\n/* ! ");
  va_start(ap, s);
  string_length = vfprintf(stdout, s, ap);
  va_end(ap);
  fprintf(stdout, " */\n");
#endif
  
  return string_length;
}

char *strdupf(char *s, ...) {
  int string_length;
  char tmp[1024];
  va_list ap;

  va_start(ap, s);
  string_length = vsprintf(tmp, s, ap);
  va_end(ap);
  
  return strdup(tmp);
}

int nulllike(void *icode) {
  if (icode == NULL) return 1;
  if ((_imp_address)(icode) <= 0x1000ULL) return 1;
  return 0;
}

void *notnull(void *icode, char *what, char *sourcefile, int lineno) {
  if (icode == NULL) {
    errorf("\"%s\":%d  '%s' is NULL\n", sourcefile, lineno, what);
    return NULL;
  } else if ((_imp_address)(icode) <= 0x1000ULL) {
    errorf("\"%s\":%d  '%s' points to zero page (%p)\n", sourcefile, lineno, what, icode);
  }
  return icode;
}


#endif // !__ERRORF_C__
