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

#define T2T_SIMPLE_API_ONLY
#include <t2t/simple.h>

struct {
  t2t_simple_message_cb note;
  t2t_simple_message_cb diag;
  t2t_simple_message_cb pass;
  t2t_simple_message_cb fail;
} cb;

static void
do_abort()
{
  fprintf(stderr, "t2t is not initalized.\n");
  abort();
}

static size_t buffer_size=0;
static char  *buffer=NULL;

static void
buffer_allocate(size_t size)
{
  if(size == 0)
  {
    free(buffer);
    buffer = NULL;
    buffer_size = 0;
  }
  else if(size > buffer_size)
  {
    if(buffer != NULL)
      free(buffer);
    buffer = malloc(size);
    buffer_size = size;
    if(buffer == NULL)
    {
      fprintf(stderr, "malloc returned NULL.\n");
      abort();
    }
  }
}

#define buffer_printf()                                        \
{                                                              \
  va_list aptr;                                                \
  int ret;                                                     \
                                                               \
  va_start(aptr, format);                                      \
  ret = vsnprintf(buffer, buffer_size, format, aptr);          \
  va_end(aptr);                                                \
                                                               \
  if(ret >= buffer_size)                                       \
  {                                                            \
    buffer_allocate(ret+1);                                    \
    va_start(aptr, format);                                    \
    ret = vsnprintf(buffer, buffer_size, format, aptr);        \
    va_end(aptr);                                              \
                                                               \
    if(ret >= buffer_size)                                     \
    {                                                          \
      fprintf(stderr, "something weird happened.\n");          \
      abort();                                                 \
    }                                                          \
  }                                                            \
}

void
t2t_simple_note(const char *language, const char *filename, int linenumber, const char *function, const char *message)
{
  if(cb.note)
    cb.note(message, language, filename, linenumber, function);
  else
    do_abort();
}

void
t2t_simple_notef(const char *language, const char *filename, int linenumber, const char *function, const char *format, ...)
{
  if(cb.note)
  {
    buffer_printf();
    cb.note(buffer, language, filename, linenumber, function);
  }
  else
    do_abort();
}

void
t2t_simple_diag(const char *language, const char *filename, int linenumber, const char *function, const char *message)
{
  if(cb.diag)
    cb.diag(message, language, filename, linenumber, function);
  else
    do_abort();
}

void
t2t_simple_diagf(const char *language, const char *filename, int linenumber, const char *function, const char *format, ...)
{
  if(cb.diag)
  {
    buffer_printf();
    cb.diag(buffer, language, filename, linenumber, function);
  }
  else
    do_abort();
}


int
t2t_simple_pass(const char *language, const char *filename, int linenumber, const char *function, const char *name)
{
  if(cb.pass)
  {
    cb.pass(name, language, filename, linenumber, function);
    return 1;
  }
  else
    do_abort();
}

int
t2t_simple_fail(const char *language, const char *filename, int linenumber, const char *function, const char *name)
{
  if(cb.fail)
  {
    cb.fail(name, language, filename, linenumber, function);
    return 0;
  }
  else
    do_abort();
}

void
t2t_simple_init(t2t_simple_message_cb note, t2t_simple_message_cb diag, t2t_simple_message_cb pass, t2t_simple_message_cb fail)
{
  cb.note = note;
  cb.diag = diag;
  cb.pass = pass;
  cb.fail = fail;
  buffer_allocate(512);
}

void
t2t_simple_deinit()
{
  cb.note = cb.diag = cb.pass = cb.fail = NULL;
  buffer_allocate(0);
}