#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <grass/gis.h>
#include "list.h"
#include "mapcalc.h"
#include "any.h"

/*
 * This is a backdoor for strange types only. We only handle void pointer
 * references and make no kind of typecheck; lot's of errors can be made
 * by the user. But at least it's possible to deal with minor issues
 * without touching the parser.
 * Memory for "any" should be one single block.
 */

typedef ANY *(*a_func)(void);
typedef ANY *(*a_func_a)(void *a0);
typedef ANY *(*a_func_aa)(void *a0, void *a1);

typedef struct Anyfunc
{
  char *name;
  void *func;
  char *proto;
}ANYFUNC;

static ANY *mkstring (void *a);
void setany (SYMBOL *var, SYMBOL *any);
SYMBOL *mkanyvar (SYMBOL *var, SYMBOL *any);
SYMBOL *anyfunc (SYMBOL *func, SYMBOL *arglist);

static ANYFUNC af[] =
{
  { "mkstring", mkstring, "a" },
  { NULL, NULL, NULL }
};

/***************************************************************************
 * Test function only
 */
static ANY *
mkstring (void *a)
{
  ANY *str;

  str = (ANY *)listitem (sizeof (ANY));
  str->type = st_str;
  str->any = (char *)strdup (a);
  str->refcnt = 1;
  printf ("\t%s\n", (char *)a);

  return str;
}

/*
 * End test function only
 ***************************************************************************/

void
init_any (void)
{
  SYMBOL *sym;
  int i;

  for (i = 0; af[i].name; i++)
  {
    sym = putsym (af[i].name);
    sym->type = sym->itype = st_afunc;
    sym->v.p = af[i].func;
    sym->proto = af[i].proto;
    sym->rettype = st_any;
  }
}

void
setany (SYMBOL *var, SYMBOL *any)
{
  SYMBOL *sym;

  if (var->name)
  {
    sym = getsym (var->name);
    if (sym)
    {
      if (--((ANY *)sym->v.p)->refcnt < 1)
	G_free (sym->v.p);
      sym->v.p = any->v.p;
    }
  }

  if (--((ANY *)var->v.p)->refcnt < 1)
    G_free (var->v.p);
  var->v.p = NULL;
  freesym (var);

  /* can't show an anyvar */
  any->v.p = NULL;
  freesym (any);
}

SYMBOL *
mkanyvar (SYMBOL *var, SYMBOL *any)
{
  var->type = var->itype = st_any;
  var->name = var->v.p;
  var->v.p = any->v.p;
  any->v.p = NULL;
  symtab = (SYMBOL *)listadd ((LIST *)symtab, (LIST *)var, cmpsymsym);

  /* can't show any */

  return var;
}

SYMBOL *
anyfunc (SYMBOL *func, SYMBOL *arglist)
{
  SYMBOL *sym;
  ANY *res = NULL;
  int argc = -1;

  if (!func || !func->v.p || func->type != st_afunc)
  {
    parseerror = 1;
    printf ("Can't call bad any-function\n");
  }
  else
    argc = listcnt ((LIST *)arglist);

  if (argc == 0 && (!func->proto || !*func->proto))
    res = (*(a_func)func->v.p)();
  else if (argc == 1 && !strcmp (func->proto, "a"))
    res = (*(a_func_a)func->v.p)(arglist->v.p);
  else if (argc == 2 && !strcmp (func->proto, "aa"))
    res = (*(a_func_aa)func->v.p)(arglist->v.p,
				  arglist->next->v.p);
  else
  {
    printf ("Bad arguments to anyfunc %s (argc = %d)\n", func->name, argc);
    parseerror = 1;
  }

  listdelall ((LIST *)func, freesym);
  listdelall ((LIST *)arglist, freesym);

  sym = (SYMBOL *)listitem (sizeof (SYMBOL));
  sym->type = st_any;
  sym->v.p = res;

  return sym;
}
