#pragma once

#include <inttypes.h>
#include <locale.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>  // offsetof
#include <stdio.h>
#include <stdlib.h>  // malloc
#include <string.h>
#include <wchar.h>

// Some tests might actually include perl.h which has the real version of this
#if !defined(warn)
#define warn(FORMAT, ...)                                                          \
    fprintf(stderr, FORMAT " at %s line %i\n", ##__VA_ARGS__, __FILE__, __LINE__); \
    fflush(stderr);
#endif

#if defined _WIN32 || defined __CYGWIN__
#include <BaseTsd.h>
// typedef SSIZE_T ssize_t;
typedef signed __int64 int64_t;
#ifdef __GNUC__
#define DLLEXPORT __attribute__((dllexport))
#else
#define DLLEXPORT __declspec(dllexport)
#endif
#else
#ifdef __GNUC__
#if __GNUC__ >= 4
#define DLLEXPORT __attribute__((visibility("default")))
#else
#define DLLEXPORT __attribute__((dllimport))
#endif
#else
#define DLLEXPORT __declspec(dllexport)
#endif
#include <inttypes.h>
#include <sys/types.h>
#endif

#define DumpHex(addr, len) _DumpHex(addr, len, __FILE__, __LINE__)

void _DumpHex(const void * addr, size_t len, const char * file, int line) {
    fflush(stdout);
    int perLine = 16;
    // Silently ignore silly per-line values.
    if (perLine < 4 || perLine > 64)
        perLine = 16;
    size_t i;
    unsigned char buff[perLine + 1];
    const unsigned char * pc = (const unsigned char *)addr;
    if (addr == NULL) {
        fprintf(stderr, "# Dumping NULL pointer at %s line %d\n", file, line);
        return;
    }
    fprintf(stderr, "# Dumping %" PRIuPTR " bytes from %p at %s line %d\n", len, addr, file, line);
    // Length checks.
    if (len == 0) {
        warn("ZERO LENGTH");
        return;
    }
    if (len < 0) {
        warn("NEGATIVE LENGTH: %" PRIuPTR, len);
        return;
    }
    for (i = 0; i < len; i++) {
        if ((i % perLine) == 0) {  // Only print previous-line ASCII buffer for
            // lines beyond first.
            if (i != 0)
                fprintf(stderr, " | %s\n", buff);
            fprintf(stderr, "#  %03" PRIuPTR " ", i);  // Output the offset of current line.
        }
        // Now the hex code for the specific character.
        fprintf(stderr, " %02x", pc[i]);
        // And buffer a printable ASCII character for later.
        if ((pc[i] < 0x20) || (pc[i] > 0x7e))  // isprint() may be better.
            buff[i % perLine] = '.';
        else
            buff[i % perLine] = pc[i];
        buff[(i % perLine) + 1] = '\0';
    }
    // Pad out last line if not exactly perLine characters.
    while ((i % perLine) != 0) {
        fprintf(stderr, "   ");
        i++;
    }
    fprintf(stderr, " | %s\n", buff);
    fflush(stderr);
}
