#include "config.h"

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

#ifdef USE_NLLIBC
#include <nllibc.h>
#endif

#include "nlltypes.h"
#include "nlllib.h"
#include "value.h"
#include "stack.h"

struct stack {
  struct stack_frame stack[STACK_MAXNUM];
  int num;
};

static struct stack stacks[STACK_TYPE_NUM];

int stack_check(void)
{
  stack_frame_t stack_frame;
  int i, n;

  for (n = 0; n < STACK_TYPE_NUM; n++) {
    if (stacks[n].num)
      return NLL_ERRCODE_STACK_NOT_EMPTY;

    for (i = 0; i < STACK_MAXNUM; i++) {
      stack_frame = &stacks[n].stack[i];
      if (stack_frame->value)
	return NLL_ERRCODE_STACK_NOT_EMPTY;
    }
  }

  return 0;
}

int stack_init(void)
{
  stack_frame_t stack_frame;
  int i, n;

  memset(stacks, 0, sizeof(stacks));

  for (n = 0; n < STACK_TYPE_NUM; n++) {
    for (i = 0; i < STACK_MAXNUM; i++) {
      stack_frame = &stacks[n].stack[i];
      stack_frame->value = NULL;
    }

    stacks[n].num = 0;
  }

  return 0;
}

static int _getnum(stack_type_t type)
{
  return stacks[type].num;
}

int stack_getnum(void)
{
  return _getnum(STACK_TYPE_USER);
}

int stack_push_type(stack_type_t type, value_t value)
{
  stack_frame_t stack_frame;
  int r;

  if (stacks[type].num >= STACK_MAXNUM)
    return NLL_ERRCODE_STACK_STACK_OVER;

  stack_frame = &stacks[type].stack[stacks[type].num];

  if (!stack_frame->value) {
    if ((r = value_alloc(&stack_frame->value)) < 0)
      return r;
  }

  if (value) {
    if ((r = value_copy_value(stack_frame->value, value)) < 0)
      return r;
  }

  stacks[type].num++;

  return 0;
}

int stack_pop_type(stack_type_t type, value_t value)
{
  stack_frame_t stack_frame;
  int r;

  if (stacks[type].num == 0)
    return NLL_ERRCODE_STACK_NOT_FOUND;

  stack_frame = &stacks[type].stack[stacks[type].num - 1];

  stacks[type].num--;

  if (value) {
    if ((r = value_copy_value(value, stack_frame->value)) < 0)
      return r;
  }

  if ((r = value_clear(stack_frame->value)) < 0)
    return r;

  return 0;
}

int stack_push(value_t value)
{
  return stack_push_type(STACK_TYPE_USER, value);
}

int stack_pop(value_t value)
{
  return stack_pop_type(STACK_TYPE_USER, value);
}

int stack_clear(void)
{
  stack_frame_t stack_frame;
  int r, i, n;

  for (n = 0; n < STACK_TYPE_NUM; n++) {
    for (i = 0; i < STACK_MAXNUM; i++) {
      stack_frame = &stacks[n].stack[i];
      if (stack_frame->value) {
	if ((r = value_free(stack_frame->value)) < 0)
	  return r;
	stack_frame->value = NULL;
      }
    }

    stacks[n].num = 0;
  }

  return 0;
}

int stack_clean(void)
{
  int r, n;

  for (n = 0; n < STACK_TYPE_NUM; n++) {
    while (stacks[n].num > 0) {
      if ((r = stack_pop_type(n, NULL)) < 0)
	return r;
    }
  }

  return 0;
}

int stack_dump(FILE *fp)
{
  stack_frame_t stack_frame;
  int i, n;

  nll_wait_output(fp);
  fprintf(fp, "\n-- Stack dump --\n");

  for (n = 0; n < STACK_TYPE_NUM; n++) {
    if (!stacks[n].num)
      continue;

    if (n > 0) {
      nll_wait_output(fp);
      fprintf(fp, "\n-- Stack[%d] --\n", n);
    }

    for (i = 0; i < stacks[n].num; i++) {
      nll_wait_output(fp);
      stack_frame = &stacks[n].stack[i];
      fprintf(fp, "[%d]\tValue:", i);
      value_dump(fp, stack_frame->value);
      fprintf(fp, "\n");
    }

    fflush(fp);
  }

  return 0;
}
