/* Copyright (c) 1993 Stephen F. White */

#include <stdio.h>
#ifdef SYSV
#include <string.h>
#else
#include <strings.h>
#endif

#include "config.h"
#include "cool.h"
#include "proto.h"
#include "sys_proto.h"
#include "servers.h"

#ifndef INLINE
void
var_free(Var  v)
{
    switch (v.type) {
      case NUM:
      case OBJ:
      case ERR:
      case PC:
	break;
      case LIST:
	list_free(v.v.list);
	break;
      case STR:
	string_free(v.v.str);
	break;
    }
}

Var
var_dup(Var v)
{
    switch (v.type) {
      case NUM:
      case OBJ:
      case ERR:
      case PC:
	break;
      case LIST:
	v.v.list = list_dup(v.v.list);
	break;
      case STR:
	v.v.str = string_dup(v.v.str);
	break;
    }    
    return v;
}

#endif

int
var_compare(Var  v1, Var  v2)
{
    switch(v1.type) {
      case NUM:
	return v1.v.num - v2.v.num;
      case STR:
	return cool_strcasecmp(v1.v.str->str, v2.v.str->str);
      case OBJ:  case ERR:  case LIST:  case PC:
	return -1;	/* cannot compare objid, error or list values */
    }
    return -1;		/* should never reach */
}

int
var_eq(Var  v1,  Var v2)
{
    int		i;

    if(v1.type != v2.type) {
	return 0;	/* values of different types are unequal */
    }
    switch(v1.type) {
      case NUM:  case PC:
	return v1.v.num == v2.v.num;
      case OBJ:
	return v1.v.obj.id == v2.v.obj.id && v1.v.obj.server == v2.v.obj.server;
      case ERR:
	return v1.v.err == v2.v.err;
      case STR:
	return !cool_strcasecmp(v1.v.str->str, v2.v.str->str);
      case LIST:
	if (v1.v.list->len != v2.v.list->len) {
	    return 0;
	}
	for (i = 0; i < v1.v.list->len; i++) {
/*
 * recursively compare all elements of list
 */
	    if (!var_eq(v1.v.list->el[i], v2.v.list->el[i])) {
		return 0;
	    }
	}
	return 1;
    }
    return 0;
}

Var
var_init(int type)
{
    Var		v;

    switch(v.type = type) {
      case STR:
	v.v.str = string_new(1);
	break;
      case NUM:  case PC:
	v.v.num = 0;
	break;
      case OBJ:
	v.v.obj.id = -1;
	v.v.obj.server = 0;
	break;
      case LIST:
	v.v.list = list_dup(empty_list);
	break;
      case ERR:
	v.v.err = E_NONE;
	break;
    }
    return v;
}
    
int
var_add_local(Method *m, int name, int *varno)
{
    Vardef 	*prev = 0, *v;

    for (v = m->vars, *varno = 0; v; v = v->next, (*varno)++) {
	prev = v;
	if (v->name == name) {
	    return 1;
	}
    }
    v = MALLOC(Vardef, 1);
    v->name = name;
    v->value.type = NUM;
    v->value.v.num = 0;
    v->next = 0;
    if (prev) {
	prev->next = v;
    } else {
	m->vars = v;
    }
    return 0;
}

int
var_add_global(Object *o, int name, Var init_value)
{
    Vardef	*v;
    int		 hval, dummy;

    if (!o->vars) {
	o->vars = hash_new(HASH_INIT_SIZE);
    }
    hval = hash(sym_get(o, name)->str) % o->vars->size;
    if (var_find(o->vars->table[hval], name, &dummy)) {
	return 1;
    } else {
	v = MALLOC(Vardef, 1);
	v->name = name;
	v->value = init_value;
	v->next = o->vars->table[hval];
	o->vars->table[hval] = v;
	o->vars->num++;
	return 0;
    }
}

Error
var_rm_global(Object *o, const char *name)
{
    Vardef	*v, *prev = 0;
    int		 hval;

    if (!o->vars) { return E_VARNF; }
    hval = hash(name) % o->vars->size;
    for (v = o->vars->table[hval]; v; prev = v, v = v->next) {
	if (!cool_strcasecmp(sym_get(o, v->name)->str, name)) {
	    if (prev) {
		prev->next = v->next;
	    } else {
		o->vars->table[hval] = v->next;
	    }
	    o->vars->num--;
	    sym_free(o, v->name);
	    var_free(v->value);
	    FREE(v);
	    return E_NONE;
	}
    }
    return E_VARNF;
}

Vardef *
var_find(Vardef *top, int name, int *varno)
{
    Vardef	*v;

    for (v = top, *varno = 0; v; v = v->next, (*varno)++) {
	if (v->name == name) {
	    return v;
	}
    }
    return 0;
}

Vardef *
var_find_local_by_name(Object *o, Method *m, const char *name, int namelen,
		 int *varno)
{
    Vardef	*v;
    const char	*vname;

    for (v = m->vars, *varno = 0; v; v = v->next, (*varno)++) {
	vname = sym_get(o, v->name)->str;
	if (strlen(vname) == namelen
	 && !cool_strncasecmp(vname, name, namelen)) {
	    return v;
	}
    }
    return 0;
}

Vardef *
var_find_local(Method *m, int varno)
{
    Vardef	*v;

    for (v = m->vars; v && varno; v = v->next, varno--)
	;
    return v;
}

void
var_assign_local(Var  *vars, int  varno, Var  value)
{
    var_free(vars[varno]);
    vars[varno] = value;
}

Error
var_assign_global(Object  *o, String *name, Var  value)
{
    Vardef	*v;
    Var		 oldvalue;
    int		 hval;

    if (var_get_global(o, name->str, &oldvalue) == E_NONE) {
	if (value.type != oldvalue.type) {
	    var_free(value);
	    return E_TYPE;
	}
    }

    if (o->vars) {
	hval = hash(name->str) % o->vars->size;
	for (v = o->vars->table[hval]; v; v = v->next) {
	    if (!cool_strcasecmp(sym_get(o, v->name)->str, name->str)) {
		var_free(v->value);
		v->value = value;
		cache_put(o, o->id.id);
		return E_NONE;
	    }
	}
    }
    name->ref++;
    var_add_global(o, sym_add(o, name), value);
    cache_put(o, o->id.id);
    return E_NONE;
}

void
var_get_local(Var  *vars, int  varno, Var *r)
{
    *r = vars[varno];
}

Error
var_get_global(Object *o, const char *name, Var *r)
{
    Vardef	*v;
    Object	*p;
    int		 i, hval;

    if (o->vars) {
	hval = hash(name) % o->vars->size;
	for (v = o->vars->table[hval]; v; v = v->next) {
	    if (!cool_strcasecmp(name, sym_get(o, v->name)->str)) {
		*r = v->value;
		return E_NONE;
	    }
	}
    }

/*
 * couldn't find locally, search on parents, recursively
 */
    for (i = 0; i < o->parents->len; i++) {
	if ((p = retrieve(o->parents->el[i].v.obj))) {
	    if (var_get_global(p, name, r) == E_NONE) {
		return E_NONE;			/* found it */
	    }
	}
    }
/*
 * no match
 */
    return E_VARNF;
}

String *
var_tostring(String *st, Var  v, int quotes)
{
    switch (v.type) {
      case NUM:  case PC:
	st = string_catnum(st, v.v.num);
	break;
      case OBJ:
	st = string_catobj(st, v.v.obj, v.v.obj.server);
	break;
      case STR:
        if (quotes) {
	    st = string_catc(st, '"');
	}
	st = string_backslash(st, v.v.str->str);
        if (quotes) {
	    st = string_catc(st, '"');
	}
	break;
      case LIST:
	st = list_tostring(st, v.v.list);
	break;
      case ERR:
	st = string_cat(st, err_id2desc(v.v.err));
	break;
    }
    return st;
}

String *
list_tostring(String *st, List *l)
{
    int		i;

    st = string_catc(st, '{');
    for (i = 0; i < l->len; i++) {
	st = var_tostring(st, l->el[i], 1);
	if (i < l->len - 1) {
	    st = string_cat(st, ", ");
	}
    }
    st = string_catc(st, '}');
    return st;
}

int
var_count_local(Method *m)
{
    int		c;
    Vardef     *v;

    for (v = m->vars, c = 0; v; v = v->next, c++)
	;
    return c;
}

void
var_init_local(Object *on, Method *m, Var *vars)
{
    int		 i;
    Vardef	*v;

    for (v = m->vars, i = 0; v; v = v->next, i++) {
	vars[i] = var_dup(v->value);
    }
}

void
var_copy_vars(Object *source, Object *dest)
{
    Vardef	*v;
    String	*varname;
    int		 hval;

    if (!source->vars) { return; }
    for (hval = 0; hval < source->vars->size; hval++) {
	for (v = source->vars->table[hval]; v; v = v->next) {
	    varname = sym_get(source, v->name);
	    (void) var_assign_global(dest, varname, var_dup(v->value));
	}
    }
}