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

#include <stdio.h>

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

#define PACK(N, f)	fwrite( (char *) &(N), sizeof(N), 1, f)

static void	pack_methods(HashT *methods, FILE *f);
static void	pack_verbs(Verbdef *vbhead, FILE *f);
static void	pack_gvars(HashT *vars, FILE *f);
static void	pack_vars(Vardef *vhead, FILE *f);
static void	pack_symbols(Object *o, FILE *f);
static void	pack_locks(Lock *lhead, FILE *f);
static void	pack_var(Var v, FILE *f);
static void	pack_string(String *s, FILE *f);
static void	pack_list(List *list, FILE *f);

int
pack_object(Object *o, FILE *f)
{
    PACK(o->id.id, f);
    PACK(o->id.server, f);
    pack_list(o->parents, f);
    pack_symbols(o, f);
    pack_methods(o->methods, f);
    pack_verbs(o->verbs, f);
    pack_gvars(o->vars, f);
    pack_locks(o->locks, f);
    return 0;
}

static void
pack_methods(HashT *methods, FILE *f)
{
    int		 intzero = 0;
    int		 hval;
    Method	*m;
    unsigned char		 flag;
    
    if (methods) {
	PACK(methods->num, f);
	for (hval = 0; hval < methods->size; hval++) {
	    for (m = methods->table[hval]; m; m = m->next) {
		PACK(m->name, f);
		flag = m->blocked;
		PACK(flag, f);
		PACK(m->ninst, f);
		fwrite((char *) m->ehandler, sizeof(m->ehandler[0]), NERRS, f);
		pack_vars(m->vars, f);
		fwrite((char *) m->code, sizeof(Inst), m->ninst, f);
	    }
	}
    } else {
	PACK(intzero, f);
    }
}

static void
pack_verbs(Verbdef *vbhead, FILE *f)
{
    int		nverbs = 0;
    Verbdef	*vb;

    for (vb = vbhead; vb; vb = vb->next) {
	nverbs++;
    }
    PACK(nverbs, f);
    for (vb = vbhead; vb; vb = vb->next) {
	PACK(vb->verb, f);
	PACK(vb->prep, f);
	PACK(vb->method, f);
    }
}

static void
pack_gvars(HashT *vars, FILE *f)
{
    int		hval, intzero = 0;
    Vardef     *v;

    if (vars) {
	PACK(vars->num, f);
	for (hval = 0; hval < vars->size; hval++) {
	    for (v = vars->table[hval]; v; v = v->next) {
		PACK(v->name, f);
		pack_var(v->value, f);
	    }
	}
    } else {
	PACK(intzero, f);
    }
}

static void
pack_vars(Vardef *vhead, FILE *f)
{
    int		 nvars = 0;
    Vardef	*v;

    for (v = vhead; v; v = v->next) {
	nvars++;
    }
    PACK(nvars, f);
    for (v = vhead; v; v = v->next) {
	PACK(v->name, f);
	pack_var(v->value, f);
    }
}

static void
pack_var(Var v, FILE *f)
{
    fputc((char) v.type, f);
    switch (v.type) {
      case STR:
	pack_string(v.v.str, f);
	break;
      case NUM:
	PACK(v.v.num, f);
	break;
      case OBJ:
	PACK(v.v.obj.id, f);
	PACK(v.v.obj.server, f);
	break;
      case LIST:
	pack_list(v.v.list, f);
	break;
      case ERR:
	PACK(v.v.err, f);
	break;
      case PC:				/* should never happen */
	break;
    }
}

static void
pack_string(String *s, FILE *f)
{
    PACK(s->len, f);
    fwrite(s->str, sizeof(char), s->len, f);
}

static void
pack_list(List *list, FILE *f)
{
    int		i;

    PACK(list->len, f);
    for (i = 0; i < list->len; i++) {
	pack_var(list->el[i], f);
    }
}

static void
pack_symbols(Object *o, FILE *f)
{
    int		i;

    PACK(o->nsymb, f);
    PACK(o->st_size, f);
    for (i = 0; i < o->nsymb; i++) {
	PACK(o->symbols[i].ref, f);
	if (o->symbols[i].ref) {
	    pack_string(o->symbols[i].s, f);
	}
    }
}

static void
pack_locks(Lock *lhead, FILE *f)
{
    Lock	*l;
    int		 nlocks = 0;

    for (l = lhead; l; l = l->next) {
	nlocks++;
    }
    PACK(nlocks, f);
    for (l = lhead; l; l = l->next) {
	pack_string(l->name, f);
	PACK(l->added_by, f);
    }
}