/**
* This is a test for miao in $package$
*/
#include "si_test.h"
#include "miao.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

struct foo {
  char * s;
};

struct foo_array miao_of_type(struct foo);

int foo_compare(const void * v1, const void * v2) 
{ 
  const struct foo * f1, * f2;
  f1 = v1;
  f2 = v2;
  return strcmp(f1->s, f2->s);  
}

int foo_each_ptr(size_t i, const struct foo * f, FILE * extra) {
  fprintf(extra, "%u => %s\n", (unsigned int)i, f->s);
  return 0;
}

int foo_each(size_t i, struct foo f, FILE * extra) {
  fprintf(extra, "%u => %s\n", (unsigned int)i, f.s);
  return 0;
}

struct foo * foo_each_ptr_with_result(size_t i, struct foo * f, char * extra) {
  printf("%u => %s\n", (unsigned int)i, f->s);
  return f;
}

int foo_check_ptr(const struct foo * f, char * extra) {
  return (strcmp(f->s, extra) == 0);
}  

struct foo foo_each_with_result(size_t i, struct foo f, char * extra) {
  printf("%u => %s\n", (unsigned int)i, f.s);
  return f;
}

int foo_check(const struct foo f, char * extra) {
  return (strcmp(f.s, extra) == 0);
}  


TEST_FUNC(miao) {
  struct foo f1, f2 , f3;
  struct foo * e1, * e2, *e3;
  struct foo_array a[1];
  struct foo_array d[1];
  
  miao_init(a);
  miao_init(d);
  e1 = miao_push_ptr(a);
  TEST_NOTNULL(e1);
  e1->s = "world";
  e1 = miao_push_ptr(a);
  TEST_NOTNULL(e1);
  e1->s = "hello";
  miao_qsort(a, foo_compare);
  TEST_INTEQ(2, miao_size(a));
  TEST_PTREQ(e1, miao_get_ptr(a, 1));
  TEST_PTREQ(e1, miao_unsafe_get_ptr(a, 1));
  TEST_NULL(miao_get_ptr(a, 77));
  TEST_ASSERT(miao_out_of_bounds(a, 2));
  TEST_ASSERT(miao_cap(a) >= 2);
  TEST_ASSERT(miao_size(a) == 2);
  TEST_NOTNULL(miao_resize(a, 64));
  TEST_ASSERT(miao_cap(a) >= 64);
  TEST_ASSERT(miao_size(a) == 2);
  e1 = miao_get_ptr(a, 1);
  TEST_STREQ("world", e1->s);
  TEST_PTREQ(e1, miao_unsafe_get_ptr(a, 1));
  TEST_NULL(miao_get_ptr(a, 77));
  TEST_ASSERT(miao_out_of_bounds(a, 77));
  TEST_INTEQ(sizeof(struct foo), miao_elsize(a));
  TEST_NOTNULL(miao_copy(d, a));
  TEST_MEMEQ(a->a, miao_size(a) * miao_elsize(a), d->a);
  miao_push_ptr(a)->s = "foo";
  miao_push_ptr(a)->s = "bar";
  TEST_NOTNULL(miao_qsort(a, foo_compare));
  miao_each(a, foo_each    , stdout);
  miao_each_ptr(a, foo_each_ptr, stdout);
  miao_each_with_result(a, foo_each_with_result, f3, foo_check, "foo");
  TEST_STREQ("foo", f3.s);
  miao_each_ptr_with_result(a, foo_each_ptr_with_result, e3, foo_check_ptr, "foo");
  TEST_STREQ("foo", e3->s);  
  f1.s = "hello";
  e3 = miao_bsearch(a, foo_compare, &f1);
  TEST_STREQ(f1.s, e3->s);
  e2 = miao_pop_ptr(a);
  TEST_STREQ("world", e2->s);
  miao_done(a);
  TEST_ZERO(miao_size(a)); 
  TEST_DONE();
}  

TEST_FUNC(miao_delete) {
  struct foo f1, f2 , f3;
  struct foo * e1, * e2, *e3;
  struct foo_array a[1];
  struct foo_array d[1];
  
  miao_init(a);
  e1 = miao_push_ptr(a);
  e1->s = "0 zero";
  e1 = miao_push_ptr(a);
  e1->s = "1 one";
  e1 = miao_push_ptr(a);
  e1->s = "2 two";
  e1 = miao_push_ptr(a);
  e1->s = "3 three";
  e1 = miao_push_ptr(a);
  e1->s = "4 four";
  e1 = miao_push_ptr(a);
  e1->s = "5 five";
 
  miao_qsort(a, foo_compare);
  miao_each_with_result(a, foo_each_with_result, f3, foo_check, "foo");
  f1.s = "3 three";
  e1 = miao_bsearch(a, foo_compare, &f1);
  TEST_NOTNULL(e1);  
  TEST_INTNEQ(0, miao_delete_entry(a, e1));
  TEST_INTEQ(5, miao_size(a));
  miao_each_with_result(a, foo_each_with_result, f3, foo_check, "foo");

  f1.s = "2 two";
  TEST_INTNEQ(0, miao_delete_bsearch(a, foo_compare, &f1));
  TEST_INTEQ(4, miao_size(a));
  miao_each_with_result(a, foo_each_with_result, f3, foo_check, "foo");
  miao_done(a);
  
  TEST_DONE();
}

TEST_FUNC(miao_push) {
  struct foo f1, f2 , f3;
  struct foo * e1, * e2, *e3;
  struct foo_array a[1];
  struct foo_array d[1];
  
  miao_init(a);
  TEST_NOTNULL(miao_grow(a, 1));
  f1.s = "0 zero";
  miao_unsafe_push(a, f1);
  f1.s = "2 two";
  miao_push(a, f1);
  f1.s = "1 one";
  miao_push(a, f1);
  miao_qsort(a, foo_compare);
  miao_each_with_result(a, foo_each_with_result, f3, foo_check, "foo");
  miao_done(a);  
  
  TEST_DONE();
}

  
int main(void) {
  TEST_INIT();
  TEST_RUN(miao_push);
  TEST_RUN(miao_delete);
  TEST_RUN(miao);
  TEST_REPORT();
}