#include "../lib.h"

#define THE_A 110

// CONSTANT Replica results signatures
#define SIG_X 32
#define SIG_Y 23
#define SIG_Z 67
#define SIG_MAX SIG_Z

// CONSTANT Voter Result Signatures
#define SIG_s_XYZ ((SIG_X - SIG_Y) + (SIG_X - SIG_Z))
#define SIG_s_XY (SIG_X - SIG_Y)
#define SIG_s_YZ (SIG_Y - SIG_Z)
#define SIG_s_XZ (SIG_X - SIG_Z)

#define REPLICA_COUNT 3

static enc_t cored_res;
static enc_t sum_out[REPLICA_COUNT];

#define XC sum_out[0]
#define YC sum_out[1]
#define ZC sum_out[2]

// The prints here can't happen because they're disabled under test ¯\_(ツ)_/¯

static INLINE enc_t apply(enc_t vc, sign_t bdyn) {
  if (bdyn > SIG_MAX) {
    PRINT("signature overflow.\n");
    fail_marker_detected();
  }
  return vc + bdyn;
}

static sign_t cored_vote(void) {
  if (equals(XC, YC, SIG_X, SIG_Y)) {
    if (equals(XC, ZC, SIG_X, SIG_Z)) {
      cored_res = apply(XC, (XC - YC) + (XC - ZC));
      return SIG_s_XYZ;
    } else {
      cored_res = apply(XC, (XC - YC));
      return SIG_s_XY;
    }
  } else if (equals(YC, ZC, SIG_Y, SIG_Z)) {
    cored_res = apply(YC, (YC - ZC));
    return SIG_s_YZ;
  } else if (equals(XC, ZC, SIG_X, SIG_Z)) {
    cored_res = apply(XC, (XC - ZC));
    return SIG_s_XZ;
  } else {
    PRINT("all replicas differ.\n");
    fail_marker_detected();
    return 0;
  }
}

template <const unsigned int N, const sign_t S> static INLINE void sum(void) {
  int sum = 0;
  for (int i = 0; i < 5; ++i) {
    sum += 1;
  }

  sum_out[N] = encode(sum, THE_A, S);
}

extern "C" EXPORT("wasm_module") int wasm_module(void) {
  XC = 0;
  YC = 0;
  ZC = 0;

  // "early" starts trace here

  sum<0, SIG_X>();
  sum<1, SIG_Y>();
  sum<2, SIG_Z>();

  // "late" starts trace here
  fail_start_trace();

  sign_t static_sig = cored_vote();

  fail_stop_trace();

  sign_t vote_result_sig;
  switch (static_sig) {
  case SIG_s_XYZ:
  case SIG_s_XY:
  case SIG_s_XZ:
    vote_result_sig = SIG_X;
    break;
  case SIG_s_YZ:
    vote_result_sig = SIG_Y;
    break;
  default:
    PRINT("unknown static_sig.\n");
    break;
  }

  // Inversely apply constant program flow signature.
  cored_res -= static_sig;

  /* Validate Vote result */
  if (!check(cored_res, THE_A, vote_result_sig)) {
    PRINT("voted result invalid.\n");
    fail_marker_detected();
    return 2;
  }

  plain_t res = decode(cored_res, THE_A, vote_result_sig);
  if (res == 5) {
    PRINT("cored success.\n");
    fail_marker_positive();
    return 0;
  } else {
    PRINT("undetected error.\n");
    fail_marker_negative();
    return 1;
  }
}
