#include <stdio.h>
#include <stdarg.h>

void vary(const char *s, ...) {
  va_list v;
  va_start(v, s);
  char d[20];
  vsnprintf(d, 20, s, v);
  puts(d);

  // Try it with copying
  va_list tempva;
  va_copy(tempva, v);
  vsnprintf(d, 20, s, tempva);
  puts(d);

  va_end(v);
}

void vary2(char color, const char *s, ...) {
  va_list v;
  va_start(v, s);
  char d[21];
  d[0] = color;
  vsnprintf(d + 1, 20, s, v);
  puts(d);
  va_end(v);
}

void varargs_listoffsets_list_evaluate(int count, va_list ap, int vaIteration) {
  while (count > 0) {
    const char *string = va_arg(ap, const char *);
    printf("%s", string);
    count--;
  }
  printf("\n");
}

void varags_listoffsets_list_copy(int count, va_list ap, int iteration) {
  va_list ap_copy;
  va_copy(ap_copy, ap);
  varargs_listoffsets_list_evaluate(count, ap_copy, iteration);
  va_end(ap_copy);
}

void varargs_listoffsets_args(int type, int count, ...) {
  va_list ap;
  va_start(ap, count);

  // evaluate a copied list
  varags_listoffsets_list_copy(count, ap, 1);
  varags_listoffsets_list_copy(count, ap, 2);
  varags_listoffsets_list_copy(count, ap, 3);
  varags_listoffsets_list_copy(count, ap, 4);
}

void varargs_listoffsets_main() {
  varargs_listoffsets_args(0, 5, "abc", "def", "ghi", "jkl", "mno", "<BAD+0>",
                           "<BAD+1>", "<BAD+2>", "<BAD+3>", "<BAD+4>",
                           "<BAD+5>", "<BAD+6>", "<BAD+7>", "<BAD+8>",
                           "<BAD+9>", "<BAD+10>", "<BAD+11>", "<BAD+12>",
                           "<BAD+13>", "<BAD+14>", "<BAD+15>", "<BAD+16>");
}

#define GETMAX(pref, type)              \
  type getMax##pref(int num, ...) {     \
    va_list vv;                         \
    va_start(vv, num);                  \
    type maxx = va_arg(vv, type);       \
    for (int i = 1; i < num; i++) {     \
      type curr = va_arg(vv, type);     \
      maxx = curr > maxx ? curr : maxx; \
    }                                   \
    va_end(vv);                         \
    return maxx;                        \
  }
GETMAX(i, int);
GETMAX(D, double);

int main(int argc, char **argv) {
  vary("*cheez: %d+%d*", 0,
       24);          // Also tests that '0' is not special as an array ender
  vary("*albeit*");  // Should not fail with no var args in vararg function
  vary2('Q', "%d*", 85);

  int maxxi = getMaxi(6, 2, 5, 21, 4, -10, 19);
  printf("maxxi:%d*\n", maxxi);
  double maxxD = getMaxD(6, (double)2.1, (double)5.1, (double)22.1, (double)4.1,
                         (double)-10.1, (double)19.1, (double)2);
  printf("maxxD:%.2f*\n", (float)maxxD);

  // And, as a function pointer
  void (*vfp)(const char * s, ...) = argc == 1211 ? NULL : vary;
  vfp("*vfp:%d,%d*", 22, 199);

  // ensure lists work properly when copied, reinited etc.
  varargs_listoffsets_main();

  return 0;
}
