/* aze_api.c 2006/03/08
   Copyright (c) SOFNEC Co., Ltd.
 */
#include <stdio.h>   /* for DEBUG */

#include <stdlib.h>
#include <string.h>
#include "aze_api.h"

#define AZE_ATOM_STRING_BUF_SIZE   AZ_MAX_ATOM_LENGTH

//extern int  UnifyInt(TERM* arg, BASEINT n);
extern void ArgError(void); /* utility.c */
//extern int unify(TERM* arg1, TERM* arg2);


extern void
aze_define_pred(char *name, int arity, AZE_PRED c_func)
{
  put_bltn(name, arity, c_func);
}

extern void
aze_raise_error_arg(AZE_ENV Env)
{
  ArgError();
}

extern AZE_TERM
aze_global_cell_new(AZE_ENV Env)
{
  AZE_TERM t;

  MakeUndef(Env);
  t = next_var_cell - 1;
  return t;
}

extern void
aze_global_cell_new_cancel(AZE_ENV Env)
{
  next_var_cell--;
}

extern int
aze_term_value_is_undef(AZE_ENV Env, AZE_TERM t)
{
  return IsUndef(t);
}

extern int
aze_term_value_is_int(AZE_ENV Env, AZE_TERM t)
{
  return IsInt(t);
}

extern int
aze_term_value_is_list(AZE_ENV Env, AZE_TERM t)
{
  return IS_LIST(t);
}

extern AZE_INT
aze_term_value_int(AZE_ENV Env, AZE_TERM t)
{
  return GetInt(t);
}

extern double
aze_term_value_double(AZE_ENV Env, AZE_TERM t)
{
  return GetDouble(t);
}

extern int
aze_unify_int(AZE_ENV Env, AZE_INT i, AZE_TERM t)
{
  int r = UnifyInt(t, i);
  return (r != 0 ? AZE_RETURN_NORMAL : -1);
}

extern int
aze_unify_double(AZE_ENV Env, double d, AZE_TERM t)
{
  int r = UnifyDouble(Env, t, d);
  return (r != 0 ? AZE_RETURN_NORMAL : -1);
}

extern int
aze_unify_atom(AZE_ENV Env, AZE_ATOM a, AZE_TERM t)
{
  int r = UnifyAtom(t, a);
  return (r != 0 ? AZE_RETURN_NORMAL : -1);
}

extern int
aze_unify_term(AZE_ENV Env, AZE_TERM t1, AZE_TERM t2)
{
  int r = Unify(t1, t2);
  return (r != 0 ? AZE_RETURN_NORMAL : -1);
}

extern AZE_ATOM
aze_cstring_to_atom(AZE_ENV Env, const char *s)
{
  if (strlen(s) > AZ_MAX_ATOM_LENGTH)
    return Asciz2Atom(Env, "");

  return Asciz2Atom(Env, (char* )s);  /* == PutAtom() */
}

extern AZE_ATOM
aze_cstring2_to_atom(AZE_ENV Env, const char *s, int len)
{
  AZE_ATOM a;
  char buf[AZE_ATOM_STRING_BUF_SIZE];

  if (len >= AZE_ATOM_STRING_BUF_SIZE - 1)
    return Asciz2Atom(Env, "");

  strncpy(buf, s, len);
  buf[len] = 0;
  a = Asciz2Atom(Env, buf);
  return a;
}

extern int
aze_atom_list_to_cstring_array(AZE_ENV Env, AZE_TERM arg, char *buf[], int max)
{
  TERM	*args;
  char s[AZE_ATOM_STRING_BUF_SIZE];
  int i;

  REALVALUE(arg);

  for (i = 0; i < max; i++)
    buf[i] = (char* )NULL;

  i = 0;
  if (IsAtom(arg)) {
    s[0] = '\0';
    Atom2Asciz(GetAtom(arg), s);
    if (s[0]) {
      buf[i] = malloc(strlen(s) + 1);
      strcpy(buf[i], s);
      i++;
    }
  }
  else if (IsCons(arg)) {
    while (! IsNil(arg)) {
      if (! IsCons(arg))
        GetCons(arg);   /* for error Illeagal argument	*/

      REALVALUE(arg);
      args = BODY(arg);
      REALVALUE(args);
      arg = BODY(arg) + 1;
      if (! IsAtom(args))
        GetAtom(args);	/* for error Illeagal argument	*/

      s[0] = '\0';
      Atom2Asciz(GetAtom(args), s);
      if (s[0]) {
        buf[i] = malloc(strlen(s) + 1);
        if (buf[i] == (char* )NULL) {
          /* error!! */
          return -1;
        }
        strcpy(buf[i], s);
        i++;
        if (i >= max) break;
      }
    }
  }
  else
    GetAtom(arg);	/* for error Illeagal argument	*/

  return i;
}


static TERM *make_list_save_nvc;

static void
make_list_ensure(AZE_ENV Env)
{
  next_var_cell = make_list_save_nvc;
}

extern void
aze_make_list_error_end(AZE_ENV Env)
{
  make_list_ensure(Env);
}

extern AZE_TERM
aze_make_list_init(AZE_ENV Env, TERM *unify_term)
{
  make_list_save_nvc = next_var_cell;
  return unify_term;
}

extern TERM*
aze_make_list_add_item(AZE_ENV Env, TERM *head, AZE_TERM *carp)
{
  TERM *car, *cdr;

  MakeUndef(Env);
  MakeUndef(Env);
  car = next_var_cell - 2;
  cdr = next_var_cell - 1;

  if (! UnifyCons(Env, head, car, cdr)) {
    make_list_ensure(Env);
    return (TERM* )NULL;
  }

  *carp = car;
  return cdr;
}

extern int
aze_make_list_end(AZE_ENV Env, TERM *head)
{
  int r = UnifyAtom(head, ATOM_NIL);
  make_list_ensure(Env);
  return (r != 0 ? AZE_RETURN_NORMAL : -1);
}

extern int
aze_make_prolog_list_from_atom_array(Frame* Env, TERM* t, AZE_ATOM a[], int n)
{
  int rec_size, gvar_size, count = 0;
  TERM *List_top;
  AZE_ATOM dum_atom;

  rec_size = n;
  gvar_size = (BASEINT )(gvar_bottom - next_gvar_cell)/2;	
  if (gvar_size < rec_size) {
    P0_s_greclaim(Env);
    gvar_size = (BASEINT)(gvar_bottom - next_gvar_cell)/2;	
    if (gvar_size < rec_size)
      return -1;
  }

  List_top = next_gvar_cell;
  while (rec_size--) {
    SETTAG(next_gvar_cell, list_tag);
    BODY(next_gvar_cell) = next_gvar_cell+1;
    next_gvar_cell++;
    dum_atom = a[count++];
    PUTATOM(dum_atom, next_gvar_cell);
    next_gvar_cell++;
  }
  SETTAG(next_gvar_cell, atom_tag);
  INT_BODY(next_gvar_cell++) = ATOM_NIL;

  if (unify(List_top, t) == 0)
    return -1;

  return 0;
}

extern AZE_TERM
aze_cstring_to_list(AZE_ENV Env, const char *s)
{
  AZE_TERM t;
  int r;

  if (s == NULL) return AZE_NULL_TERM;

  MakeUndef(Env); t = next_var_cell - 1;
  r = az_cstring_to_list(Env, s, strlen(s), t);
  if (r != 0) {
    return AZE_NULL_TERM;
  }

  return t;
}

extern int
aze_unify_cell_from_c_data(AZE_ENV Env, AZE_C_DATA data, AZE_TERM cell)
{
  int r;

  switch (data->type) {
  case AZE_C_TYPE_INT:
    r = UnifyInt(cell, (BASEINT )data->u.i);
    break;
  case AZE_C_TYPE_ATOM:
    r = UnifyAtom(cell, data->u.a);
    break;
  case AZE_C_TYPE_DOUBLE:
    r = UnifyDouble(Env, cell, data->u.d);
    break;
  case AZE_C_TYPE_STRING_TO_ATOM:
    {
     AZE_ATOM a = aze_cstring_to_atom(Env, data->u.s);
     r = UnifyAtom(cell, (BASEINT )a);
    }
    break;
  case AZE_C_TYPE_STRING_TO_LIST:
    cell = aze_cstring_to_list(Env, data->u.s);
    r = (cell != NULL ? 1 : 0);
    break;

  case AZE_C_TYPE_AZE_TERM:
    r = Unify(cell, data->u.t);
    break;

  default:
    r = 0; /* error */
    break;
  }

  return (r != 0 ? AZE_RETURN_NORMAL : -1);
}

extern AZE_TERM
aze_make_cell_from_c_data(AZE_ENV Env, AZE_C_DATA data)
{
  int r;
  AZE_TERM cell;

  MakeUndef(Env); cell = next_var_cell - 1;
  r = aze_unify_cell_from_c_data(Env, data, cell);
  next_var_cell--;

  return (AZE_TERM )(r == 0 ? cell : NULL);
}


extern int
aze_unify_cell_from_c_data_list(AZE_ENV Env, AZE_C_DATA_TYPE data[], int n,
                                AZE_TERM t)
{
  int i, r;
  AZE_TERM head, car;

  head = aze_make_list_init(Env, t);
  for (i = 0; i < n; i++) {
    head = aze_make_list_add_item(Env, head, &car);
    if (head == NULL) {
    err:
      aze_make_list_error_end(Env);
      next_var_cell--;
      return -1;
    }

    r = aze_unify_cell_from_c_data(Env, &(data[i]), car);
    if (r != 0) goto err;
  }
  aze_make_list_end(Env, head);

  return AZE_RETURN_NORMAL;
}

extern AZE_TERM
aze_make_cell_from_c_data_list(AZE_ENV Env, AZE_C_DATA_TYPE data[], int n)
{
  int r;
  AZE_TERM t;

  MakeUndef(Env); t = next_var_cell - 1;
  r = aze_unify_cell_from_c_data_list(Env, data, n, t);
  
  return (AZE_TERM )(r == 0 ? t : NULL);
}

extern int
aze_list_each_callback(AZE_ENV Env, AZE_TERM list
                       , AZE_CALLBACK_LIST_EACH callback_func, void* arg)
{
  AZE_TERM t;
  int r, i;

  t = list;
  REALVALUE(t);

  if (! IsCons(t)) return -1;

  i = 0;
  while (IS_LIST(t)) {
    t = BODY(t);
    r = callback_func(Env, t, i, arg);
    if (r != AZE_CALLBACK_NORMAL)
      return r;

    t++;
    REALVALUE(t);
    i++;
  }

  return AZE_CALLBACK_NORMAL;
}

extern AZE_TERM
aze_list_at(AZE_ENV Env, AZE_TERM list, int at)
{
  AZE_TERM t;
  int i;

  if (! IsCons(list)) return AZE_NULL_TERM;

  if (at < 0) {
    int len = az_list_len(list);
    at = len + at;
    if (at < 0) return AZE_NULL_TERM;
  }

  t = list;
  i = 0;
  REALVALUE(t);
  while (IS_LIST(t)) {
    t = BODY(t);
    if (i == at) return t;

    t++;
    REALVALUE(t);
    i++;
  }

  return AZE_NULL_TERM;
}

extern int
aze_list_to_term_carray(AZE_ENV Env, AZE_TERM list, AZE_TERM* a)
{
  AZE_TERM t;
  int i;

  if (! IsCons(list)) return -1;

  t = list;
  i = 0;
  REALVALUE(t);
  while (IS_LIST(t)) {
    t = BODY(t);
    a[i] = t;

    t++;
    REALVALUE(t);
    i++;
  }

  return i;
}

// T.Inaba Dummy
extern int
initiate_aze_api()
{
  return 0;
}
