#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 "position.h"

static struct position positions[POSITION_MAXNUM];
static int position_num = 0;

int position_check(void)
{
  if (position_num)
    return NLL_ERRCODE_POSITION_NOT_EMPTY;

  return 0;
}

int position_init(void)
{
  memset(positions, 0, sizeof(positions));
  position_num = 0;
  return 0;
}

int position_clear(void)
{
  return position_clean();
}

int position_clean(void)
{
  int r;

  while (position_num > 0) {
    if ((r = position_pop(POSITION_TYPE_NONE, NULL, NULL, NULL, NULL)) < 0)
      return r;
  }

  return 0;
}

int position_push(position_type_t type, spot_t spot, value_t value, int arg)
{
  int r;
  position_t position;

  if (position_num >= POSITION_MAXNUM)
    return NLL_ERRCODE_POSITION_STACK_OVER;

  position = &positions[position_num];
  memset(position, 0, sizeof(*position));
  position->type         = type;
  position->spot.line    = spot ? spot->line    : NULL;
  position->spot.command = spot ? spot->command : NULL;
  position->value        = NULL;
  position->arg          = arg;

  if ((r = value_alloc(&position->value)) < 0)
    return r;

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

  position_num++;

  return 0;
}

int position_pop(position_type_t type, position_type_t *typep,
		 spot_t spot, value_t value, int *argp)
{
  int r;
  position_t position;

  if (position_num == 0)
    return NLL_ERRCODE_POSITION_NOT_FOUND;

  position = &positions[position_num - 1];
  if ((type != POSITION_TYPE_NONE) && (type != position->type))
    return NLL_ERRCODE_POSITION_DIFFERENT_TYPE;

  position_num--;

  if (typep) {
    *typep = position->type;
  }

  if (spot) {
    spot->line    = position->spot.line;
    spot->command = position->spot.command;
  }

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

  if (argp) {
    *argp = position->arg;
  }

  if ((r = value_free(position->value)) < 0)
    return r;

  return 0;
}

int position_peek(position_type_t type, spot_t spot, value_t *valuep, int *argp)
{
  position_t position;

  if (position_num == 0)
    return NLL_ERRCODE_POSITION_NOT_FOUND;

  position = &positions[position_num - 1];
  if ((type != POSITION_TYPE_NONE) && (type != position->type))
    return NLL_ERRCODE_POSITION_DIFFERENT_TYPE;

  if (spot) {
    spot->line    = position->spot.line;
    spot->command = position->spot.command;
  }

  if (valuep) {
    *valuep = position->value;
  }

  if (argp) {
    *argp = position->arg;
  }

  return 0;
}

position_type_t position_get_type(void)
{
  position_t position;

  if (position_num == 0)
    return POSITION_TYPE_NONE;

  position = &positions[position_num - 1];

  return position->type;
}

int position_get(spot_t spot, position_t *positionp)
{
  position_t position;
  int i;

  for (i = 0; i < position_num; i++) {
    position = &positions[i];
    if ((position->spot.line == spot->line) &&
	(!spot->command || (position->spot.command == spot->command))) {
      *positionp = position;
      return 0;
    }
  }

  *positionp = NULL;
  return 0;
}

int position_set(spot_t spot, position_t position)
{
  position->spot.line = spot->line;
  position->spot.command = spot->command;
  return 0;
}

int position_reset(position_t position)
{
  struct spot s;
  s.line = NULL;
  s.command = NULL;
  return position_set(&s, position);
}

int position_dump(FILE *fp)
{
  position_t position;
  int i;
  char *s;

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

  for (i = 0; i < position_num; i++) {
    nll_wait_output(fp);
    position = &positions[i];
    switch (position->type) {
    case POSITION_TYPE_NONE:  s = "NONE";    break;
    case POSITION_TYPE_LOOP:  s = "LOOP";    break;
    case POSITION_TYPE_FOR:   s = "FOR";     break;
    case POSITION_TYPE_IF:    s = "IF";      break;
    case POSITION_TYPE_GOSUB: s = "GOSUB";   break;
    case POSITION_TYPE_LABEL: s = "LABEL";   break;
    case POSITION_TYPE_EVAL:  s = "EVAL";    break;
    default:                  s = "UNKNOWN"; break;
    }
    fprintf(fp, "[%d]\tType:%s\tArg:%d\tValue:", i, s, position->arg);
    value_dump(fp, position->value);
    fprintf(fp, "\n");
  }

  fflush(fp);

  return 0;
}
