#pragma strong_types
#pragma save_types

/* obj/simul_efun.c
 *
 * The simul-efun object provides functions which can be accessed as
 * if they were true efuns. This is most useful to protect sensitive
 * efuns from clumsy fingers, but is also used to simulate functions
 * which formerly were implemented by the driver.
 */

#define MAX_LOG_SIZE 50000

#define BACKBONE_WIZINFO_SIZE 5

#define LIVING_NAME 3
#define NAME_LIVING 4

#include "/sys/wizlist.h"
#include "/sys/erq.h"
#include "/sys/files.h"

mapping living_name_m, name_living_m;
  /* Living -> Name and Name -> Living mappings.
   */

//---------------------------------------------------------------------------
void start_simul_efun()

/* Activate the simul-efun object.
 */

{
    mixed *info;

    if ( !(info = get_extra_wizinfo(0)) )
	set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
    if (!(living_name_m = info[LIVING_NAME]))
	living_name_m = info[LIVING_NAME] = allocate_mapping(0, 1);
    if (!(name_living_m = info[NAME_LIVING]))
	name_living_m = info[NAME_LIVING] = allocate_mapping(0, 1);
    if (find_call_out("clean_simul_efun") < 0)
	call_out("clean_simul_efun", 1800);
}

//---------------------------------------------------------------------------
static void clean_simul_efun()
{
    /* There might be destructed objects as keys. */
    m_indices(living_name_m);
    remove_call_out("clean_simul_efun");
    if (find_call_out("clean_name_living_m") < 0)
    {
	call_out(
	  "clean_name_living_m",
	  1,
	  m_indices(name_living_m),
	  sizeof(name_living_m)
	);
    }
    call_out("clean_simul_efun", 3600);
}

//---------------------------------------------------------------------------
static void clean_name_living_m(string *keys, int left)
{
    int i, j;
    mixed a;

    if (left) {
	if (pointerp(a = name_living_m[keys[--left]]) && member(a, 0)>= 0) {
	    i = sizeof(a);
	    do {
		if (a[--i])
		    a[<++j] = a[i];
	    } while (i);
	    name_living_m[keys[left]] = a = j > 1 ? a[<j..] : a[<1];
	}
	if (!a)
	    efun::m_delete(name_living_m, keys[left]);
	call_out("clean_name_living_m", 1, keys, left);
    }
}

//---------------------------------------------------------------------------
void ls (string path)

/* Print the directory listing of <path>, like the unix command.
 */

{
    int max, i, len, tmp;
    status trunc_flag;
    mixed *dir;
    set_this_object(previous_object());
    dir = get_dir (path, GETDIR_NAMES|GETDIR_SIZES);
    if (path != "/")
	path += "/";
    if (!dir) {
        write("No such directory.\n");
        return;
    }
    if (sizeof(dir) > 999)
    {
        dir = dir[0..998];
        trunc_flag = 1;
    }
    for(i = sizeof(dir); i--; ) {
        if(dir[i--] == -2)
            dir[i]+="/";
        len = strlen(dir[i]);
        if (len > max)
            max = len;
    }
    ++max;
    if (max > 79)
        max = 79;
    for (i=0; i < sizeof(dir); i+=2) {
	string name;
            name = dir[i];
	tmp = strlen(name);
	if (len + tmp > 79) {
	    len = 0;
	    write("\n");
	}
	write(name);
        if (len + max > 79) {
            write("\n");
            len = 0;
        } else {
            write("                                                                                "[80-max+tmp..]);
            len += max;
        }
    }
    write("\n");
    if (trunc_flag) write("***TRUNCATED***\n");
}

//---------------------------------------------------------------------------
string create_wizard(string owner, string domain)
{
    mixed result;

    set_this_object(previous_object());
    result =
      (mixed)__MASTER_OBJECT__->master_create_wizard(owner, domain, previous_object());
    if (stringp(result)) return result;
    return 0;
}

//---------------------------------------------------------------------------
void log_file(string file,string str)
{
    string file_name;
    int *st;

    file_name = "/log/" + file;
#ifdef COMPAT_FLAG
    if (sizeof(regexp(({file}), "/")) || file[0] == '.' || strlen(file) > 30 )
    {
        write("Illegal file name to log_file("+file+")\n");
        return;
    }
#endif
    if ( sizeof(st = get_dir(file_name,2) ) && st[0] > MAX_LOG_SIZE) {
	catch(rename(file_name, file_name + ".old")); /* No panic if failure */
    }
    set_this_object(previous_object());
    write_file(file_name, str);
}

//---------------------------------------------------------------------------
void localcmd()
{
    string *verbs;
    int i,j;

    verbs = query_actions(this_player());
    for (i=0, j = sizeof(verbs); --j >= 0; i++) {
	write(verbs[i]+" ");
    }
    write("\n");
}

//---------------------------------------------------------------------------
mixed *unique_array(mixed *arr,string func,mixed skipnum)
{
    mixed *al, last;
    int i, j, k, *ordinals;

    if (sizeof(arr) < 32) return efun::unique_array(arr, func, skipnum);
    for (ordinals = allocate(i = sizeof(arr)); i--; )
	ordinals[i] = i;
    al = order_alist(map_objects(arr, func), ordinals, (ordinals=0,arr));
    arr = al[2];
    ordinals = al[1];
    al = al[0];
    if (k = i = sizeof(al)) {
	for (last = al[j = --i]; i--; ) {
	    if (al[i] != last) {
		if (last != skipnum) {
		    arr[--k] = arr[i+1..j];
		    ordinals[k] = ordinals[j];
		}
		last = al[j = i];
	    }
	}
	if (last != skipnum) {
	    arr[--k] = arr[0..j];
	    ordinals[k] = ordinals[j];
	}
    }
    return order_alist(ordinals[k..], arr[k..])[1];
}

//---------------------------------------------------------------------------
mixed snoop(mixed snoopee)
{
    int result;

    if (snoopee && query_snoop(snoopee)) {
        write("Busy.\n");
        return 0;
    }
    result = snoopee ? efun::snoop(this_player(), snoopee)
                     : efun::snoop(this_player());
    switch (result) {
	case -1:
	    write("Busy.\n");
	case  0:
	    write("Failed.\n");
	case  1:
	    write("Ok.\n");
    }
    if (result > 0) return snoopee;
}

//---------------------------------------------------------------------------
void notify_fail(mixed message)
{
    if ( !(stringp(message) && strstr(message, "@@") < 0) ) {
	efun::notify_fail(message);
	return;
    }
    efun::notify_fail(
      funcall(
        bind_lambda(#'lambda, previous_object()),
        0, ({#'process_string, message})
      )
    );
}

//---------------------------------------------------------------------------
string version() {
    return __VERSION__;
}

//---------------------------------------------------------------------------
string query_host_name() {
    return __HOST_NAME__;
}

//---------------------------------------------------------------------------
nomask void set_environment() {}

nomask void set_this_player() {}

//---------------------------------------------------------------------------
void add_worth(int value, object ob)
{
    mixed old;
#ifdef __COMPAT_MODE__
    switch (explode(file_name(previous_object()), "/")[0]) {
#else
    switch (explode(file_name(previous_object()), "/")[1]) {
#endif
      default:
	raise_error("Illegal call of add_worth.\n");
      case "obj":
      case "std":
      case "room":
    }
    if (!ob) {
	if ( !(ob = previous_object(1)) )
	    return;
    }
    if (intp(old = get_extra_wizinfo(ob)))
        set_extra_wizinfo(ob, old + value);
}

//---------------------------------------------------------------------------
void wizlist(string name)
{
    int i, pos, total_cmd;
    int *cmds;
    mixed *a;
    mixed *b;

    if (!name) {
        name = this_player()->query_real_name();
        if (!name)
            return;
    }
    a = transpose_array(wizlist_info());
    cmds = a[WL_COMMANDS];
    a[WL_COMMANDS] = a[0];
    a[0] = cmds;
    a = order_alist(a);
    cmds = a[0];
    a[0] = a[WL_COMMANDS];
    a[WL_COMMANDS] = cmds;
    if ((pos = member(a[WL_NAME], name)) < 0 && name != "ALL")
        return;
    b = allocate(sizeof(cmds));
    for (i = sizeof(cmds); i;) {
        b[<i] = i;
        total_cmd += cmds[--i];
    }
    a = transpose_array(a + ({b}) );
    if (name != "ALL") {
        if (pos + 18 < sizeof(cmds)) {
            a = a[pos-2..pos+2]+a[<15..];
        } else if (pos < sizeof(cmds) - 13) {
            a = a[pos-2..];
        } else {
            a = a[<15..];
        }
    }
    write("\nWizard top score list\n\n");
    if (total_cmd == 0)
        total_cmd = 1;
    for (i = sizeof(a); i; ) {
        b = a[<i--];
        printf("%-15s %5d %2d%% (%d)\t[%4dk,%5d] %6d %d\n",
          b[WL_NAME], b[WL_COMMANDS], b[WL_COMMANDS] * 100 / total_cmd, b[<1],
          b[WL_EVAL_COST] / 1000,
          b[WL_HEART_BEATS], b[WL_EXTRA], b[WL_ARRAY_TOTAL]
        );
    }
    printf("\nTotal         %7d     (%d)\n\n", total_cmd, sizeof(cmds));
}

//---------------------------------------------------------------------------
void shout(string s)
{
    filter_array(users(), lambda(({'u}),({#'&&,
      ({#'environment, 'u}),
      ({#'!=, 'u, ({#'this_player})}),
      ({#'tell_object, 'u, to_string(s)})
    })));
}

//---------------------------------------------------------------------------
protected void set_living_name(string name)
{
    string old;
    mixed a;
    int i;

    if (old = living_name_m[previous_object()]) {
	if (pointerp(a = name_living_m[old])) {
	    a[member(a, previous_object())] = 0;
	} else {
	    efun::m_delete(name_living_m, old);
	}
    }
    living_name_m[previous_object()] = name;
    if (a = name_living_m[name]) {
	if (!pointerp(a)) {
	    name_living_m[name] = ({a, previous_object()});
	    return;
	}
	/* Try to reallocate entry from destructed object */
	if ((i = member(a, 0)) >= 0) {
	    a[i] = previous_object();
	    return;
	}
	name_living_m[name] = a + ({previous_object()});
	return;
    }
    name_living_m[name] = previous_object();
}

//---------------------------------------------------------------------------
object find_living(string name)
{
    mixed *a, r;
    int i;

    if (pointerp(r = name_living_m[name])) {
	if ( !living(r = (a = r)[0])) {
	    for (i = sizeof(a); --i;) {
		if (living(a[<i])) {
		    r = a[<i];
		    a[<i] = a[0];
		    return a[0] = r;
		}
	    }
	}
	return r;
    }
    return living(r) && r;
}

//---------------------------------------------------------------------------
object find_player(string name)
{
    mixed *a, r;
    int i;

    if (pointerp(r = name_living_m[name])) {
	if ( !(r = (a = r)[0]) || !query_once_interactive(r)) {
	    for (i = sizeof(a); --i;) {
		if (a[<i] && query_once_interactive(a[<i])) {
		    r = a[<i];
		    a[<i] = a[0];
		    return a[0] = r;
		}
	    }
	    return 0;
	}
	return r;
    }
    return r && query_once_interactive(r) && r;
}

/*===========================================================================
 * The following functions provide the necessary compatibility of this
 * compat-mode mudlib with a plain driver.
 * Just the parse_command() efun is not simulated.
 */

#ifndef __COMPAT_MODE__
//---------------------------------------------------------------------------
string function_exists (string str, object ob)
{
    string rc;

    rc = efun::function_exists(str, ob);
    return stringp(rc) ? rc[1..] : 0;
}

//---------------------------------------------------------------------------
string file_name(object ob)
{
    string rc;

    rc = efun::file_name(ob);
    return stringp(rc) ? rc[1..] : 0;
}

//---------------------------------------------------------------------------
string object_name(object ob)
{
    string rc;

    rc = efun::object_name(ob);
    return stringp(rc) ? rc[1..] : 0;
}

//---------------------------------------------------------------------------
string program_name(object ob)
{
    string rc;

    rc = efun::program_name(ob);
    return stringp(rc) ? rc[1..] : 0;
}

//---------------------------------------------------------------------------
string to_string(mixed arg)
{
    string rc;

    rc = efun::to_string(arg);
    return objectp(arg) ? rc[1..] : rc;
}

//---------------------------------------------------------------------------
string creator(object ob)
{
    return getuid(ob);
}

//---------------------------------------------------------------------------
varargs void add_action(string fun, string cmd, int flag)
{
    if (fun == "exit")
        raise_error("Illegal to define a command to the exit() function.\n");

    efun::set_this_object(previous_object());
    if (cmd)
        efun::add_action(fun, cmd, flag);
    else /* historic usage */
        efun::add_action(fun);
}

//---------------------------------------------------------------------------
object present_clone (mixed obj, object env)
{
  if (stringp(obj) && '/' != obj[0])
      obj = "/"+obj;
  return efun::present_clone(obj, env);
}

//---------------------------------------------------------------------------
#endif /* __COMPAT_MODE__ */

#if !__EFUN_DEFINED__(present)
//---------------------------------------------------------------------------
varargs object present(mixed ob, object env)
{
    int specific, num, i;
    object found;
    string str;

    if (!env)
    {
        env = previous_object();
        specific = 0;
    }
    else
        specific = 1;

    if (objectp(ob))
    {
        /* Quick check: is ob there or not? */

        if (specific)
            return environment(ob) == env ? ob : 0;
        if (environment(ob) == env
         || (environment(env) && environment(ob) == environment(env))
           )
            return ob;
        return 0;
    }

    /* Search by name. Prepare the search parameters */
    if (2 != sscanf(ob, "%s %d", str, num))
    {
        num = 1;
        str = ob;
    }

    /* First, search in env by name */
    for (found = first_inventory(env), i = 0
        ; found
        ; found = next_inventory(env))
    {
        if (found->id(str) && ++i == num)
            break;
        if (!found)  /* may happen */
            break;
    }

    if (found || specific)
        return found;

    /* If not found, search in environment(env) by name */
    env = environment(env);
    if (!env)
        return 0;
    if (env->id(ob))
        return env;
    if (!env) /* the id() may have destructed env */
        return 0;

    for (found = first_inventory(env), i = 0
        ; found
        ; found = next_inventory(env))
    {
        if (found->id(str) && ++i == num)
            break;
        if (!found)  /* may happen */
            break;
    }

    return found;
}

#endif /* !efun_defined(present) */

#if !__EFUN_DEFINED__(transfer)
//---------------------------------------------------------------------------
/*
 * Transfer an object from an object to an object.
 * Call add_weight(), drop(), get(), prevent_insert(), add_weight(),
 * and can_put_and_get() where needed.
 * Return 0 on success, and special code on failure:
 *
 * 1: To heavy for destination.
 * 2: Can't be dropped.
 * 3: Can't take it out of it's container.
 * 4: The object can't be inserted into bags etc.
 * 5: The destination doesn't allow insertions of objects.
 * 6: The object can't be picked up.
 */
int transfer(object item, object dest)
{
    int weight;
    object from;

    weight = item->query_weight();
    if (!item)
        return 3;

    from = environment(item);
    if (from)
    {
        /*
         * If the original place of the object is a living object,
         * then we must call drop() to check that the object can be dropped.
         */
        if (living(from))
        {
            if (item->drop() || !item)
                return 2;
        }
        /*
         * If 'from' is not a room and not a player, check that we may
         * remove things out of it.
         */
        else if (environment(from))
        {
            if (from->can_put_and_get() || !from)
                return 3;
        }
    }

    /*
     * If the destination is not a room, and not a player,
     * Then we must test 'prevent_insert', and 'can_put_and_get'.
     */
    if (environment(dest) && !living(dest))
    {
        if (item->prevent_insert())
            return 4;
        if (!dest->can_put_and_get() || !dest)
            return 5;
    }

    if (living(dest))
    {
        if (item->get() || !item)
            return 6;
    }

    /*
     * If it is not a room, correct the total weight in the destination.
     */
    if (environment(dest) && weight)
    {
        if (!dest->add_weight(weight) || !dest)
            return 1;
    }

    /*
     * If it is not a room, correct the weight in the 'from' object.
     */
    if (from && environment(from) && weight)
    {
        from->add_weight(-weight);
    }

    move_object(item, dest);

    return 0;
}

#endif /* !efun_define(transfer) */

#if !__EFUN_DEFINED__(extract)
//---------------------------------------------------------------------------
mixed extract (mixed data, varargs mixed*from_to)

{
    int from, to;

    if (!stringp(data) && !pointerp(data))
    {
        raise_error("Illegal type for extract(): must be string or array.\n");
        return 0;
    }

    switch(sizeof(from_to))
    {
    case 0: return data;
    case 1:
        if (!intp(from_to[0]))
        {
            raise_error("Illegal 'from' index for extract(): must be a number.\n");
            return 0;
        }
        from = from_to[0];
        if (from >= 0)
            return data[from..];
        return data[<-from..];
    case 2;
        if (!intp(from_to[0]) || !intp(from_to[1]))
        {
            raise_error("Illegal index for extract(): must be a number.\n");
            return 0;
        }
        from = from_to[0];
        to = from_to[1];
        if (from >= 0)
        {
            if (to >= 0)
                return data[from..to];
            return data[<from..<-to];
        }
        if (to >= 0)
            return data[<-from..to];
        return data[<from..<-to];
    }

    raise_error("Illegal number of arguments for extract().\n");
    return 0;
}

#endif /* !efun_defined(extract) */

/*************************************************************************/
