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

#include <setjmp.h>
#ifdef sun
#include <alloca.h>
#endif
#include "config.h"
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "regexp.h"
#include "mapping.h"
#include "exec.h"

extern jmp_buf error_recovery_context;
extern int error_recovery_context_exists;
extern struct svalue const0;
extern struct svalue *sp;

/*
 * This file contains functions used to manipulate arrays.
 * Some of them are connected to efuns, and some are only used internally
 * by the game driver.
 */
extern int d_flag;

int num_arrays;
int total_array_size;

/*
 * Make an empty vector for everyone to use, never to be deallocated.
 * It is cheaper to reuse it, than to use malloc() and allocate.
 */
struct vector null_vector = {
    0,	/* size */
    1	/* Ref count, which will ensure that it will never be deallocated */
};

/*
 * Allocate an array of size 'n'.
 */
struct vector *
allocate_array(int n)
{
    extern struct svalue const0;
    int i;
    struct vector *p;

    if (n < 0 || n > MAX_ARRAY_SIZE)
	error("Illegal array size.\n");
    if (n == 0) {
        p = &null_vector;
	p->ref++;
	return p;
    }
    num_arrays++;
    total_array_size += sizeof (struct vector) + sizeof (struct svalue) *
	(n-1);
    p = ALLOC_VECTOR(n);
    p->ref = 1;
    p->size = n;
    for (i=0; i<n; i++)
	p->item[i] = const0;
    return p;
}

void 
free_vector(struct vector *p)
{
    int i;
    p->ref--;
    if (p->ref > 0)
	return;
#ifdef DEBUG
    if (p == &null_vector)
    {
	p->ref = 1;
	debug_message("Tried to free the zero-size shared vector.\n");
	return;
    }
#endif
    for (i = 0; i < p->size; i++)
	free_svalue(&p->item[i]);
    num_arrays--;
    total_array_size -= sizeof (struct vector) + sizeof (struct svalue) *
	(p->size-1);
    free((char *)p);
}

struct vector *
explode_string(char *str, char *del)
{
    char *p, *beg;
    int num, len, extra;
    struct vector *ret;
    char *buff, schar[2];

    len = strlen(del);
    /*
     * Take care of the case where the delimiter is an
     * empty string. Then, return an array with only one element,
     * which is the original string.
     */
    if (len == 0)
    {
	len = strlen(str); 
	ret = allocate_array(len);
	for (num = 0; num < len; num++)
	{
	    schar[0] = str[num]; schar[1] = 0;
	    ret->item[num].type = T_STRING;
	    ret->item[num].string_type = STRING_MALLOC;
	    ret->item[num].u.string = string_copy(schar);
	}
	return ret;
    }

    if (!*str) /* Empty string */
      return allocate_array(0);

#ifdef OLD_EXPLODE
    /*
     * Skip leading 'del' strings, if any.
     */
    while(strncmp(str, del, len) == 0) 
    {
      str += len;
      if (str[0] == '\0')
          return allocate_array(0);
    }
#endif
    /*
     * Find number of occurences of the delimiter 'del'.
     */
    for (extra = 1, p = str, num = 0; *p;) 
    {
	if (strncmp(p, del, len) == 0) 
	{
	    num++;
	    p += len;
	    extra = 0;
	} 
	else
	{
	    p += 1;
	    extra = 1;
	}
    }
    /*
     * Compute number of array items. It is either number of delimiters,
     * or, one more.
     */
    if (extra)
	num++;
    buff = xalloc(strlen(str) + 1);
    ret = allocate_array(num);
    for (p = str, beg = str, num = 0; *p; ) 
    {
	if (strncmp(p, del, len) == 0) 
	{
	    strncpy(buff, beg, p - beg);
	    buff[p-beg] = '\0';
	    if (num >= ret->size)
		fatal("Too big index in explode !\n");
	    /* free_svalue(&ret->item[num]); Not needed for new array */
	    ret->item[num].type = T_STRING;
	    ret->item[num].string_type = STRING_MALLOC;
	    ret->item[num].u.string = string_copy(buff);
	    num++;
	    beg = p + len;
	    p = beg;
	} 
	else 
	{
	    p += 1;
	}
    }
    /* Copy last occurence, if there was not a 'del' at the end. */
    if (*beg != '\0') {
	free_svalue(&ret->item[num]);
	ret->item[num].type = T_STRING;
	ret->item[num].string_type = STRING_MALLOC;
	ret->item[num].u.string = string_copy(beg);
    }
    free(buff);
    return ret;
}

char *
implode_string(struct vector *arr, char *del)
{
    int size, i, num;
    char *p, *q;

    for (i=0, size = 0, num = 0; i < arr->size; i++) 
    {
	if (arr->item[i].type == T_STRING)
	{
	    size += strlen(arr->item[i].u.string);
	    num++;
	}
    }
    if (num == 0)
	return string_copy("");
    p = xalloc(size + (num-1) * strlen(del) + 1);
    q = p;
    p[0] = '\0';
    for (i = 0, size = 0, num = 0; i < arr->size; i++) 
    {
	if (arr->item[i].type == T_STRING) 
	{
	    if (num > 0) 
	    {
		strcpy(p, del);
		p += strlen(del);
	    }
	    strcpy(p, arr->item[i].u.string);
	    p += strlen(arr->item[i].u.string);
	    num++;
	}
    }
    return q;
}

struct vector *
users() 
{
    struct object *ob;
    extern int num_player; /* set by comm1.c */
    int i;
    struct vector *ret;
    extern struct svalue *sapply();
    
    if (!num_player)
	return allocate_array(0);
    
    ret = allocate_array(num_player);
    for (i = 0; i < num_player; i++) {
	ret->item[i].type = T_OBJECT;
	add_ref(ret->item[i].u.ob = get_interactive_object(i),"users");
    }
    return ret;
}

/*
 * Check that an assignment to an array item is not cyclic.
 */
static void 
check_for_recursion(struct vector *vec, struct vector *v)
{
    register int i;
    extern int eval_cost;

    eval_cost++;
    if (v == vec)
	error("Recursive asignment of vectors.\n");
    for (i = 0; i < v->size; i++)
    {
	if (v->item[i].type == T_POINTER)
	    check_for_recursion(vec, v->item[i].u.vec);
    }
}

/*
 * Slice of an array.
 */
struct vector *
slice_array(struct vector *p, int from, int to)
{
    struct vector *d;
    int cnt;
    
#if 0
    if (from < 0)
	from = p->size + from;
#endif
    if (from < 0)
	from = 0;
    if (from >= p->size)
	return allocate_array(0); /* Slice starts above array */
#if 0
    if (to < 0)
	to = p->size + to;
#endif
    if (to >= p->size)
	to = p->size - 1;
    if (to < from)
	return allocate_array(0); 
    
    d = allocate_array(to - from + 1);
    for (cnt = from; cnt <= to; cnt++) 
	assign_svalue_no_free (&d->item[cnt - from], &p->item[cnt]);
    
    return d;
}

/* EFUN: filter (array part)
   
   Runs all elements of an array through ob->func()
   and returns an array holding those elements that ob->func
   returned 1 for.
   */
struct vector *
filter_arr(struct vector *p, char *func, struct object *ob, 
	   struct svalue *extra)
{
    struct vector *r;
    struct svalue *v;
    char *flags;
    int cnt,res;
    short funcix;
    
    res = 0;
    r = 0;

    if (p->size<1)
	return allocate_array(0);

    flags = tmpalloc(p->size + 1); 
    for (cnt = 0; cnt < p->size; cnt++) 
    {
	if (ob->flags & O_DESTRUCTED)
	    error("object used by filter() (array) destructed");
	flags[cnt] = 0;
	push_svalue(&p->item[cnt]);
	if (extra) 
	{
	    push_svalue(extra);
	    v = apply(func, ob, 2, ob != current_object);
	} 
	else 
	{
	    v = apply(func, ob, 1, ob != current_object);
	}
	if ((v) && (v->type==T_NUMBER)) 
	{
	    if (v->u.number) 
	    { 
		flags[cnt] = 1; 
		res++; 
	    }
	}
    }
    r = allocate_array(res);
    if (res) 
    {
	for (cnt = res = 0; (res < r->size) && (cnt < p->size); cnt++) 
	{
	    if (flags[cnt]) 
		assign_svalue_no_free (&r->item[res++], &p->item[cnt]);
	}
    }
    tmpfree(flags);
    return r;
}

/* Unique maker
   
   These routines takes an array of objects and calls the function 'func'
   in them. The return values are used to decide which of the objects are
   unique. Then an array on the below form are returned:
   
   ({
   ({Same1:1, Same1:2, Same1:3, .... Same1:N }),
   ({Same2:1, Same2:2, Same2:3, .... Same2:N }),
   ({Same3:1, Same3:2, Same3:3, .... Same3:N }),
   ....
   ....
   ({SameM:1, SameM:2, SameM:3, .... SameM:N }),
   })
   i.e an array of arrays consisting of lists of objectpointers
   to all the nonunique objects for each unique set of objects.
   
   The basic purpose of this routine is to speed up the preparing of the
   array used for describing.
   
   */

struct unique
{
    int count;
    struct svalue *val;
    struct svalue mark;
    struct unique *same;
    struct unique *next;
};

static int 
sameval(struct svalue *arg1, struct svalue *arg2)
{
    if (!arg1 || !arg2) return 0;
    if (arg1->type == T_NUMBER && arg2->type == T_NUMBER) 
    {
	return arg1->u.number == arg2->u.number;
    } 
    else if (arg1->type == T_POINTER && arg2->type == T_POINTER) 
    {
	return arg1->u.vec == arg2->u.vec;
    } 
    else if (arg1->type == T_STRING && arg2->type == T_STRING) 
    {
	return !strcmp(arg1->u.string, arg2->u.string);
    } 
    else if (arg1->type == T_OBJECT && arg2->type == T_OBJECT)
    {
	return arg1->u.ob == arg2->u.ob;
    } 
    else
	return 0;
}


static int 
put_in(struct unique **ulist, struct svalue *marker, struct svalue *elem)
{
    struct unique *llink, *slink, *tlink;
    int cnt,fixed;
    
    llink = *ulist;
    cnt = 0; fixed = 0;
    while (llink) 
    {
	if ((!fixed) && (sameval(marker, &(llink->mark))))
	{
	    for (tlink = llink; tlink->same; tlink = tlink->same) 
		(tlink->count)++;
	    (tlink->count)++;
	    slink = (struct unique *) tmpalloc(sizeof(struct unique));
	    slink->count = 1;
	    assign_svalue_no_free(&slink->mark, marker);
	    slink->val = elem;
	    slink->same = 0;
	    slink->next = 0;
	    tlink->same = slink;
	    fixed = 1; /* We want the size of the list so do not break here */
	}
	llink = llink->next;
	cnt++;
    }
    if (fixed) 
	return cnt;
    llink = (struct unique *) tmpalloc(sizeof(struct unique));
    llink->count = 1;
    assign_svalue_no_free(&llink->mark, marker);
    llink->val = elem;
    llink->same = 0;
    llink->next = *ulist;
    *ulist = llink;
    return cnt + 1;
}


struct vector *
make_unique(struct vector *arr, char *func, struct svalue *skipnum)
{
    struct svalue *v;
    struct vector *res, *ret;
    struct unique *head, *nxt, *nxt2;
    
    int cnt, ant, cnt2;
    
    if (arr->size < 1)
	return allocate_array(0);

    head = 0; 
    ant = 0; 
    arr->ref++;
    for(cnt = 0; cnt < arr->size; cnt++) 
    {
	if (arr->item[cnt].type == T_OBJECT)
	{
	    v = apply(func, arr->item[cnt].u.ob, 0, 1);
	    if ((!v) || (v->type != skipnum->type) || !sameval(v, skipnum)) 
		if (v) 
		    ant = put_in(&head, v, &(arr->item[cnt]));
	}
    }
    arr->ref--;
    ret = allocate_array(ant);
    
    for (cnt = ant - 1; cnt >= 0; cnt--) /* Reverse to compensate put_in */
    {
	ret->item[cnt].type = T_POINTER;
	ret->item[cnt].u.vec = res = allocate_array(head->count);
	nxt2 = head;
	head = head->next;
	cnt2 = 0;
	while (nxt2) 
	{
	    assign_svalue_no_free (&res->item[cnt2++], nxt2->val);
	    free_svalue(&nxt2->mark);
	    nxt = nxt2->same;
	    tmpfree((char *) nxt2);
	    nxt2 = nxt;
	}
	if (!head) 
	    break; /* It shouldn't but, to avoid skydive just in case */
    }
    return ret;
}

/*
 * End of Unique maker
 *************************
 */

/* Concatenation of two arrays into one
 */
struct vector *
add_array(struct vector *p, struct vector *r)
{
    int cnt,res;
    struct vector *d;
    
    d = allocate_array(p->size + r->size);
    res = 0;
    for (cnt = 0; cnt < p->size; cnt++) 
    {
	assign_svalue_no_free (&d->item[res],&p->item[cnt]);
	res++; 
    }
    for (cnt = 0; cnt < r->size; cnt++)
    {
	assign_svalue_no_free (&d->item[res],&r->item[cnt]);
	res++; 
    }
    return d;
}


/* Returns an array of all objects contained in 'ob'
 */
struct vector *
all_inventory(struct object *ob)
{
    struct vector *d;
    struct object *cur;
    int cnt,res;
    
    cnt = 0;
    for (cur = ob->contains; cur; cur = cur->next_inv)
	cnt++;
    
    if (!cnt)
	return allocate_array(0);

    d = allocate_array(cnt);
    cur = ob->contains;
    
    for (res = 0; res < cnt; res++) 
    {
	d->item[res].type = T_OBJECT;
	d->item[res].u.ob = cur;
	add_ref(cur, "all_inventory");
	cur = cur->next_inv;
    }
    return d;
}


/* Runs all elements of an array through ob::func
   and replaces each value in arr by the value returned by ob::func
   */
struct vector *
map_array (struct vector *arr, char *func, struct object *ob,
	   struct svalue *extra)
{
    struct vector *r;
    struct svalue *v;
    int cnt;
    char *cprogn, *srcpos;
    short funcix;
    jmp_buf save_error_recovery_context;
    int save_rec_exists;
    extern struct program *current_prog;

    if (arr->size < 1) 
	return allocate_array(0);

    memcpy((char *) save_error_recovery_context,
	   (char *) error_recovery_context, sizeof error_recovery_context);
    save_rec_exists = error_recovery_context_exists;
    error_recovery_context_exists = 1;

    r = allocate_array(arr->size);
/*    srcpos = get_srccode_position_if_any(); */
    cprogn = current_prog->name;

    for (cnt = 0; cnt < arr->size; cnt++)
    {
	if (setjmp(error_recovery_context)) 
	{
	    free_vector(r);
	    memcpy((char *) error_recovery_context,
		   (char *) save_error_recovery_context,
		   sizeof error_recovery_context);
	    error_recovery_context_exists = save_rec_exists;
	    error("Error in map_array(), (%s) %s::%s (File: %s)\n", 
		      ob->name, cprogn, func, "...deleted...");
/*	    
            error("Error in map_array(), (%s) %s::%s (File: %s)\n", 
		      ob->name, cprogn, func, srcpos);
*/
	}
	else
	{
	    if (ob->flags & O_DESTRUCTED)
		error("object used by map_array destructed"); /* amylaar */

	    push_svalue(&arr->item[cnt]);
	    if (extra)
	    {
		push_svalue (extra);
		v = apply(func, ob, 2, ob != current_object);
	    }
	    else 
	    {
		v = apply(func, ob, 1, ob != current_object);
	    }
	}
	if (v)
	    assign_svalue_no_free (&r->item[cnt], v);
    }
    memcpy((char *) error_recovery_context,
	   (char *) save_error_recovery_context,
	   sizeof error_recovery_context);
    error_recovery_context_exists = save_rec_exists;
    return r;
}

/*
 * deep_inventory()
 *
 * This function returns the recursive inventory of an object. The returned 
 * array of objects is flat, ie there is no structure reflecting the 
 * internal containment relations.
 *
 */
struct vector *
deep_inventory(struct object *ob, int take_top)
{
    struct vector	*dinv, *ainv, *sinv, *tinv;
    int			il;

    ainv = all_inventory(ob);
    if (take_top)
    {
	sinv = allocate_array(1);
	sinv->item[0].type = T_OBJECT;
	add_ref(ob,"deep_inventory");
	sinv->item[0].u.ob = ob;
	dinv = add_array(sinv, ainv);
	free_vector(sinv);
	free_vector(ainv);
	ainv = dinv;
    }
    sinv = ainv;
    for (il = take_top ? 1 : 0 ; il < ainv->size ; il++)
    {
	if (ainv->item[il].u.ob->contains)
	{
	    dinv = deep_inventory(ainv->item[il].u.ob,0);
	    tinv = add_array(sinv, dinv);
	    if (sinv != ainv)
		free_vector(sinv);
	    sinv = tinv;
	    free_vector(dinv);
	}
    }
    if (ainv != sinv)
	free_vector(ainv);
    return sinv;
}

struct vector *
match_regexp(struct vector *v, char *pattern)
{
    struct regexp *reg;
    char *res;
    int i, num_match;
    struct vector *ret;
    extern int eval_cost;

    if (v->size == 0)
	return allocate_array(0);
    reg = regcomp(pattern, 0);
    if (reg == 0)
	return 0;
    res = (char *)alloca(v->size);
    for (num_match=i=0; i < v->size; i++)
    {
	res[i] = 0;
	if (v->item[i].type != T_STRING)
	    continue;
	eval_cost++;
	if (regexec(reg, v->item[i].u.string) == 0)
	    continue;
	res[i] = 1;
	num_match++;
    }
    ret = allocate_array(num_match);
    for (num_match=i=0; i < v->size; i++)
    {
	if (res[i] == 0)
	    continue;
	assign_svalue_no_free(&ret->item[num_match], &v->item[i]);
	num_match++;
    }
    free((char *)reg);
    return ret;
}

/* 
    An attempt at rewrite using mappings
*/
#define SETARR_SUBTRACT 1
#define SETARR_INTERSECT 2

struct vector *
set_manipulate_array(struct vector *arr1, struct vector *arr2, int op)
{
    struct mapping *m;
    struct vector *r;
    struct svalue tmp, *v;
    char *flags;
    int cnt, res;
    short funcix;
    
    if (arr1->size < 1 || arr2->size < 1)
    {
	switch (op)
	{
	case SETARR_SUBTRACT:
	    arr1->ref++;
	    return arr1;

        case SETARR_INTERSECT:
	    return allocate_array(0);
	}
    }

    m = make_mapping(arr2, 0);
    tmp.type = T_NUMBER;
    tmp.u.number = 1;
    assign_svalue_no_free(get_map_lvalue(m, &arr2->item[0], 1), &tmp);

    res = 0;

    flags = tmpalloc(arr1->size + 1); 
    for (cnt = 0; cnt < arr1->size; cnt++) 
    {
	flags[cnt] = 0;
	v = get_map_lvalue(m, &(arr1->item[cnt]), 0);
	if (op == SETARR_INTERSECT && v->u.number != 0)
	{
	    flags[cnt] = 1; 
	    res++; 
	}
	else if (op == SETARR_SUBTRACT && v->u.number == 0)
	{
	    flags[cnt] = 1; 
	    res++; 
	}
    }
    r = allocate_array(res);
    if (res) 
    {
	for (cnt = res = 0; (res < r->size) && (cnt < arr1->size); cnt++) 
	{
	    if (flags[cnt]) 
		assign_svalue_no_free (&r->item[res++], &arr1->item[cnt]);
	}
    }
    tmpfree(flags);
    free_mapping(m);
    return r;
}

INLINE struct vector *
intersect_array(struct vector *arr1, struct vector *arr2)
{
    return set_manipulate_array(arr1, arr2, SETARR_INTERSECT);
}

INLINE struct vector *
subtract_array(struct vector *arr1, struct vector *arr2)
{
    return set_manipulate_array(arr1, arr2, SETARR_SUBTRACT);
}