#ifndef _include_fail_h
#define _include_fail_h

#include <stdint.h>

#define INLINE __attribute__((always_inline)) inline
#define NOINLINE __attribute__((noinline))

#define EXPORT(fnct) __attribute__((export_name(fnct)))
#define IMPORT(fnct) __attribute__((import_module("env"), import_name(fnct)))

#if !defined(TARGET_FAIL) && !defined(TARGET_LINUX_BAREMETAL) &&               \
    !defined(TARGET_LINUX) && !defined(TARGET_WASM)
// Set to linux while editing to prevent lsp errors
#define TARGET_LINUX
#endif

// Both the wasm wrapper and the wasm module use lib.h
// TARGET_WASM is for the wasm module...
#ifdef TARGET_WASM
#define PRINT(fmt, ...) print(fmt)
#endif

// ...the rest is for the wasm wrapper
#ifdef TARGET_FAIL
#define MAIN void os_main(void)
#define PRINT(fmt, ...)
#define PRINT_ERROR(fmt, ...)
#define PRINT_SUCCESS(fmt, ...)
#define RET(val) return
#endif

#ifdef TARGET_LINUX_BAREMETAL
#define MAIN void os_main(void)
#define PRINT(fmt, ...)
#define PRINT_ERROR(fmt, ...)
#define PRINT_SUCCESS(fmt, ...)
#define RET(val) return
#endif

#ifdef TARGET_LINUX
#include <stdio.h>
#include <string.h>
#define MAIN int main(int argc, char *argv[])
#define PRINT(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
#define PRINT_ERROR(fmt, ...)                                                  \
  fprintf(stderr, "[Error]   ");                                               \
  fprintf(stderr, fmt, ##__VA_ARGS__)
#define PRINT_SUCCESS(fmt, ...)                                                \
  fprintf(stdout, "[Success] ");                                               \
  fprintf(stdout, fmt, ##__VA_ARGS__)
#define RET(val) return val
#endif

typedef uint16_t enc_t;
typedef uint8_t plain_t;
typedef int8_t sign_t;

#define check(vc, A, B) (((vc - B) % A) == 0)
#define encode(v, A, B) ((((plain_t)v) * ((sign_t)A)) + ((sign_t)B))
#define decode(vc, A, B) ((vc - B) / A)
#define equals(vc1, vc2, B1, B2) ((vc1 - vc2) == (B1 - B2))

#ifdef __cplusplus
extern "C" {
#endif

// Those functions are defined in the host program
void NOINLINE IMPORT("fail_start_trace")
    fail_start_trace(void); // Mark start of injection
void NOINLINE IMPORT("fail_stop_trace")
    fail_stop_trace(void); // Mark end of injection
void NOINLINE IMPORT("fail_marker_positive")
    fail_marker_positive(void); // Everything ok
void NOINLINE IMPORT("fail_marker_detected")
    fail_marker_detected(void); // Everything ok
void NOINLINE IMPORT("fail_marker_negative")
    fail_marker_negative(void); // Invalid code
void NOINLINE IMPORT("print") print(const char *msg);

#ifdef __cplusplus
}
#endif

#endif
