/* extract.c */

#include "copyright.h"

#include <stdio.h>
#include <sys/param.h>
#include "config.h"
#include "db.h"

/* The following routines are ONLY here to allow linking with a few needed
 * files.  It is okay that they do nothing.
 */
/*****************************************************************************/
dbref free_get()
{ return NOTHING; }

void fix_free_list()
{ }

void report()
{ }

void parse_que(player, command, cause)
    dbref player, cause;
    char *command;
{ }

int eval_boolexp(player, b, privs, nrecurs, locktype)
     dbref player;
     struct boolexp *b;
     dbref privs;
     int nrecurs;
     int locktype;
{ return 1; }

int wild_match(s, d)
    char *s, *d;
{ return 1; }

int controls(a, b)
    dbref a,b;
{ return 1; }

void notify(player, msg)
    dbref player;
    const char *msg;
{ }

void init_match(player, arg, type)
    dbref player;
    const char *arg;
    int type;
{ }

void match_everything()
{ }

dbref noisy_match_result()
{ return 1; }

char *tprintf(format, a, b, c, d, e, f)
    char *format;
    int a, b, c, d, e, f;
{ return (char *)""; }

void panic(message)
const char *message;
{
  fprintf(stderr, "PANIC: %s\n", message);
  exit(-1);
}
/*****************************************************************************/

static int include_all = 0;	/* include everything unless specified */

static dbref included[NCARGS + 1];
static dbref excluded[NCARGS + 1];

static dbref *trans;		/* translation vector */
#define DEFAULT_LOCATION (0)
#define DEFAULT_OWNER (1)

static int isok();
/* returns 1 if it is not excluded */
static int not_excluded(x)
    dbref x;
{
  int i;
  /* check that it isn't excluded */
  for (i = 0; excluded[i] != NOTHING; i++) {
    if (excluded[i] == x || excluded[i] == db[x].owner)
      /* exclude specifics, but also exclude something that is owned by
       * a direct exclusion to prevent conflicts.
       */
      return 0;
  }

  /* if it's an exit, check that its destination is ok */
  if (Typeof(x) == TYPE_EXIT && db[x].location != x) {
    return isok(db[x].location);
  } else {
    return 1;
  }
}
/* returns 1 if it should be included in translation vector */
static int isok(x)
    dbref x;
{
  int i;
  if (x == DEFAULT_OWNER || x == DEFAULT_LOCATION)
    return 1;

  for (i = 0; included[i] != NOTHING; i++) {
    if (included[i] == x)
      return 1;			/* always get specific ones */
    if (included[i] == db[x].owner) {
      return not_excluded(x);
    }
  }

  /* not in the list, can only get it if include_all is on */
  /* or it's owned by DEFAULT_OWNER */
  return (include_all && not_excluded(x));
}

static void build_trans()
{
  dbref i;
  dbref val;
  if ((trans = (dbref *) malloc(sizeof(dbref) * db_top)) == 0) {
    abort();
  }
  val = 0;
  for (i = 0; i < db_top; i++) {
    if (isok(i)) {
      trans[i] = val++;
    } else {
      trans[i] = NOTHING;
    }
  }
}

static dbref translate(x)
    dbref x;
{
  if (x == NOTHING || x == HOME) {
    return (x);
  } else {
    return (trans[x]);
  }
}
/* TRUE_BOOLEXP means throw this argument out */
/* even on OR; it's effectively a null boolexp */
/* NOTE: this doesn't free anything, it just munges it up */
static struct boolexp *translate_boolexp(exp)
    struct boolexp *exp;
{
  struct boolexp *s1;
  struct boolexp *s2;
  if (exp == TRUE_BOOLEXP) {
    return TRUE_BOOLEXP;
  } else {
    switch (exp->type) {
      case BOOLEXP_NOT:
      case BOOLEXP_IND:
      case BOOLEXP_IS:
      case BOOLEXP_CARRY:
	s1 = translate_boolexp(exp->sub1);
	if (s1 == TRUE_BOOLEXP) {
	  return TRUE_BOOLEXP;
	} else {
	  exp->sub1 = s1;
	  return exp;
	}
	/* break; */
      case BOOLEXP_AND:
      case BOOLEXP_OR:
	s1 = translate_boolexp(exp->sub1);
	s2 = translate_boolexp(exp->sub2);
	if (s1 == TRUE_BOOLEXP && s2 == TRUE_BOOLEXP) {
	  /* nothing left */
	  return TRUE_BOOLEXP;
	} else if (s1 == TRUE_BOOLEXP && s2 != TRUE_BOOLEXP) {
	  /* s2 is all that is left */
	  return s2;
	} else if (s1 != TRUE_BOOLEXP && s2 == TRUE_BOOLEXP) {
	  /* s1 is all that is left */
	  return s1;
	} else {
	  exp->sub1 = s1;
	  exp->sub2 = s2;
	  return exp;
	}
	/* break; */
      case BOOLEXP_ATR:
	return exp;
      case BOOLEXP_CONST:
	exp->thing = translate(exp->thing);
	if (exp->thing == NOTHING) {
	  return TRUE_BOOLEXP;
	} else {
	  return exp;
	}
	/* break; */
      default:
	abort();		/* bad boolexp type, we lose */
	return TRUE_BOOLEXP;
    }
  }
}

static int ok(x)
    dbref x;
{
  if (x == NOTHING || x == HOME) {
    return 1;
  } else {
    return trans[x] != NOTHING;
  }
}

static void check_bad_exits(x)
    dbref x;
{
  dbref e;
  if (Typeof(x) == TYPE_ROOM && !isok(x)) {
    /* mark all exits as excluded */
    DOLIST(e, db[x].exits) {
      trans[e] = NOTHING;
    }
  }
}

static void check_owner(x)
    dbref x;
{
  if (ok(x) && !ok(db[x].owner)) {
    db[x].owner = DEFAULT_OWNER;
  }
}

static void check_location(x)
    dbref x;
{
  dbref loc;
  dbref newloc;
  if (ok(x) && (Typeof(x) == TYPE_THING || Typeof(x) == TYPE_PLAYER)
      && !ok(loc = db[x].location)) {
    /* move it to home or DEFAULT_LOCATION */
    if (ok(db[x].exits)) {
      newloc = db[x].exits;	/* home */
    } else {
      newloc = DEFAULT_LOCATION;
    }
    db[loc].contents = remove_first(db[loc].contents, x);
    PUSH(x, db[newloc].contents);
    db[x].location = newloc;
  }
}

static void check_next(x)
    dbref x;
{
  dbref next;
  if (ok(x)) {
    while (!ok(next = db[x].next))
      db[x].next = db[next].next;
  }
}

static void check_contents(x)
    dbref x;
{
  dbref c;
  if (ok(x)) {
    while (!ok(c = db[x].contents))
      db[x].contents = db[c].next;
  }
}
/* also updates home */
/* MUST BE CALLED AFTER check_owner! */
static void check_exits(x)
    dbref x;
{
  dbref e;
  if (ok(x) && !ok(e = db[x].exits)) {
    switch (Typeof(x)) {
      case TYPE_ROOM:
	while (!ok(e = db[x].exits))
	  db[x].exits = db[e].next;
	break;
      case TYPE_PLAYER:
      case TYPE_THING:
	if (ok(db[db[x].owner].exits)) {
	  /* set it to owner's home */
	  db[x].exits = db[db[x].owner].exits;	/* home */
	} else {
	  /* set it to DEFAULT_LOCATION */
	  db[x].exits = DEFAULT_LOCATION;	/* home */
	}
	break;
    }
  }
}

static void do_write()
{
  dbref i;
  dbref kludge;
  /* this is braindamaged */
  /* we have to rebuild the translation map */
  /* because part of it may have gotten nuked in check_bad_exits */
  for (i = 0, kludge = 0; i < db_top; i++) {
    if (trans[i] != NOTHING)
      trans[i] = kludge++;
  }

  /* more of a kludge. newer db's need an object count at top */
  kludge = 0;
  for (i = 0; i < db_top; i++) {
    if(ok(i)) kludge++;
  }
  printf("~%d\n", kludge);
  for (i = 0; i < db_top; i++) {
    if (ok(i)) {
      /* translate all object pointers */
      db[i].location = translate(db[i].location);
      db[i].contents = translate(db[i].contents);
      db[i].exits = translate(db[i].exits);
      db[i].next = translate(db[i].next);
      db[i].key = translate_boolexp(db[i].key);
      db[i].owner = translate(db[i].owner);

      /* write it out */
      printf("!%d\n", translate(i));  /* make it right out the current db 
				       * type otherwise we cannot read it
				       * back in!
				       */
      db_write_object(stdout, i);
    }
  }
  puts("***END OF DUMP***");
}

void main(argc, argv)
    int argc;
    char **argv;
{
  dbref i;
  int top_in;
  int top_ex;
  char *arg0;
  top_in = 0;
  top_ex = 0;

  /* now parse args */
  if(argc < 2) {
    fprintf(stderr,"Usage: %s [all] [{<include_dbref> } {-<exclude_dbref> }]\n",
	    *argv);
    fprintf(stderr, "\t all -- assume all objects included unless specified\n");
    fprintf(stderr, "\t <include> -- specifically include this object\n");
    fprintf(stderr, "\t              (and anything it owns)\n");
    fprintf(stderr, "\t-<exclude> -- specifically exclude this object\n");
    fprintf(stderr, "\t              (and anything it owns)\n");
    exit(-1);
  }
  arg0 = *argv;
  for (argv++, argc--; argc > 0; argv++, argc--) {
    i = atol(*argv);
    if (i == 0) {
      if (!strcmp(*argv, "all")) {
	include_all = 1;
      } else {
	fprintf(stderr, "%s: bogus argument %s\n", arg0, *argv);
      }
    } else if (i < 0) {
      excluded[top_ex++] = -i;
    } else {
      included[top_in++] = i;
    }
  }

  /* Terminate */
  included[top_in++] = NOTHING;
  excluded[top_ex++] = NOTHING;

  /* Load database */
  if (db_read(stdin) < 0) {
    fputs("Database load failed!\n", stderr);
    exit(1);
  }
  fputs("Done loading database...\n", stderr);

  /* Build translation table */
  build_trans();
  fputs("Done building translation table...\n", stderr);

  /* Scan everything */
  for (i = 0; i < db_top; i++)
    check_bad_exits(i);
  fputs("Done checking bad exits...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_owner(i);
  fputs("Done checking owners...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_location(i);
  fputs("Done checking locations...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_next(i);
  fputs("Done checking next pointers...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_contents(i);
  fputs("Done checking contents...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_exits(i);
  fputs("Done checking homes and exits...\n", stderr);

  do_write();
  fputs("Done.\n", stderr);

  exit(0);
}