/* match.c */

#include "copyright.h"
/* Routines for parsing arguments */
#include <ctype.h>
#ifdef WANT_ANSI
#ifdef __STDC__
#include <stddef.h>
#endif /* __STDC__ */
#endif /* WANT_ANSI */

#include "config.h"
#include "db.h"
#include "mudconf.h"
#include "externs.h"
#include "match.h"

extern long random();

static dbref exact_match = NOTHING;	/* holds result of exact match */
static int check_keys = 0;	/* if non-zero, check for keys */
static dbref last_match = NOTHING;	/* holds result of last match */
static int match_count;		/* holds total number of inexact matches */
static dbref match_who;		/* player who is being matched around */
static const char *match_name;	/* name to match */
static int preferred_type = NOTYPE;	/* preferred type */

/*
 * This function removes repeated spaces from the template to which object
 * names are being matched.  It also removes inital and terminal spaces.
 */

static char *munge_space_for_match(char *name)
{
static char buffer[LBUF_SIZE];
char	*p, *q;

	p = name;
	q = buffer;
	while (isspace(*p))
		p++;		/* remove inital spaces */
	while (*p) {
		while (*p && !isspace(*p))
			*q++ = *p++;
		while (*p && isspace(*++p)) ;
		if (*p)
			*q++ = ' ';
	}
	*q = '\0';		/* remove terminal spaces and terminate
				 * string */
	return (buffer);
}

/*
 * check a list for objects that are prefix's for string, && are controlled
 * || link_ok
 */

dbref pref_match(dbref player, dbref list, const char *string)
{
  dbref lmatch = NOTHING;
  int mlen = 0;
  while (list != NOTHING) {
    if (string_prefix(string, Name(list)) &&
	(Flags(list) & PUPPET) &&
	controls(player, list)) {
      if (strlen(Name(list)) > mlen) {
	lmatch = list;
	mlen = strlen(Name(list));
      }
    }
    list = Next(list);
  }
  return (lmatch);
}

void init_match(dbref player, const char *name, int type)
{
  exact_match = last_match = NOTHING;
  match_count = 0;
  match_who = player;
  match_name = munge_space_for_match((char *)name);
  check_keys = 0;
  preferred_type = type;
}

void init_match_check_keys(dbref player, const char *name, int type)
{
  init_match(player, name, type);
  check_keys = 1;
}

static dbref choose_thing(dbref thing1, dbref thing2)
{
  int has1;
  int has2;
  if (thing1 == NOTHING) {
    return thing2;
  } else if (thing2 == NOTHING) {
    return thing1;
  }
  if (preferred_type != NOTYPE) {
    if (Typeof(thing1) == preferred_type) {
      if (Typeof(thing2) != preferred_type) {
	return thing1;
      }
    } else if (Typeof(thing2) == preferred_type) {
      return thing2;
    }
  }
  if (check_keys) {
    has1 = could_doit(match_who, thing1, A_LOCK);
    has2 = could_doit(match_who, thing2, A_LOCK);

    if (has1 && !has2) {
      return thing1;
    } else if (has2 && !has1) {
      return thing2;
    }
    /* else fall through */
  }
  return (random() % 2 ? thing1 : thing2);
}

void match_player()
{
  dbref match;
  char *p;
  if (*match_name == LOOKUP_TOKEN) {
    for (p = (char *)match_name + 1; isspace(*p); p++) ;
    if ((match = lookup_player(NOTHING, p, 1)) != NOTHING) {
      exact_match = match;
    }
  }
}
/* returns nnn if name = #nnn, else NOTHING */

static dbref absolute_name()
{
dbref	match;

	if (*match_name == NUMBER_TOKEN) {
		match = parse_dbref(match_name + 1);
		if (!Good_obj(match)) {
			return NOTHING;
		} else {
			return match;
		}
	} else {
		return NOTHING;
	}
}

void match_absolute()
{
dbref	match;

	match = absolute_name();
	if (Good_obj(match)) {
		exact_match = match;
	}
}

void match_me()
{
	if (!string_compare(match_name, "me")) {
		exact_match = match_who;
	}
}

void match_here()
{
	if (Good_obj(match_who) && Has_location(match_who)) {
		if (!string_compare(match_name, "here") &&
		    Good_obj(Location(match_who))) {
			exact_match = Location(match_who);
		}
	}
}

static void match_list(dbref first)
{
dbref	absolute;

	absolute = absolute_name();
	if (!Good_obj(absolute) || !controls(match_who, absolute))
		absolute = NOTHING;

	DOLIST(first, first) {
		if (first == absolute) {
			exact_match = first;
			return;
		} else if (!string_compare(Name(first), match_name)) {
			/* if multiple exact matches, randomly choose one */
			exact_match = choose_thing(exact_match, first);
		} else if (string_match(Name(first), match_name)) {
			last_match = first;
			match_count++;
		}
	}
}

void match_possession()
{
	if (Good_obj(match_who) && Has_contents(match_who))
		match_list(Contents(match_who));
}

void match_neighbor()
{
dbref	loc;

	if (Good_obj(match_who) && Has_location(match_who)) {
		loc = Location(match_who);
		if (Good_obj(loc)) {
			match_list(Contents(loc));
		}
	}
}

static void match_exit_internal (dbref loc)
{
dbref	exit, absolute;

	if (!Good_obj(loc) || !Has_exits(loc))
		return;
	absolute = absolute_name();
	if (!Good_obj(absolute) || !controls(match_who, absolute))
		absolute = NOTHING;

	DOLIST(exit, Exits(loc)) {
		if (exit == absolute) {
			exact_match = exit;
			return;
		}
		if (matches_exit_from_list((char *)match_name, Name(exit)))
			exact_match = choose_thing(exact_match, exit);
	}
}

void match_exit()
{
	if (Good_obj(match_who) && Has_location(match_who))
		match_exit_internal(Location(match_who));
}

void match_exit_with_parents()
{
dbref	loc, parent;
int	lev;

	if (Good_obj(match_who) && Has_location(match_who)) {
		loc = Location(match_who);
		match_exit_internal(loc);
		if (exact_match == NOTHING) {
			for (lev=0, parent=Parent(loc);
			     (Good_obj(parent) &&
			      (lev < mudconf.parent_nest_lim) &&
			      (exact_match == NOTHING));
			     parent=Parent(parent), lev++) {
				match_exit_internal(parent);
			}
		}
	}
}

void match_carried_exit()
{
	if (Good_obj(match_who) && Has_exits(match_who))
		match_exit_internal(match_who);
}

void match_carried_exit_with_parents()
{
dbref	parent;
int	lev;

	if (Good_obj(match_who) && Has_exits(match_who)) {
		match_exit_internal(match_who);
		if (exact_match == NOTHING) {
			for (lev=0, parent=Parent(match_who);
			     (Good_obj(parent) &&
			      (lev < mudconf.parent_nest_lim) &&
			      (exact_match == NOTHING));
			     parent=Parent(parent), lev++) {
				match_exit_internal(parent);
			}
		}
	}
}

void match_master_exit()
{
	if (Good_obj(mudconf.master_room))
		match_exit_internal(mudconf.master_room);
}

void match_everything()
{
	match_carried_exit();
	match_exit();
	match_neighbor();
	match_possession();
	match_me();
	match_here();
	match_absolute();
	match_player();
}

dbref match_result()
{
  if (exact_match != NOTHING) {
    return exact_match;
  } else {
    switch (match_count) {
      case 0:
	return NOTHING;
      case 1:
	return last_match;
      default:
	return AMBIGUOUS;
    }
  }
}
/* use this if you don't care about ambiguity */
dbref last_match_result()
{
  if (exact_match != NOTHING) {
    return exact_match;
  } else {
    return last_match;
  }
}

dbref noisy_match_result()
{
  dbref match;
  switch (match = match_result()) {
    case NOTHING:
      notify(match_who, NOMATCH_MESSAGE);
      return NOTHING;
    case AMBIGUOUS:
      notify(match_who, AMBIGUOUS_MESSAGE);
      return NOTHING;
    default:
      return match;
  }
}