/*
  test_labcomm2014.c -- Various labcomm2014 tests

  Copyright 2013 Anders Blomdell <anders.blomdell@control.lth.se>

  This file is part of LabComm.

  LabComm is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  LabComm is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "labcomm2014_private.h"
#include "labcomm2014_default_error_handler.h"
#include "labcomm2014_default_memory.h"
#include "labcomm2014_default_scheduler.h"
#include "test/gen/test_sample.h"

static struct labcomm2014_writer writer;

static struct expect {
  char *description;
  int result;
  int (*trampoline)(struct labcomm2014_decoder *context);
  struct labcomm2014_decoder *context;
} *expect;

static void check_expect()
{
  struct expect *p = expect;

  {
    int i;
    for (i = 0 ; i < writer.pos ; i++) {
      fprintf(stderr, "%02x ", writer.data[i]);
    }
    fprintf(stderr, "\n");
  }
  if (p && p->trampoline) {
    int err;
    
    expect = p + 1;
    fprintf(stderr, "Checking '%s' expected=%d ", p->description, p->result);
    err = p->trampoline(p->context);
    fprintf(stderr, "actual=%d\n", err);
    if (p->result >= 0 && p->result != err) {
      fprintf(stderr, "FAILED\n");
      exit(1);
    } else if (err == 0) {
      fprintf(stderr, "FAILED (unexpected 0)\n");
      exit(1);
    }
    writer.pos = 0;
  }
}

static unsigned char buffer[512];

static int writer_alloc(struct labcomm2014_writer *w, 
			struct labcomm2014_writer_action_context *action_context)
{
  w->data = buffer;
  w->data_size = sizeof(buffer);
  w->count = sizeof(buffer);
  
  return 0;
}

static int writer_start(struct labcomm2014_writer *w, 
			 struct labcomm2014_writer_action_context *action_context,
			 int index, const struct labcomm2014_signature *signature,
			 void *value)
{
  return 0;
}

static int buf_writer_end(
  struct labcomm2014_writer *w, 
  struct labcomm2014_writer_action_context *action_context)
{
  check_expect();
  return 0;
}

const struct labcomm2014_writer_action writer_action = {
  .alloc = writer_alloc,
  .start = writer_start,
  .end = buf_writer_end,
};

static struct labcomm2014_writer_action_context writer_action_context = {
  .next = NULL,
  .action = &writer_action,
  .context = NULL
}; 

static struct labcomm2014_writer writer =  {
  .action_context = &writer_action_context,
  .data = buffer,
  .data_size = sizeof(buffer),
  .count = sizeof(buffer),
  .pos = 0,
  .error = 0,
};

static int reader_alloc(struct labcomm2014_reader *r, 
			struct labcomm2014_reader_action_context *action_context)
{
  r->data = buffer;
  r->data_size = sizeof(buffer);
  r->count = 0;
  r->memory = labcomm2014_default_memory;
  
  return 0;
}

static int reader_fill(struct labcomm2014_reader *r, 
		       struct labcomm2014_reader_action_context *action_context)
{
  r->error = -ENOMEM;
  return r->error;
}

const struct labcomm2014_reader_action reader_action = {
  .alloc = reader_alloc,
  .fill = reader_fill,
};

static struct labcomm2014_reader_action_context reader_action_context = {
  .next = NULL,
  .action = &reader_action,
  .context = NULL
}; 

static struct labcomm2014_reader reader =  {
  .action_context = &reader_action_context,
  .data = buffer,
  .data_size = sizeof(buffer),
  .count = 0,
  .pos = 0,
  .error = 0,
};

static int32_t encoder_data[256];
static test_sample_test_var encoder_var = {
  .n_0 = 1,
  .n_2 = 1,
  .a = encoder_data,
};

static int32_t decoder_data[256];
static test_sample_test_var decoder_var = {
  .n_0 = 1,
  .n_2 = 1,
  .a = decoder_data,
};;

void handle_test_var(test_sample_test_var *v, void *ctx)
{
  decoder_var.a[0] = v->a[0];  
}

int test_decode_one(struct labcomm2014_decoder *decoder)
{
  int result;

  for (reader.count = 0 ; reader.count < writer.pos ; reader.count++) {
    reader.error = 0;
    reader.pos = 0;
    result = labcomm2014_decoder_decode_one(decoder); 
    if (result >= 0 ) {
      fprintf(stderr,
              "Got result from buffer with bogus length (%d, %d != %d)\n",
	      result, reader.count, writer.pos);
      exit(1);
    }
  }
  reader.error = 0;
  reader.pos = 0;
  reader.count = writer.pos;
  result = labcomm2014_decoder_decode_one(decoder);
  if (result < 0) {
    fprintf(stderr, "Got no result from buffer with correct length (%d)\n",
	    result);
    exit(1);
  }
  return result;
}

static void test_encode_decode(struct labcomm2014_encoder *encoder,
			       struct labcomm2014_decoder *decoder,
			       int expected, uint32_t n_0, uint32_t n_2)
{
  int err;

  writer.pos = 0;
  encoder_var.n_0 = n_0;
  encoder_var.n_2 = n_2;
  encoder_var.a[0] = 314;
  labcomm2014_encode_test_sample_test_var(encoder, &encoder_var);
  err = test_decode_one(decoder);
  fprintf(stderr, "decode of sample %u * 2 * %u -> size=%d err=%d\n", 
	  n_0, n_2, writer.pos, err);
  if (writer.pos != labcomm2014_sizeof_test_sample_test_var(&encoder_var)) {
    fprintf(stderr, "Incorrect sizeof %u * 2 * %u (%d != %d)\n",
	    n_0, n_2, 
	    writer.pos, labcomm2014_sizeof_test_sample_test_var(&encoder_var));
    exit(1);
  }
  if (writer.pos != expected) {
    fprintf(stderr, "Unexpected size %u * 2 * %u (%d != %d)\n",
	    n_0, n_2, 
	    writer.pos, expected);
    exit(1);
  }
}

int main(void)
{
  int i;
  struct labcomm2014_decoder *decoder = labcomm2014_decoder_new(
    &reader,
    labcomm2014_default_error_handler,
    labcomm2014_default_memory,
    labcomm2014_default_scheduler);
  struct expect expect_version[] = {
    { "Version", 1, test_decode_one, decoder },
    { 0, 0, 0 }
  };
  expect = expect_version;
  struct labcomm2014_encoder *encoder = labcomm2014_encoder_new(
    &writer, 
    labcomm2014_default_error_handler,
    labcomm2014_default_memory,
    labcomm2014_default_scheduler);
  labcomm2014_decoder_register_test_sample_test_var(decoder,
						handle_test_var, 
						NULL);
  struct expect expect_registration[] = {
    { "Sampledef", -1, test_decode_one, decoder },
#ifdef SHOULD_THIS_BE_REMOVED
    { "Typedef", -1, test_decode_one, decoder },
#endif
    { "Binding", -1, test_decode_one, decoder },
    { 0, 0, 0 }
  };
  expect = expect_registration;
  labcomm2014_encoder_register_test_sample_test_var(encoder);
  test_encode_decode(encoder, decoder, 12, 1, 1);
  if (decoder_var.a[0] != encoder_var.a[0]) {
    fprintf(stderr, "Failed to decode correct value %d != %d\n", 
	    encoder_var.a[0], decoder_var.a[0]);
    exit(1);
  }
  test_encode_decode(encoder, decoder, 36, 2, 2);
  test_encode_decode(encoder, decoder, 4, 0, 0);
  for (i = 1 ; i <= 4 ; i++) {
    test_encode_decode(encoder, decoder, 3+i, 0, (1<<(7*i))-1);
    test_encode_decode(encoder, decoder, 4+i, 0, (1<<(7*i)));
  }
  test_encode_decode(encoder, decoder, 8, 0, 4294967295);
  return 0;
}