/* Do not remove the headers from this file! see /USAGE for more info. */

inherit M_EXIT;

private nosave mixed def_dest = 0;
private nosave mixed def_exit_msg = 0;
private nosave mixed def_enter_msg = 0;
private nosave mixed def_check = 0;
private nosave mixed default_desc = 0;
private nosave mapping prep_dest = ([]);
private nosave mapping prep_exit_msg = ([ ]);
private nosave mapping prep_enter_msg = ([ ]);
private nosave mapping prep_check = ([ ]);
private nosave mapping prep_desc = ([ ]);
private nosave string go_verb = "go";
private nosave string base = 0;
private nosave mapping prep_aliases = ([ ]);

string query_go_method();
void set_default_enter_msg(mixed);
void set_default_exit_msg(mixed);

void alias_prep(string src, string dest) {
    if (!prep_dest[src]) {
        error(sprintf("***Preposition doesn't exist to alias from: %O", src));
    }
    prep_dest[dest] = prep_dest[src];
    prep_exit_msg[dest] = prep_exit_msg[src];
    prep_enter_msg[dest] = prep_enter_msg[src];
    prep_check[dest] = prep_check[src];
    prep_desc[dest]  = prep_desc[src];
}

void setup_default_prep_aliases() {
    foreach (string k, string value in prep_aliases) {
        if (prep_dest[k] && !prep_dest[value]) {
            alias_prep(k, value);
        }
    }
}

mixed prep_default_msg(string prep) {
    if (stringp(prep))
        return "$N $v" + query_go_method() + " " + prep + " " + 
           this_object()->the_short() + ".";
    else
        return "$N $v" + query_go_method() + " " +
            this_object()->the_short() + ".";
}

string query_go_method() {
    return go_verb;
}

void set_go_method(string g) {
    go_verb = g;
}

mixed query_default_destination() {
    mixed dirs = keys(prep_dest) + query_exit_directions(0);
    if (def_dest == 0)
        if (member_array(dirs[0], query_exit_directions(0)) != -1)
            return query_exit_destination(dirs[0], base);
        else
            return eval_dest(prep_dest[dirs[0]], 0, base);
    return eval_dest(def_dest, 0, base);
}

mixed query_default_exit_msg() {
    return evaluate(def_exit_msg);
}

mixed query_default_enter_msg() {
    return evaluate(def_enter_msg);
}

void set_default_destination(mixed arg) {
   def_dest = arg;
    def_check = 1;
    if ( def_exit_msg == 0 ) set_default_exit_msg( (: prep_default_msg :) );
    if ( def_enter_msg == 0 ) set_default_enter_msg( (: prep_default_msg :) );
}

void set_default_exit_msg(mixed arg) {
    def_exit_msg = arg;
}

void set_default_enter_msg(mixed arg) {
    def_enter_msg = arg;
}

void set_default_check(mixed arg) {
    def_check = arg;
}

void set_prep_exit_msg(string prep, mixed msg) {
    prep_exit_msg[prep] = msg;
}

void set_prep_enter_msg(string prep, mixed msg) {
    prep_enter_msg[prep] = msg;
}

void set_prep_check(string prep, mixed check) {
    prep_check[prep] = check;
}

//needed so that "enter door" calls the exit check in direct_go_word_obj()
void set_exit_check(string dir, function f) {
    ::set_exit_check(dir, f);
    set_prep_check(dir, f);
}

void set_prep_description(string prep, mixed desc) {
    prep_desc[prep] = desc;
}

void add_prep(string prep, mixed dest) {
    if (stringp(dest) && dest[0] == '#')
        prep_check[prep] = dest[1..];
    else
        prep_check[prep] = 1;
    prep_dest[prep] = dest;
    prep_desc[prep] = default_desc;
    prep_exit_msg[prep] = (: prep_default_msg :);
    prep_enter_msg[prep] = (: prep_default_msg :);
}

void set_preps(mapping p) {
    mixed key;
   if (mapp(p)) {
        prep_dest += p;
        foreach (key in keys(p)) {
            if (stringp(p[key]) && p[key][0] == '#')
                prep_check[key] = p[key][1..];
            else
                prep_check[key] = 1;
            prep_desc[key] = default_desc;
            prep_exit_msg[key] = (: prep_default_msg :);
            prep_enter_msg[key] = prep_exit_msg[key];
        }
    }
}

mixed query_prep_exit_msg(string prep) {
    prep = PREPOSITION_D->translate_preposition(prep);
    return evaluate(prep_exit_msg[prep], prep);
}

mixed query_prep_enter_msg(string prep) {
    prep = PREPOSITION_D->translate_preposition(prep);
    return evaluate(prep_enter_msg[prep], prep);
}

mixed query_prep_description(string prep) {
    prep = PREPOSITION_D->translate_preposition(prep);
    return evaluate(prep_desc[prep]);
}

mixed query_prep_destination(string prep) {
    prep = PREPOSITION_D->translate_preposition(prep);
    return eval_dest(prep_dest[prep], 0, base);
}

// prep_only only adds the direction to the object, 
// and NOT to the room.. 
// i.e. ("up","blah.c", 1) means that just "up"/"go up" won't work
// you have to say "go up id"
varargs void add_exit(mixed dir, mixed dest, int prep_only) {
    add_prep(dir, dest);
    if (!prep_only) 
        ::add_exit(dir, dest);
}

mixed direct_go_word_obj(string prep) {
    prep = PREPOSITION_D->translate_preposition(prep);
    if (undefinedp(prep_dest[prep]))
        return "You can't " + query_go_method() + " " + prep + " that, only " +
               format_list(keys(prep_dest), "or") + ".";
    //prep and this_body() are needed because of set_exit_check()
    return evaluate(prep_check[prep], prep, this_body());
}

mixed direct_go_wrd_obj(string prep) {
    // The driver calls this instead of direct_go_word_obj() for WRD
    // OBJ rules.. bizarre...
    return direct_go_word_obj(prep);
}

mixed direct_go_obj() {
    mixed dirs = keys(prep_dest);
    if (def_dest == 0) {
        switch (sizeof(dirs)) {
	case 0:
            return environment(this_object())->get_default_exit();
	case 1:
            return direct_go_word_obj(dirs[0]);
        default:
            return "Do you want to " + query_go_method() + " " +
                   format_list(keys(prep_dest), "or") + " that?";
        }
    }
    return evaluate(def_check);
}

mixed direct_enter_obj()
{
   if(query_go_method() != "enter")
      return 0;

   return direct_go_obj();
}

mixed direct_climb_obj()
{
   if(query_go_method() != "climb")
      return 0;

   return direct_go_obj();
}

mixed direct_climb_wrd_obj(string prep)
{
   if(query_go_method() != "climb")
      return 0;

   return direct_go_word_obj(prep);
}

mixed direct_board_obj()
{
   if(query_go_method() != "board")
      return 0;

   return direct_go_obj();
}

mixed direct_dismount_obj()
{
   if(query_go_method() != "mount")
      return 0;
   return 1;
}

void propogate_exits_up() {
    string array dirs = query_exit_directions(1);
    string dir;
    foreach (dir in dirs) {
        if (member_array(dir, 
              environment(this_object())->query_exit_directions()) == -1)
            environment(this_object())->add_exit(dir, this_object());
        else
            error("*Exit already exists in room: " + dir);
    }
    dirs = query_hidden_exits();
    foreach (dir in dirs) {
         environment(this_object())->add_hidden_exit(dir, this_object());
    }
}

void delete_exits_up() {
    string array dirs = query_exit_directions(0);
    string dir;
    foreach (dir in dirs) {
        environment(this_object())->delete_exit(dir);
    }
    dirs = query_hidden_exits();
    foreach (dir in dirs) {
        environment(this_object())->remove_hidden_exit(dir);
        environment(this_object())->delete_exit(dir);
    }
}

varargs void on_clone() {
    propogate_exits_up();
    base = environment(this_object())->query_base();
    setup_default_prep_aliases(); 
}

void remove() {
    delete_exits_up();
}
