#include "config.h"

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

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

#include "const.h"
#include "nlltypes.h"
#include "nlllib.h"
#include "memory.h"
#include "string.h"
#include "label.h"
#include "array.h"
#include "area.h"
#include "key.h"
#include "framebuf.h"
#include "value.h"

static int _procid = 0;

static int procid_set(void)
{
  if ((++_procid) == 0)
    _procid = 1;
  return _procid;
}

static int value_num = 0;
static int pool_num = 0;

static value_t pool = NULL;

static value_t pool_alloc(void)
{
  value_t value;

  if (!pool)
    return NULL;

  value = pool;
  pool = pool->next;
  pool_num--;

  return value;
}

static int pool_free(value_t value)
{
  value->next = pool;
  pool = value;
  pool_num++;
  return 0;
}

#ifndef NLL_MEMORY_STATIC
static int _init(void)
{
  return 0;
}

static int _done(void)
{
  while (pool)
    memory_free(pool_alloc());
  return 0;
}

static int _check(void)
{
  if (pool)
    return NLL_ERRCODE_VALUE_NOT_EMPTY;
  return 0;
}

static value_t _alloc(void)
{
  value_t value;
  value = pool_alloc();
  if (!value)
    value = memory_alloc(sizeof(*value));
  return value;
}

static int _free(value_t value)
{
  if ((VALUE_MAXNUM < 0) || (pool_num < VALUE_MAXNUM))
    return pool_free(value);
  memory_free(value);
  return 0;
}
#else
static struct value values[VALUE_MAXNUM];

static int _init(void)
{
  value_t value;
  int i;

  memset(values, 0, sizeof(values));

  for (i = 0; i < VALUE_MAXNUM; i++) {
    value = &values[i];
    value->next = pool;
    pool = value;
    pool_num++;
  }

  return 0;
}

static int _done(void)
{
  return 0;
}

static int _check(void)
{
  value_t value;
  int n = 0;

  for (value = pool; value && (n < VALUE_MAXNUM); value = value->next)
    n++;

  if (value || (n != VALUE_MAXNUM) || (n != pool_num))
    return NLL_ERRCODE_VALUE_NOT_EMPTY;

  return 0;
}

static value_t _alloc(void)
{
  return pool_alloc();
}

static int _free(value_t value)
{
  return pool_free(value);
}
#endif

int value_check(void)
{
  if (value_num)
    return NLL_ERRCODE_VALUE_NOT_EMPTY;

  return _check();
}

int value_alloc(value_t *valuep)
{
  value_t value;

  if ((value = _alloc()) == NULL)
    return NLL_ERRCODE_VALUE_BUFFER_OVER;
  value_num++;

  memset(value, 0, sizeof(*value));

  value->refcount++;

  *valuep = value;

  return 0;
}

int value_free(value_t value)
{
  value_t next;
  int r;

  if (value->refcount > 1) {
    value->refcount--;
    return 1;
  }

  for (; value; value = next) {
    next = value->next;
    value->next = NULL;

    if (--(value->refcount) == 0) {
      value->flags &= ~(VALUE_FLAG_CONST|VALUE_FLAG_RDONLY);
      if ((r = value_clear(value)) < 0)
	return r;

      value_num--;
      if ((r = _free(value)) < 0)
	return r;
    }
  }

  return 0;
}

int value_init(void)
{
  value_num = 0;
  pool_num = 0;
  pool = NULL;
  return _init();
}

int value_done(void)
{
  return _done();
}

int value_clear(value_t value)
{
  int r;
  value_type_t type;

  if (!value || (value->flags & (VALUE_FLAG_CONST|VALUE_FLAG_RDONLY)))
    return 0;

  type = value->type;

  /* For link of the loop, set undefined before clear process. */
  value->type = VALUE_TYPE_UNDEFINED;

  switch (type) {
  case VALUE_TYPE_UNDEFINED:
  case VALUE_TYPE_NULL:
  case VALUE_TYPE_INTEGER:
#ifdef NLL_FLOATING_POINT
  case VALUE_TYPE_FLOAT:
#endif
  case VALUE_TYPE_FUNCTION:
    break;

  case VALUE_TYPE_VALUE:
  case VALUE_TYPE_VECTOR:
    if (value->val.value) {
      if ((r = value_free(value->val.value)) < 0)
	return r;
    }
    break;

  case VALUE_TYPE_STRING:
    if (value->val.string) {
      if ((r = string_del(value->val.string)) < 0)
	return r;
    }
    break;

  case VALUE_TYPE_ARRAY:
    if (value->val.array.a) {
      if ((r = array_free(value->val.array.a, -1)) < 0)
	return r;
    }
    break;

  case VALUE_TYPE_AREA:
    if (value->val.area) {
      if ((r = area_free(value->val.area)) < 0)
	return r;
    }
    break;

  case VALUE_TYPE_POINTER:
    if (value->val.pointer.point) {
      if ((r = value_free(value->val.pointer.point)) < 0)
	return r;
    }
    break;

  case VALUE_TYPE_LABEL:
    if (value->val.label) {
      if ((r = label_del(value->val.label)) < 0)
	return r;
    }
    break;

  default:
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
  }

  return 0;
}

static int type_code[] = {
  NLL_T_UNDEFINED,
  NLL_T_NULL,
  NLL_T_VALUE,
  NLL_T_VECTOR,
  NLL_T_INTEGER,
  NLL_T_STRING,
#ifdef NLL_FLOATING_POINT
  NLL_T_FLOAT,
#endif
  NLL_T_ARRAY,
  NLL_T_AREA,
  NLL_T_POINTER,
  NLL_T_FUNCTION,
  NLL_T_LABEL,
};

static const char *type_name[] = {
  "UNDEF",
  "NULL",
  "VALUE",
  "VECTOR",
  "INTEGER",
  "STRING",
#ifdef NLL_FLOATING_POINT
  "FLOAT",
#endif
  "ARRAY",
  "AREA",
  "POINTER",
  "FUNCTION",
  "LABEL",
};

static int _typecode(value_type_t type)
{
  if ((type >= 0) && (type < VALUE_TYPE_NUM))
    return type_code[type];
  return NLL_T_UNKNOWN;
}

int value_typecode(value_t value)
{
  value = value_entity(value);
  if (!value)
    return NLL_T_UNKNOWN;
  return _typecode(value->type);
}

const char *value_typename_string(value_type_t type)
{
  if ((type >= 0) && (type < VALUE_TYPE_NUM))
    return type_name[type];
  return "UNKNOWN";
}

const char *value_typename(value_t value)
{
  value = value_entity(value);
  if (!value)
    return "NULL";
  return value_typename_string(value->type);
}

int value_is_true(value_t value)
{
  int r, length;
  integer_t integer;
  char *string;
#ifdef NLL_FLOATING_POINT
  double f;
#endif
  array_t array;
  area_t area;
  value_t point;
  function_t function;
  label_t label;

  value = value_entity(value);
  if (!value)
    return 0;

  switch (value->type) {
  case VALUE_TYPE_UNDEFINED:
  case VALUE_TYPE_NULL:
    return 0;

  case VALUE_TYPE_VECTOR:
    for (value = value->val.value; value; value = value->next) {
      if ((r = value_is_true(value)) < 0)
	return r;
      if (r)
	break;
    }
    return value ? 1 : 0;

  case VALUE_TYPE_INTEGER:
    if ((r = value_get_integer(value, &integer)) < 0)
      return r;
    return integer ? 1 : 0;

  case VALUE_TYPE_STRING:
    if ((r = value_get_string(value, &string, &length)) < 0)
      return r;
    return (length > 0) ? 1 : 0;

#ifdef NLL_FLOATING_POINT
  case VALUE_TYPE_FLOAT:
    if ((r = value_get_float(value, &f)) < 0)
      return r;
    return (f != 0.0) ? 1 : 0;
#endif

  case VALUE_TYPE_ARRAY:
    if ((r = value_get_array(value, &array, NULL)) < 0)
      return r;
    return array ? 1 : 0;

  case VALUE_TYPE_AREA:
    if ((r = value_get_area(value, &area)) < 0)
      return r;
    return area ? 1 : 0;

  case VALUE_TYPE_POINTER:
    if ((r = value_get_pointer(value, &point, NULL)) < 0)
      return r;
    return point ? 1 : 0;

  case VALUE_TYPE_FUNCTION:
    if ((r = value_get_function(value, &function)) < 0)
      return r;
    return function ? 1 : 0;

  case VALUE_TYPE_LABEL:
    if ((r = value_get_label(value, &label)) < 0)
      return r;
    return label ? 1 : 0;

  case VALUE_TYPE_VALUE:
  default:
    break;
  }

  return NLL_ERRCODE_VALUE_INVALID_TYPE;
}

value_t value_entity(value_t value)
{
  while (value && (value->type == VALUE_TYPE_VALUE))
    value = value->val.value;

  return value;
}

int value_insert_value(value_t *valuep)
{
  value_t value;
  int r;

  if ((r = value_alloc(&value)) < 0)
    return r;
  if ((r = value_set_next(value, *valuep)) < 0)
    return r;
  *valuep = value;

  return 0;
}

int value_extend_value(value_t *valuep)
{
  if (*valuep)
    return 0;
  return value_insert_value(valuep);
}

int value_shrink_value(value_t *valuep)
{
  int r;

  if (*valuep) {
    if ((r = value_free(*valuep)) < 0)
      return r;
    *valuep = NULL;
  }

  return 0;
}

int value_match_values(value_t *valuep, int num)
{
  int r, i;

  for (i = 0; i < num; i++) {
    if ((r = value_extend_value(valuep)) < 0)
      return r;
    valuep = &(*valuep)->next;
  }

  if ((r = value_shrink_value(valuep)) < 0)
    return r;

  return 0;
}

int value_cmp_value(value_t value0, value_t value1)
{
  value0 = value_entity(value0);
  value1 = value_entity(value1);
  if (!value0 && !value1) return 0;
  if (!value0 || !value1) return 1;

  if (value0->type != value1->type)
    return 1;

  switch (value0->type) {
  case VALUE_TYPE_NULL:
    return 0;
  case VALUE_TYPE_VECTOR:
    return value_cmp_values(value0->val.value, value1->val.value);
  case VALUE_TYPE_INTEGER:
    if (value0->val.integer != value1->val.integer)
      return 1;
    break;
  case VALUE_TYPE_STRING:
    if (!value0->val.string && !value1->val.string)
      return 0;
    if (!value0->val.string || !value1->val.string)
      return 1;
    if (value0->val.string->length != value1->val.string->length)
      return 1;
    if (memcmp(value0->val.string->s, value1->val.string->s, value0->val.string->length))
      return 1;
    break;
#ifdef NLL_FLOATING_POINT
  case VALUE_TYPE_FLOAT:
    if (value0->val.floating != value1->val.floating)
      return 1;
    break;
#endif
  case VALUE_TYPE_ARRAY:
    if ((value0->val.array.a      != value1->val.array.a) ||
	(value0->val.array.offset != value1->val.array.offset))
      return 1;
    break;
  case VALUE_TYPE_AREA:
    if (value0->val.area != value1->val.area)
      return 1;
    break;
  case VALUE_TYPE_POINTER:
    if ((value0->val.pointer.point  != value1->val.pointer.point) ||
	(value0->val.pointer.offset != value1->val.pointer.offset))
      return 1;
    break;
  case VALUE_TYPE_FUNCTION:
    if (value0->val.function != value1->val.function)
      return 1;
    break;
  case VALUE_TYPE_LABEL:
    if (value0->val.label != value1->val.label)
      return 1;
    break;
  case VALUE_TYPE_UNDEFINED:
    break;
  default:
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
  }

  return 0;
}

int value_cmp_values(value_t value0, value_t value1)
{
  int r;

  for (; value0 || value1; value0 = value0->next, value1 = value1->next) {
    if (!value0 || !value1)
      return 1;
    if ((r = value_cmp_value(value0, value1)) != 0)
      return r;
  }

  return 0;
}

int value_copy_value(value_t dvalue, value_t svalue)
{
  int r;
  value_t value;

  value = value_entity(svalue);
  if (!value)
    return 0;

  /* To avoid to free source value in the copy process */
  svalue->refcount++;

  switch (value->type) {
  case VALUE_TYPE_NULL:
    if ((r = value_set_null(dvalue)) < 0)
      goto ret;
    break;
  case VALUE_TYPE_VECTOR:
    if (value_get_type(dvalue) != VALUE_TYPE_VECTOR) {
      if ((r = value_set_vector(dvalue, NULL)) < 0)
	goto ret;
    }
    dvalue = value_entity(dvalue);
    if ((r = value_copy_values(&dvalue->val.value, value->val.value)) < 0)
      goto ret;
    break;
  case VALUE_TYPE_INTEGER:
    if ((r = value_set_integer(dvalue, value->val.integer)) < 0)
      goto ret;
    break;
  case VALUE_TYPE_STRING:
    if (value->val.string) {
      if ((r = value_set_string(dvalue, value->val.string->s, value->val.string->length)) < 0)
	goto ret;
    } else {
      if ((r = value_set_string(dvalue, NULL, -1)) < 0)
	goto ret;
    }
    break;
#ifdef NLL_FLOATING_POINT
  case VALUE_TYPE_FLOAT:
    if ((r = value_set_float(dvalue, value->val.floating)) < 0)
      goto ret;
    break;
#endif
  case VALUE_TYPE_ARRAY:
    if ((r = value_set_array(dvalue, value->val.array.a, value->val.array.offset)) < 0)
      goto ret;
    break;
  case VALUE_TYPE_AREA:
    if ((r = value_set_area(dvalue, value->val.area)) < 0)
      goto ret;
    break;
  case VALUE_TYPE_POINTER:
    if ((r = value_set_pointer(dvalue, value->val.pointer.point, value->val.pointer.offset)) < 0)
      goto ret;
    break;
  case VALUE_TYPE_FUNCTION:
    if ((r = value_set_function(dvalue, value->val.function)) < 0)
      goto ret;
    break;
  case VALUE_TYPE_LABEL:
    if ((r = value_set_label(dvalue, value->val.label)) < 0)
      goto ret;
    break;
  case VALUE_TYPE_UNDEFINED:
    break;
  default:
    r = NLL_ERRCODE_VALUE_INVALID_TYPE;
    goto ret;
  }

  r = 0;

ret:
  /* Decrement the reference counter incremented on the top of this function */
  value_free(svalue);

  return r;
}

int value_copy_values(value_t *dvaluep, value_t svalue)
{
  int r;

  for (; svalue; svalue = svalue->next) {
    if ((r = value_extend_value(dvaluep)) < 0)
      return r;
    if ((r = value_copy_value(*dvaluep, svalue)) < 0)
      return r;
    dvaluep = &(*dvaluep)->next;
  }

  if ((r = value_shrink_value(dvaluep)) < 0)
    return r;

  return 0;
}

int value_copy_values_novector(value_t *dvaluep, value_t svalue)
{
  int r;

  for (; svalue; svalue = svalue->next) {
    if ((r = value_extend_value(dvaluep)) < 0)
      return r;
    if (value_get_type(svalue) == VALUE_TYPE_VECTOR) {
      if ((r = value_set_vector(*dvaluep, NULL)) < 0)
	return r;
    } else {
      if ((r = value_copy_value(*dvaluep, svalue)) < 0)
	return r;
    }
    dvaluep = &(*dvaluep)->next;
  }

  if ((r = value_shrink_value(dvaluep)) < 0)
    return r;

  return 0;
}

value_type_t value_get_type(value_t value)
{
  value = value_entity(value);
  if (!value)
    return VALUE_TYPE_UNDEFINED;
  return value->type;
}

unsigned int value_get_flags(value_t value)
{
  value = value_entity(value);
  return value->flags;
}

int value_set_flags(value_t value, unsigned int flags)
{
  value = value_entity(value);
  value->flags |= flags;
  return 0;
}

int value_reset_flags(value_t value, unsigned int flags)
{
  value = value_entity(value);
  value->flags &= ~flags;
  return 0;
}

int value_size(value_t value)
{
  int n = 0;

  value = value_entity(value);
  if (!value)
    return 0;

  switch (value->type) {
  case VALUE_TYPE_INTEGER:
#ifdef NLL_FLOATING_POINT
  case VALUE_TYPE_FLOAT:
#endif
    n = 1;
    break;
  case VALUE_TYPE_VECTOR:
    for (value = value->val.value; value; value = value->next)
      n++;
    break;
  case VALUE_TYPE_STRING:
    if (value->val.string)
      n = value->val.string->length;
    break;
  case VALUE_TYPE_ARRAY:
    if (value->val.array.a)
      n = array_get_number(value->val.array.a) - value->val.array.offset;
    break;
  case VALUE_TYPE_AREA:
    if (value->val.area)
      n = area_get_size(value->val.area);
    break;
  case VALUE_TYPE_POINTER:
    if (value->val.pointer.point)
      n = 1;
    break;
  case VALUE_TYPE_FUNCTION:
    if (value->val.function)
      n = 1;
    break;
  case VALUE_TYPE_LABEL:
    if (value->val.label)
      n = 1;
    break;
  case VALUE_TYPE_UNDEFINED:
  case VALUE_TYPE_NULL:
  default:
    break;
  }

  return n;
}

int value_is_same(value_t value0, value_t value1)
{
  value0 = value_entity(value0);
  value1 = value_entity(value1);
  return (value0 && (value0 == value1)) ? 1 : 0;
}

int value_chain_to_vector(value_t *valuep)
{
  value_t value;
  int r;

  if ((*valuep)->type == VALUE_TYPE_VECTOR)
    return 0;

  if ((*valuep)->type == VALUE_TYPE_UNDEFINED) {
    if ((r = value_set_vector(*valuep, NULL)) < 0)
      return r;
    return 0;
  }

  if ((r = value_alloc(&value)) < 0)
    return r;
  if ((r = value_set_vector(value, NULL)) < 0)
    return r;
  value->val.value = *valuep;
  *valuep = value;

  return 0;
}

int value_vector_to_chain(value_t *valuep)
{
  value_t value;
  int r;

  if ((*valuep)->type != VALUE_TYPE_VECTOR)
    return 0;

  if ((*valuep)->val.value == NULL) {
    if ((r = value_clear(*valuep)) < 0)
      return r;
    return 0;
  }

  value = *valuep;
  *valuep = (*valuep)->val.value;
  value->val.value = NULL;

  if ((r = value_free(value)) < 0)
    return r;

  return 0;
}

int value_value_to_vector(value_t value)
{
  value_t _value;

  if (value->type == VALUE_TYPE_VALUE) {
    _value = value->val.value;
    value->type = VALUE_TYPE_VECTOR;
    value->val.value = _value;
  }

  return 0;
}

int value_vector_to_value(value_t value)
{
  value_t _value;

  if (value->type == VALUE_TYPE_VECTOR) {
    _value = value->val.value;
    value->type = VALUE_TYPE_VALUE;
    value->val.value = _value;
  }

  return 0;
}

int value_set_next(value_t value, value_t _value)
{
  int r;

  if (_value)
    _value->refcount++;

  if (value->next) {
    if ((r = value_free(value->next)) < 0)
      return r;
  }

  value->next = _value;

  return 0;
}

int value_set_null(value_t value)
{
  int r;

  value = value_entity(value);
  if (!value)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_NULL)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  value->type = VALUE_TYPE_NULL;

  return 0;
}

int value_get_value(value_t value, value_t *valuep)
{
  if (value->type != VALUE_TYPE_VALUE)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (valuep)
    *valuep = value->val.value;

  return 0;
}

int value_set_value(value_t value, value_t _value)
{
  int r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_VALUE)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  _value->refcount++;

  if (value->type == VALUE_TYPE_VALUE) {
    if ((r = value_free(value->val.value)) < 0)
      return r;
  }

  value->type = VALUE_TYPE_VALUE;
  value->val.value = _value;

  return 0;
}

int value_set_values(value_t value, value_t _value)
{
  int r;

  for (; value && _value; value = value->next, _value = _value->next) {
    if ((r = value_set_value(value, _value)) < 0)
      return r;
  }

  return 0;
}

int value_get_vector(value_t value, value_t *valuep)
{
  value = value_entity(value);
  if (!value)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (value->type != VALUE_TYPE_VECTOR)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (valuep)
    *valuep = value->val.value;

  return 0;
}

int value_set_vector(value_t value, value_t _value)
{
  int r;

  value = value_entity(value);
  if (!value)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_VECTOR)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  if (_value)
    _value->refcount++;

  if (value->type == VALUE_TYPE_VECTOR) {
    if (value->val.value) {
      if ((r = value_free(value->val.value)) < 0)
	return r;
    }
  }

  value->type = VALUE_TYPE_VECTOR;
  value->val.value = _value;

  return 0;
}

static int _getval(value_t *valuep)
{
  value_t value;

  value = value_entity(*valuep);
  if (!value)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

/* #define GETVAL_SEARCH_VECTOR */
#ifdef GETVAL_SEARCH_VECTOR
  if (value->type == VALUE_TYPE_VECTOR) {
    if (!value->val.value)
      return NLL_ERRCODE_VALUE_EMPTY_VALUE;
    value = value->val.value;
  }
#endif

  *valuep = value;

  return 0;
}

static int _getval_alloc(value_t *valuep)
{
  value_t value;
#ifdef GETVAL_SEARCH_VECTOR
  int r;
#endif

  value = value_entity(*valuep);
  if (!value)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

#ifdef GETVAL_SEARCH_VECTOR
  if (value->type == VALUE_TYPE_VECTOR) {
    if (!value->val.value) {
      if (value->flags & (VALUE_FLAG_CONST|VALUE_FLAG_RDONLY))
	return NLL_ERRCODE_VALUE_EMPTY_VALUE;
      if ((r = value_alloc(&value->val.value)) < 0)
	return r;
    }
    value = value->val.value;
  }
#endif

  *valuep = value;

  return 0;
}

int value_get_integer(value_t value, integer_t *integerp)
{
  int r;

  if ((r = _getval(&value)) < 0)
    return r;

  if (value->type != VALUE_TYPE_INTEGER)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (integerp)
    *integerp = value->val.integer;

  return 0;
}

int value_set_integer(value_t value, integer_t integer)
{
  int r;

  if ((r = _getval_alloc(&value)) < 0)
    return r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_INTEGER)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  value->type = VALUE_TYPE_INTEGER;
  value->val.integer = integer;

  return 0;
}

int value_get_string(value_t value, char **stringp, int *lengthp)
{
  int r;

  if ((r = _getval(&value)) < 0)
    return r;

  if (value->type != VALUE_TYPE_STRING)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (stringp)
    *stringp = value->val.string ? value->val.string->s : NULL;
  if (lengthp)
    *lengthp = value->val.string ? value->val.string->length : -1;

  return 0;
}

int value_set_string(value_t value, const char *string, int length)
{
  int r;

  if ((r = _getval_alloc(&value)) < 0)
    return r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_STRING)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  if (!string) {
    if ((value->type == VALUE_TYPE_STRING) && value->val.string) {
      if ((r = string_del(value->val.string)) < 0)
	return r;
    }
    value->type = VALUE_TYPE_STRING;
    value->val.string = NULL;
  } else {
    if (length > STRING_MAXLEN)
      return NLL_ERRCODE_STRING_TOO_LONG;

    if ((value->type == VALUE_TYPE_UNDEFINED) ||
	((value->type == VALUE_TYPE_STRING) && !value->val.string)) {
      if ((r = string_alloc(&value->val.string)) < 0)
	return r;
    }

    value->type = VALUE_TYPE_STRING;
    if ((r = string_set(value->val.string, string, length)) < 0)
      return 0;
  }

  return 0;
}

#ifdef NLL_FLOATING_POINT
int value_get_float(value_t value, double *floatingp)
{
  int r;

  if ((r = _getval(&value)) < 0)
    return r;

  if (value->type != VALUE_TYPE_FLOAT)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (floatingp)
    *floatingp = value->val.floating;

  return 0;
}

int value_set_float(value_t value, double floating)
{
  int r;

  if ((r = _getval_alloc(&value)) < 0)
    return r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_FLOAT)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  value->type = VALUE_TYPE_FLOAT;
  value->val.floating = floating;

  return 0;
}
#endif

int value_get_array(value_t value, array_t *arrayp, int *offsetp)
{
  int r;

  if ((r = _getval(&value)) < 0)
    return r;

  if (value->type != VALUE_TYPE_ARRAY)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (arrayp)
    *arrayp = value->val.array.a;
  if (offsetp)
    *offsetp = value->val.array.offset;

  return 0;
}

int value_set_array(value_t value, array_t array, int offset)
{
  int r;

  if ((r = _getval_alloc(&value)) < 0)
    return r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_ARRAY)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  if (array)
    array[0].value->refcount++;

  if (value->type == VALUE_TYPE_ARRAY) {
    if (value->val.array.a) {
      if ((r = array_free(value->val.array.a, -1)) < 0)
	return r;
    }
  }

  value->type = VALUE_TYPE_ARRAY;
  value->val.array.a = array;
  value->val.array.offset = array ? offset : 0;

  return 0;
}

int value_get_area(value_t value, area_t *areap)
{
  int r;

  if ((r = _getval(&value)) < 0)
    return r;

  if (value->type != VALUE_TYPE_AREA)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (areap)
    *areap = value->val.area;

  return 0;
}

int value_set_area(value_t value, area_t area)
{
  int r;

  if ((r = _getval_alloc(&value)) < 0)
    return r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_AREA)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  if (area)
    area->refcount++;

  if (value->type == VALUE_TYPE_AREA) {
    if (value->val.area) {
      if ((r = area_free(value->val.area)) < 0)
	return r;
    }
  }

  value->type = VALUE_TYPE_AREA;
  value->val.area = area;

  return 0;
}

int value_get_pointer(value_t value, value_t *pointp, int *offsetp)
{
  int r;

  if ((r = _getval(&value)) < 0)
    return r;

  if (value->type != VALUE_TYPE_POINTER)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (pointp)
    *pointp = value->val.pointer.point;
  if (offsetp)
    *offsetp = value->val.pointer.offset;

  return 0;
}

int value_set_pointer(value_t value, value_t point, int offset)
{
  int r;

  if ((r = _getval_alloc(&value)) < 0)
    return r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_POINTER)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  if (point)
    point->refcount++;

  if (value->type == VALUE_TYPE_POINTER) {
    if (value->val.pointer.point) {
      if ((r = value_free(value->val.pointer.point)) < 0)
	return r;
    }
  }

  value->type = VALUE_TYPE_POINTER;
  value->val.pointer.point = point;
  value->val.pointer.offset = point ? offset : 0;

  return 0;
}

int value_get_function(value_t value, function_t *functionp)
{
  int r;

  if ((r = _getval(&value)) < 0)
    return r;

  if (value->type != VALUE_TYPE_FUNCTION)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (functionp)
    *functionp = value->val.function;

  return 0;
}

int value_set_function(value_t value, function_t function)
{
  int r;

  if ((r = _getval_alloc(&value)) < 0)
    return r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_FUNCTION)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  value->type = VALUE_TYPE_FUNCTION;
  value->val.function = function;

  return 0;
}

int value_get_label(value_t value, label_t *labelp)
{
  int r;

  if ((r = _getval(&value)) < 0)
    return r;

  if (value->type != VALUE_TYPE_LABEL)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if (labelp)
    *labelp = value->val.label;

  return 0;
}

int value_set_label(value_t value, label_t label)
{
  int r;

  if ((r = _getval_alloc(&value)) < 0)
    return r;

  if (value->flags & VALUE_FLAG_CONST)
    return NLL_ERRCODE_VALUE_CONST_VALUE;

  if (value->flags & VALUE_FLAG_RDONLY)
    return 0;

  if ((value->type != VALUE_TYPE_UNDEFINED) &&
      (value->type != VALUE_TYPE_LABEL)) {
    if (value->flags & VALUE_FLAG_STATIC)
      return NLL_ERRCODE_VALUE_INVALID_TYPE;
#ifdef NLL_TYPECHANGE_ENABLE
    if ((r = value_clear(value)) < 0)
      return r;
#else
    return NLL_ERRCODE_VALUE_INVALID_TYPE;
#endif
  }

  if (label)
    label->refcount++;

  if (value->type == VALUE_TYPE_LABEL) {
    if (value->val.label) {
      if ((r = label_del(value->val.label)) < 0)
	return r;
    }
  }

  value->type = VALUE_TYPE_LABEL;
  value->val.label = label;

  return 0;
}

int value_get_integer_float(value_t value, integer_t *integerp)
{
  int r;
#ifdef NLL_FLOATING_POINT
  double f;
#endif

  if ((r = value_get_integer(value, integerp)) < 0) {
#ifdef NLL_FLOATING_POINT
    if ((r = value_get_float(value, &f)) < 0)
      return r;
    if (integerp)
      *integerp = f;
#else
    return r;
#endif
  }

  return 0;
}

#ifdef NLL_FLOATING_POINT
int value_get_float_integer(value_t value, double *floatingp)
{
  int r;
  integer_t integer;

  if ((r = value_get_float(value, floatingp)) < 0) {
    if ((r = value_get_integer(value, &integer)) < 0)
      return r;
    if (floatingp)
      *floatingp = integer;
  }

  return 0;
}
#endif

static int _puts(FILE *fp, const char *str)
{
  if (fp) {
    nll_wait_output(fp);
    fputs(str, fp);
  } else {
#ifdef USE_FRAMEBUF
    framebuf_puts(str);
#else
    fputs(str, nll_stdout);
#endif
  }
  return 0;
}

static int output_values(FILE *fp, value_t value, const char *separator, int procid);

static int output_value(FILE *fp, value_t value, int procid)
{
  char buf[32];
  int i, n;
  char *p, c;
  value_t v;

  value = value_entity(value);
  if (!value)
    return 0;

  /* For link of the loop, check whether it was already handled. */
  if (value->procid == procid) {
    _puts(fp, "...");
    goto ret;
  }
  value->procid = procid;

  switch (value->type) {
  case VALUE_TYPE_NULL:
    _puts(fp, "NULL");
    break;
  case VALUE_TYPE_VECTOR:
    _puts(fp, "(");
    output_values(fp, value->val.value, ",", procid);
    _puts(fp, ")");
    break;
  case VALUE_TYPE_INTEGER:
    sprintf(buf, "%ld", value->val.integer);
    _puts(fp, buf);
    break;
  case VALUE_TYPE_STRING:
    if (value->val.string) {
      _puts(fp, value->val.string->s);
    } else {
      _puts(fp, "NULLSTR");
    }
    break;
#ifdef NLL_FLOATING_POINT
  case VALUE_TYPE_FLOAT:
    sprintf(buf, "%f", value->val.floating);
    _puts(fp, buf);
    break;
#endif
  case VALUE_TYPE_ARRAY:
    if (value->val.array.a) {
      _puts(fp, "[");
      n = array_get_number(value->val.array.a) - value->val.array.offset;
      for (i = 0; i < n; i++) {
	if (i > 0)
	  _puts(fp, ",");
	if (array_get_value(value->val.array.a, value->val.array.offset + i, &v) < 0)
	  continue;
	output_value(fp, v, procid);
      }
      _puts(fp, "]");
    } else {
      _puts(fp, "NULLARRAY");
    }
    break;
  case VALUE_TYPE_AREA:
    if (value->val.area) {
      n = area_get_size(value->val.area);
      p = area_get_buffer(value->val.area);
      for (i = 0; i < n; i++) {
	c = p[i];
	c = isprint(c) ? c : '.';
	sprintf(buf, "%c", c);
	_puts(fp, buf);
      }
    } else {
      _puts(fp, "NULLMEM");
    }
    break;
  case VALUE_TYPE_POINTER:
    if (value->val.pointer.point) {
      _puts(fp, "&");
      output_value(fp, value->val.pointer.point, procid);
    } else {
      _puts(fp, "NULLPTR");
    }
    break;
  case VALUE_TYPE_FUNCTION:
    if (value->val.function) {
      _puts(fp, value->val.function->name);
    } else {
      _puts(fp, "NULLFUNC");
    }
    break;
  case VALUE_TYPE_LABEL:
    if (value->val.label) {
      _puts(fp, value->val.label->name);
    } else {
      _puts(fp, "NULLLABEL");
    }
    break;
  case VALUE_TYPE_UNDEFINED:
  default:
    break;
  }

ret:
  if (fp)
    fflush(fp);

  return 0;
}

static int output_values(FILE *fp, value_t value, const char *separator, int procid)
{
  int printed = 0;

  if (!separator) separator = "";

  for (; value; value = value->next) {
    if (printed)
      _puts(fp, separator);
    output_value(fp, value, procid);
    printed = 1;
  }

  if (fp)
    fflush(fp);

  return 0;
}

int value_output_value(FILE *fp, value_t value)
{
  return output_value(fp, value, procid_set());
}

int value_output_values(FILE *fp, value_t value, const char *separator)
{
  return output_values(fp, value, separator, procid_set());
}

static int dump(FILE *fp, value_t value, int procid);

static int dump_value(FILE *fp, value_t value, int procid)
{
  nll_wait_output(fp);

  /* For link of the loop, check whether it was already handled. */
  if (value->procid == procid) {
    fprintf(fp, "...");
    goto ret;
  }
  value->procid = procid;

#if 0 /* printed in variable_dump() */
  if (value->flags & VALUE_FLAG_CONST)
    fprintf(fp, "(C)");
  if (value->flags & VALUE_FLAG_RDONLY)
    fprintf(fp, "(R)");
  if (value->flags & VALUE_FLAG_STATIC)
    fprintf(fp, "(S)");
#endif

  switch (value->type) {
  case VALUE_TYPE_UNDEFINED:
    fprintf(fp, "UNDEF");
    break;
  case VALUE_TYPE_NULL:
    fprintf(fp, "NULL");
    break;
  case VALUE_TYPE_VALUE:
    fprintf(fp, "{");
    dump(fp, value->val.value, procid);
    fprintf(fp, "}");
    break;
  case VALUE_TYPE_VECTOR:
    fprintf(fp, "(");
    dump(fp, value->val.value, procid);
    fprintf(fp, ")");
    break;
  case VALUE_TYPE_INTEGER:
    fprintf(fp, "%ld", value->val.integer);
    break;
  case VALUE_TYPE_STRING:
    if (value->val.string) {
      string_dump_string(fp, value->val.string);
    } else {
      fprintf(fp, "NULLSTR");
    }
    break;
#ifdef NLL_FLOATING_POINT
  case VALUE_TYPE_FLOAT:
    fprintf(fp, "%f(FLOAT)", value->val.floating);
    break;
#endif
  case VALUE_TYPE_ARRAY:
    if (value->val.array.a) {
      fprintf(fp, "ARRAY");
      array_dump(fp, value->val.array.a);
    } else {
      fprintf(fp, "NULLARRAY");
    }
    break;
  case VALUE_TYPE_AREA:
    if (value->val.area) {
      fprintf(fp, "AREA");
      area_dump(fp, value->val.area);
    } else {
      fprintf(fp, "NULLMEM");
    }
    break;
  case VALUE_TYPE_POINTER:
    if (value->val.pointer.point) {
      fprintf(fp, "&");
      dump(fp, value->val.pointer.point, procid);
      fprintf(fp, "[%d]", value->val.pointer.offset);
    } else {
      fprintf(fp, "NULLPTR");
    }
    break;
  case VALUE_TYPE_FUNCTION:
    if (value->val.function) {
      fprintf(fp, "%s(FUNC)", value->val.function->name);
    } else {
      fprintf(fp, "NULLFUNC");
    }
    break;
  case VALUE_TYPE_LABEL:
    if (value->val.label) {
      fprintf(fp, "%s(LABEL)", value->val.label->name);
    } else {
      fprintf(fp, "NULLLABEL");
    }
    break;
  default:
    fprintf(fp, "ERROR");
    break;
  }

ret:
  fflush(fp);

  return 0;
}

static int dump(FILE *fp, value_t value, int procid)
{
  value_t v = NULL;

  for (v = value; value; value = value->next) {
    nll_wait_output(fp);
    if (v != value)
      fprintf(fp, ",");
    dump_value(fp, value, procid);
  }

  fflush(fp);

  return 0;
}

int value_dump_value(FILE *fp, value_t value)
{
  return dump_value(fp, value, procid_set());
}

int value_dump(FILE *fp, value_t value)
{
  return dump(fp, value, procid_set());
}
