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

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

static void unpack_methods (Object * o, FILE * f);
static Verbdef *unpack_verbs (FILE * f);
static void unpack_gvars (Object * o, FILE * f);
static Vardef *unpack_vars (FILE * f);
static void unpack_symbols (Object * o, FILE * f);
static Lock *unpack_locks (FILE * f);
static Var unpack_var (FILE * f);
static String *unpack_string (FILE * f);
static List *unpack_list (FILE * f);
static Map *unpack_map (FILE * f);

#define UNPACK(N, f)    fread( (void *) &(N), sizeof(N), 1, f)

Object *unpack_object (FILE * f)
{
  Object *o = new_object ();

  UNPACK (o->id.id, f);
  UNPACK (o->id.server, f);
  o->parents = unpack_list (f);
  unpack_symbols (o, f);
  unpack_methods (o, f);
  o->verbs = unpack_verbs (f);
  unpack_gvars (o, f);
  o->locks = unpack_locks (f);
  return o;
}

static void unpack_methods (Object * o, FILE * f)
{
  int i, nmethods;
  Method *m;
  unsigned char flag;

  UNPACK (nmethods, f);
  o->methods = hash_new (nmethods / HASH_INIT_LOAD);
  for (i = 0; i < nmethods; i++) {
    m = MALLOC (Method, 1);
    UNPACK (m->name, f);
    UNPACK (flag, f);
    m->blocked = flag;
    UNPACK (m->ninst, f);
    fread ((void *) m->ehandler, sizeof (m->ehandler[0]), NERRS, f);
    m->vars = unpack_vars (f);
    if (m->ninst > 0) {
      m->code = MALLOC (Inst, m->ninst);
      fread ((void *) m->code, sizeof (Inst), m->ninst, f);
    }                           /* if */
    m->ref = 1;
    m->next = 0;
    add_method (o, m);
  }                             /* for */
}

static Verbdef *unpack_verbs (FILE * f)
{
  int nverbs;
  Verbdef *vb, *prev = 0, *head = 0;

  UNPACK (nverbs, f);
  while (nverbs--) {
    vb = MALLOC (Verbdef, 1);
    UNPACK (vb->verb, f);
    UNPACK (vb->prep, f);
    UNPACK (vb->method, f);
    vb->next = 0;
    if (prev) {
      prev->next = vb;
    } else {
      head = vb;
    }
    prev = vb;
  }
  return head;
}

static void unpack_gvars (Object * o, FILE * f)
{
  int nvars, name;

  UNPACK (nvars, f);
  o->vars = hash_new (nvars / HASH_INIT_LOAD);
  while (nvars--) {
    UNPACK (name, f);
    var_add_global (o, name, unpack_var (f));
  }
}

static Vardef *unpack_vars (FILE * f)
{
  int nvars = 0;
  Vardef *v, *prev = 0, *head = 0;

  UNPACK (nvars, f);
  while (nvars--) {
    v = MALLOC (Vardef, 1);
    UNPACK (v->name, f);
    v->value = unpack_var (f);
    v->next = 0;
    if (prev) {
      prev->next = v;
    } else {
      head = v;
    }
    prev = v;
  }
  return head;
}

static Var unpack_var (FILE * f)
{
  Var v;

  v.type = (Type_spec) fgetc (f);
  switch (v.type) {
  case STR:
    v.v.str = unpack_string (f);
    break;
  case NUM:
    UNPACK (v.v.num, f);
    break;
  case OBJ:
    UNPACK (v.v.obj.id, f);
    UNPACK (v.v.obj.server, f);
    break;
  case LIST:
    v.v.list = unpack_list (f);
    break;
  case MAP:
    v.v.map = unpack_map (f);
    break;
  case ERR:
    UNPACK (v.v.err, f);
    break;
  case PC:                     /* should never happen */
    break;
  }
  return v;
}

static String *unpack_string (FILE * f)
{
  String *s;
  int len;

  UNPACK (len, f);
  s = string_new (len + 1);
  fread (s->str, sizeof (char), len, f);
  s->str[len] = '\0';
  s->len = len;
  return s;
}

static List *unpack_list (FILE * f)
{
  int i;
  List *list = MALLOC (List, 1);

  UNPACK (list->len, f);
  list->mem = list->len;
  if (list->len > 0) {
    list->el = MALLOC (Var, list->len);
  }
  for (i = 0; i < list->len; i++) {
    list->el[i] = unpack_var (f);
  }
  list->ref = 1;
  return list;
}

static Map *unpack_map (FILE * f)
{
  int i, nels;
  Map *map;
  Var from, to;

  UNPACK (nels, f);
  map = map_new (nels);
  for (i = 0; i < nels; i++) {
    from = unpack_var (f);
    to = unpack_var (f);
    map = map_add (map, from, to);
  }
  return map;
}

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

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

static Lock *unpack_locks (FILE * f)
{
  Lock *l, *prev = 0, *head = 0;
  int nlocks = 0;

  UNPACK (nlocks, f);
  while (nlocks--) {
    l = MALLOC (Lock, 1);
    l->name = unpack_string (f);
    UNPACK (l->added_by, f);
    l->next = 0;
    if (prev) {
      prev->next = l;
    } else {
      head = l;
    }
    prev = l;
  }
  return head;
}