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

#include "../parser/parser.h"
#include "../semantics/vardecl.h"
#include "../semantics/opcodes.h"
#include "../semantics/procdecl.h"
#include "../semantics/statement.h"

#include "custom/include/test/test_fmw.h"

extern int yyparse(struct ParserState *);

static void
init_state_for_test(struct ParserState *state, const char * buffer)
{
  state->buffer = buffer;
  state->strings = create_string_store();
  state->bufferSize = strlen(buffer);
  wh_array_init(&state->values, sizeof(struct SemValue));

  init_glbl_stmt(&state->globalStmt);
  state->pCurrentStmt = &state->globalStmt;
}

static void
free_state(struct ParserState *state)
{
  release_string_store(state->strings);
  clear_glbl_stmt(&(state->globalStmt));
  wh_array_clean(&state->values);
}

static bool_t
check_used_vals(struct ParserState *state)
{
  int vals_count = wh_array_count(&state->values);
  while (--vals_count >= 0)
    {
      struct SemValue *val = wh_array_get(&state->values, vals_count);
      if (val->val_type != VAL_REUSE)
        {
          return TRUE;                /* found value still in use */
        }

    }

  return FALSE;                        /* no value in use */
}

char proc_decl_buffer[] =
  "PROCEDURE test_proc_rc (v1 TABLE (f3 TEXT, f1 DATE, f2 DATE ARRAY, f0 INT16),\n"
  "                        v2 TABLE (some_field INT32, f3 TEXT, f1 HIRESTIME, f0 INT16, f2 DATE ARRAY))\n"
  "RETURN UINT32 \n"
  "DO\n"
  "   v1*[0] = v2[0];\n"
  "   v1{f3, f1}[0] = v2[0];\n"
  "   v2{some_field}[0] = v1{f0}[0];\n"
  "   v2![0] = v1[0];\n"
  "   \n"
  "   RETURN NULL;\n"
  "ENDPROC\n"
  "";

static bool_t
check_procedure(struct ParserState* state)
{
  const char* proc_name = "test_proc_rc";

  struct Statement *stmt = find_proc_decl(state, proc_name, strlen(proc_name), FALSE);
  uint8_t *code = wh_ostream_data(stmt_query_instrs( stmt));
  int code_size = wh_ostream_size(stmt_query_instrs( stmt));

  static uint8_t code_expected[] = {
      0x0E, 0x00,
      0x03, 0x00,
      0x0E, 0x01,
      0x03, 0x00,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x00, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x00, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x1B,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x03, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x03, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x17,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x06, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x06, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x27,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x09, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x09, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x20,
      0x14, 0x08,
      0x0E, 0x00,
      0x03, 0x00,
      0x0E, 0x01,
      0x03, 0x00,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x03, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x03, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x17,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x09, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x09, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x20,
      0x14, 0x06,
      0x0E, 0x01,
      0x03, 0x00,
      0x0E, 0x00,
      0x03, 0x00,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x0C, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x00, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x1C,
      0x14, 0x05,
      0x0E, 0x01,
      0x03, 0x00,
      0x0E, 0x00,
      0x03, 0x00,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x00, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x00, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x1B,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x03, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x03, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x19,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x06, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x06, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x27,
      0x10, 0x02, 0x00, 0x00, 0x00,
      0x77, 0x09, 0x00, 0x00, 0x00,
      0x10, 0x03, 0x00, 0x00, 0x00,
      0x75,
      0x10, 0x04, 0x00, 0x00, 0x00,
      0x77, 0x09, 0x00, 0x00, 0x00,
      0x10, 0x05, 0x00, 0x00, 0x00,
      0x75,
      0x20,
      0x14, 0x08,
      0x01, 0x01,
      0x2C
  };

  if (memcmp(code, code_expected, sizeof code_expected) != 0
      || code_size != sizeof code_expected)
  {
    return FALSE;
  }

  return TRUE;
}

int
main()
{
  bool_t test_result = TRUE;
  struct ParserState state = { 0, };

  init_state_for_test( &state, proc_decl_buffer);

  printf("Testing parse..");
  if (yyparse( &state) != 0)
  {
    printf("FAILED\n");
    test_result = FALSE;
  }
  else
  {
    printf("PASSED\n");
  }

  if (test_result)
  {
    printf("Testing garbage vals...");
    if (check_used_vals( &state))
    {
      /* those should no be here */
      printf("FAILED\n");
      test_result = FALSE;
    }
    else
    {
      printf("PASSED\n");
    }
  }

  printf("Testing different row copy constructs operator usage ...");
  if (check_procedure(&state))
  {
    printf("PASSED\n");
  }
  else
  {
    printf("FAILED\n");
    test_result = FALSE;
  }

  free_state( &state);
  printf("Memory peak: %u bytes \n", (uint_t)test_get_mem_peak());
  printf("Current memory usage: %u bytes...", (uint_t)test_get_mem_used());
  if (test_get_mem_used() != 0)
  {
    test_result = FALSE;
    printf("FAILED\n");
  }
  else
  {
    printf("PASSED\n");
  }

  if (test_result == FALSE)
  {
    printf("TEST RESULT: FAIL\n");
    return -1;
  }

  printf("TEST RESULT: PASS\n");
  return 0;
}

