/* @@@HEAD@@@
// Declarations for objects.
//
// The header ordering conventions break down here; we need to make sure data.h
// has finished, not just that the typedefs have been done.
*/

#ifndef _object_h_
#define _object_h_

#include <stdio.h>
#include "cdc_types.h"

struct object {
    list_t *parents;
    list_t *children;

    /* Variables are stored in a table, with index threads starting at the
     * hash table entries.  There is also an index thread for blanks.  This
     * way we can write the hash table to disk easily. */
    struct {
	Var *tab;
	int *hashtab;
	int blanks;
	int size;
    } vars;

    /* Methods are also stored in a table.  Since methods are fairly big, we
     * store a table of pointers to methods so that we don't waste lots of
     * space. */
    struct {
	struct mptr {
	    method_t * m;
	    int next;
	} *tab;
	int *hashtab;
	int blanks;
	int size;
    } methods;

    /* Table for string references in methods. */
    String_entry *strings;
    int num_strings;
    int strings_size;

    /* Table for identifier references in methods. */
    Ident_entry	*idents;
    int num_idents;
    int idents_size;

    /* Information for the cache. */
    Dbref dbref;
    int   refs;
    char  dirty;                 /* Flag: Object has been modified. */
    char  dead;	                 /* Flag: Object has been destroyed. */
    int   ucounter;              /* counter: Object references */

    long search;                 /* Last search to visit object. */

    /* Pointers to next and previous objects in cache chain. */
    object_t * next;
    object_t * prev;
};

/* The object string and identifier tables simplify storage of strings and
 * identifiers for methods.  When we want to free an object without destroying
 * it, we don't need to scan the method code to determine what strings and
 * identifiers to free, and we don't need to do any modification of method
 * code to reflect a new identifier table when we reload the object. */

/* We keep a ref count on object string entries because we have to know when
 * to delete it from the object.  As far as the string is concerned, all these
 * references are really just one reference, since we only duplicate or discard
 * when we're adding or removing a string from an object. */
struct string_entry {
    string_t *str;
    int refs;
};

/* Similar logic for identifier references. */
struct ident_entry {
    Ident id;
    int refs;
};

struct var {
    Ident name;
    Dbref cclass;
    data_t val;
    int next;
};

struct method {
    Ident name;
    object_t *object;
    int num_args;
    Object_ident *argnames;
    Object_ident rest;
    int num_vars;
    Object_ident *varnames;
    int num_opcodes;
    long *opcodes;
    int num_error_lists;
    Error_list *error_lists;

    /* consolidate the following into bit flags */
#ifndef NEW_OVERRIDE
    int overridable;
#else
    int m_state;       /* public, protected, private */
    int m_flags;       /* overridable, synchronized, locked */
#endif
    int refs;
};

/* states: only one at a time */
#define MS_PUBLIC    0x1    /* public */
#define MS_PROTECTED 0x2    /* protected */
#define MS_PRIVATE   0x4    /* private */
#define MS_ROOT      0x8    /* root */
#define MS_DRIVER    0x16   /* sender() and caller() are 0 */

/* perhaps create a method reference to call a task on for perm
   checking, with sender() and caller()? */

/* flags: any number of the following */
#define MF_NONE      0    /* No flags */
#define MF_NOOVER    1    /* not overridable */
#define MF_SYNC      2    /* synchronized */
#define MF_LOCK      4    /* locked */
#define MF_NATIVE    8    /* native */
#define MF_UNDF1     16   /* undefined */
#define MF_UNDF2     32   /* undefined */
#define MF_UNDF3     64   /* undefined */
#define MF_UNDF4     128  /* undefined */

struct error_list {
    int num_errors;
    int *error_ids;
};

/* ..................................................................... */
/* function prototypes */

#ifdef  _object_


/* We use MALLOC_DELTA to keep table sizes to 32 bytes less than a power of
 * two, if pointers and longs are four bytes. */
#define MALLOC_DELTA            8
#define ANCTEMP_STARTING_SIZE   (32 - MALLOC_DELTA)
#define VAR_STARTING_SIZE       (16 - MALLOC_DELTA - 1)
#define METHOD_STARTING_SIZE    (16 - MALLOC_DELTA - 1)
#define STRING_STARTING_SIZE    (16 - MALLOC_DELTA)
#define IDENTS_STARTING_SIZE    (16 - MALLOC_DELTA)
#define METHOD_CACHE_SIZE       503

/* ..................................................................... */
/* types and structures */

/* data_t for method searches. */
typedef struct search_params Search_params;

struct search_params {
    unsigned long name;
    long stop_at;
    int done;
    method_t * last_method_found;
};

struct {
    long stamp;
    Dbref dbref;
    Ident name;
    Dbref after;
    Dbref loc;
} method_cache[METHOD_CACHE_SIZE];

/* ..................................................................... */
/* function prototypes */
static void    object_update_parents(object_t *object,
                                  list_t *(*list_op)(list_t *, data_t *));
static list_t   *object_ancestors_aux(long dbref, list_t *ancestors);
static int     object_has_ancestor_aux(long dbref, long ancestor);
static Var    *object_create_var(object_t *object, long cclass, long name);
static Var    *object_find_var(object_t *object, long cclass, long name);
static method_t * object_find_method_local(object_t *object, long name);
static method_t * method_cache_check(long dbref, long name, long after);
static void    method_cache_set(long dbref, long name, long after, long loc);
static void    search_object(long dbref, Search_params *params);
static void    method_delete_code_refs(method_t * method);
static void    object_text_dump_aux(object_t *obj, FILE *fp);

object_t *object_new(long dbref, list_t *parents);
void    object_free(object_t *object);
void    object_destroy(object_t *object);
void    object_construct_ancprec(object_t *object);
int     object_change_parents(object_t *object, list_t *parents);
list_t   *object_ancestors(long dbref);
int     object_has_ancestor(long dbref, long ancestor);
void    object_reconstruct_descendent_ancprec(long dbref);
int     object_add_string(object_t *object, string_t *string);
void    object_discard_string(object_t *object, int ind);
string_t *object_get_string(object_t *object, int ind);
int     object_add_ident(object_t *object, char *ident);
void    object_discard_ident(object_t *object, int ind);
long    object_get_ident(object_t *object, int ind);
long    object_add_param(object_t *object, long name);
long    object_del_param(object_t *object, long name);
long    object_assign_var(object_t *object, object_t *cclass, long name, data_t *val);
long    object_delete_var(object_t *object, object_t *cclass, long name);
long    object_retrieve_var(object_t *object, object_t *cclass, long name,
                            data_t *ret);
void    object_put_var(object_t *object, long cclass, long name, data_t *val);
method_t * object_find_method(long dbref, long name);
method_t * object_find_next_method(long dbref, long name, long after);
void    object_add_method(object_t *object, long name, method_t *method);
int     object_del_method(object_t *object, long name);
list_t   *object_list_method(object_t *object, long name, int indent, int parens);
void    method_free(method_t *method);
method_t *method_grab(method_t *method);
void    method_discard(method_t *method);
void    object_text_dump(long dbref, FILE *fp);

/* ..................................................................... */
/* global variables */

/* Count for keeping track of of already-searched objects during searches. */
long cur_search;

/* Keeps track of dbref for next object in database. */
long db_top;

/* Validity count for method cache (incrementing this count invalidates all
 * cache entries. */
static int cur_stamp = 1;

#else /* _object_ */

extern object_t *object_new(long dbref, list_t *parents);
extern void    object_free(object_t *object);
extern void    object_destroy(object_t *object);
extern void    object_construct_ancprec(object_t *object);
extern int     object_change_parents(object_t *object, list_t *parents);
extern list_t   *object_ancestors(long dbref);
extern int     object_has_ancestor(long dbref, long ancestor);
extern void    object_reconstruct_descendent_ancprec(long dbref);
extern int     object_add_string(object_t *object, string_t *string);
extern void    object_discard_string(object_t *object, int ind);
extern string_t *object_get_string(object_t *object, int ind);
extern int     object_add_ident(object_t *object, char *ident);
extern void    object_discard_ident(object_t *object, int ind);
extern long    object_get_ident(object_t *object, int ind);
extern long    object_add_param(object_t *object, long name);
extern long    object_del_param(object_t *object, long name);
extern long    object_assign_var(object_t *object, object_t *cclass, long name,
                                 data_t *val);
extern long    object_delete_var(object_t *object, object_t *cclass, long name);
extern long    object_retrieve_var(object_t *object, object_t *cclass, long name,
                                   data_t *ret);
extern void    object_put_var(object_t *object, long cclass, long name,
                              data_t *val);
extern method_t *object_find_method(long dbref, long name);
extern method_t *object_find_next_method(long dbref, long name, long after);
extern void    object_add_method(object_t *object, long name, method_t *method);
extern int     object_del_method(object_t *object, long name);
extern list_t   *object_list_method(object_t *object, long name, int indent,
                                  int parens);
extern void    method_free(method_t *method);
extern method_t *method_grab(method_t *method);
extern void    method_discard(method_t *method);
extern void    object_text_dump(long dbref, FILE *fp);
extern int     object_get_method_flags(object_t * object, long name);
extern int     object_get_method_state(object_t * object, long name);
extern int     object_set_method_flags(object_t * object, long name, int flags);
extern int     object_set_method_state(object_t * object, long name, int state);

/* variables */
extern long db_top;
extern long cur_search;

#endif /* _object_ */

#endif /* _object_h_ */