/
CDC-1.2b/
CDC-1.2b/src/
parent $utilities
object $has_messages

var $root dbref 'has_messages
var $root child_index 1
var $root fertile 1
var $root manager $has_messages
var $root owned [$has_messages]
var $root owners [$]
var $root writable []
var $root readable ['parameters, 'methods, 'code]
var $root inited 1
var $has_messages messages 0
var $has_messages message_info 0

method add_message
    arg name;
    var mess;
    
    // name is the name of the message
    // def is the def value of the message.
    .perms(sender(), 'writer);
    if (type(name) != 'string)
        throw(~type, "Name must be a string");
    .set_message_info(name, [$base_evaluator, $compile_evaluator, $uncompile_evaluator, []]);
    mess = (| messages[this()] |) || #[];
    mess = dict_add(mess, name, $message_class.new(#[]));
    if (!messages)
        messages = #[];
    messages = dict_add(messages, this(), mess);
.

method message_info
    arg [name];
    
    if (name) {
        catch ~keynf {
            return message_info[name[1]];
        } with handler {
            throw(~messagenf, ("Message " + (name[1])) + " is not defined here.");
        }
    } else {
        return message_info || #[];
    }
.

method set_message_info
    arg name, value;
    
    if ((value[1]) && (type(value[1]) != 'dbref))
        throw(~type, "The first value should be the evaluator.");
    if ((value[2]) && (type(value[2]) != 'dbref))
        throw(~type, "The second value should be the compiler.");
    if ((value[3]) && (type(value[3]) != 'dbref))
        throw(~type, "The third value should be a the uncompiler.");
    if ((value[4]) && (type(value[4]) != 'list))
        throw(~type, "The fourth value should be a list of strings.");
    if (!message_info)
        message_info = #[];
    message_info = dict_add(message_info, name, value);
.

method del_message_part
    arg name, old_part;
    var current, parts, p, k, kids;
    
    current = .message_info(name);
    parts = current[4];
    if (old_part in parts)
        parts = delete(parts, old_part in parts);
    .set_message_info(name, replace(current, 4, parts));
    kids = [this()];
    while (kids) {
        k = kids[1];
        .debug(k);
        kids = sublist(kids, 2);
        k._del_message_part(name, old_part);
        kids = [@kids, @k.children()];
    }
.

method message_parts
    arg name;
    
    return (| (message_info[name])[4] |) || [];
.

method evalutor
    arg name;
    
    return (defined_messages[name])[1];
.

method set_evaluator
    arg name, evaluator;
    var current;
    
    .perms(sender(), 'writer);
    current = defined_messages[name];
    current = replace(current, 1, evaluator);
    .set_message_info(name, current);
.

method defines_message
    arg name;
    var n;
    
    // returns 1 if the message <name> is defined here.
    if (message_info) {
        for n in (dict_keys(message_info)) {
            if (match_begin(name, n))
                return 1;
        }
    }
    return 0;
.

method del_message
    arg name;
    var mess, kids;
    
    .perms(sender(), 'writer);
    message_info = dict_del(message_info, name);
    ._del_message(name);
    kids = .children();
    while (kids) {
        (| (kids[1])._del_message(name) |);
        kids = [@sublist(kids, 2), @(kids[1]).children()];
    }
.

method local_messages
    return messages || #[];
.

method messages
    var all_messages, a, a_messages, d_messages, d, m, my_d;
    
    // return all messages set on this() or parents
    // a : on eancestor
    // all_messages: the sum total of message
    // a_messages: messages from ancestor a
    // d: the definer of a message
    // d_messages: messages from ancestor a, defined on d
    // m: a specific message
    // my_d: messags from definer d that have already been found.
    all_messages = #[];
    for a in (.ancestors()) {
        if (a.has_ancestor(definer())) {
            a_messages = a.local_messages();
            for d in (dict_keys(a_messages)) {
                d_messages = a_messages[d];
                my_d = (| all_messages[d] |) || #[];
                for m in (dict_keys(d_messages)) {
                    if (!(m in dict_keys(my_d)))
                        my_d = dict_add(my_d, m, d_messages[m]);
                }
                all_messages = dict_add(all_messages, d, my_d);
            }
        }
    }
    return all_messages;
.

method local_message
    arg name, [definer];
    var d;
    
    if (messages) {
        if (!definer) {
            for d in (dict_keys(messages)) {
                catch ~keynf {
                    return (messages[d])[name];
                }
            }
        } else {
            catch ~keynf {
                return (messages[definer[1]])[name];
            }
        }
    }
    throw(~messagenf, "Message was not found.");
.

method prep_evalutor
    arg name;
    
    .perms(sender(), 'writers);
    return (.messaage_info(name))[2];
.

method message
    arg name, [definer];
    var a;
    
    if (definer)
        definer = definer[1];
    else
        definer = ._find_message_definer(name);
    
    // Umm, ._find_message_definer already loops ancestors (-Brandon)
    // for a in (.ancestors()) {
    catch ~messagenf, ~methodnf {
        //        return a.local_message(name, definer);
        return definer.local_message(name, definer);
    }
    
    // }
    throw(~messagenf, ("Message " + name) + " not found.");
.

method set_message
    arg name, message, [definer];
    var mess;
    
    .perms(sender(), 'writers);
    if (type(name) != 'string)
        throw(~type, "Name must be a string");
    if (!definer)
        definer = ._find_message_definer(name);
    if (definer.message_parts(name)) {
        if (type(message) == 'dict)
            message = $message_class.new(message, (.message_info(name, definer))[2]);
        if ((type(message) != 'frob) || (class(message) != $message_class))
            throw(~type, "message must be a $message_class, a dict");
    } else {
        if (type(message) == 'string)
            message = $ctext_class.new(message, (.message_info(name, definer))[2]);
        if ((type(message) != 'frob) || (class(message) != $ctext_class))
            throw(~type, "message must be a $ctext_class or a string");
    }
    if (!messages)
        messages = #[];
    mess = (| messages[definer] |) || #[];
    mess = dict_add(mess, name, message);
    messages = dict_add(messages, definer, mess);
.

method set_message_part
    arg name, message, [definer];
    var mes, part, pos, m;
    
    if (definer)
        definer = definer[1];
    else
        definer = ._find_message_definer(name);
    pos = $string.rindex(name, ".");
    part = substr(name, pos + 1);
    name = substr(name, 1, pos - 1);
    m = (| .message(name, definer) |) || ($message_class.new(#[], (.message_info(name, definer))[2]));
    if (!(part in (definer.message_parts(name))))
        throw(~partnf, (("Invalid part: " + part) + " for message: ") + message);
    m = m.add_entry(part, message);
    if (!messages)
        messages = #[];
    mes = (| messages[definer] |) || #[];
    messages = dict_add(messages, definer, dict_add(mes, name, m));
.

method local_matching_messages
    arg name, [definer];
    var n, matches, d;
    
    matches = [];
    if (definer) {
        for n in (dict_keys(messages[definer])) {
            if (match_begin(n, name))
                matches = [@matches, n];
        }
    } else {
        for d in (dict_keys(messages)) {
            for n in (dict_keys(messages[d])) {
                if (match_begin(n, name))
                    matches = [@matches, n];
            }
        }
    }
    return matches;
.

method matching_messages
    arg name;
    var matches, a, local, d, n;
    
    //returns a list of all messages set that match name
    matches = #[];
    for a in (.ancestors()) {
        if (a.has_ancestor(this())) {
            local = a.local_matching_message(name);
            for d in (dict_keys(local)) {
                for n in (dict_keys[d]) {
                    if (!(((local[d])[n]) in (matches[d])))
                        matches = dict_add(matches, d, [@matches[d], n]);
                }
            }
        }
    }
    return #[];
.

method _del_message
    arg name;
    var mess;
    
    mess = messages[sender()];
    mess = dict_del(mess, name);
    messages = dict_add(messages, sender(), mess);
.

method _find_message_definer
    arg name;
    var a;
    
    for a in (.ancestors()) {
        catch ~methodnf {
            if (a.defines_message(name))
                return a;
        }
    }
    throw(~definernf, ("Could not find definer for " + name) + ".");
.

method add_message_part
    arg name, [new_parts];
    var current, parts, p;
    
    .perms(sender(), 'writer);
    current = .message_info(name);
    parts = current[4];
    for p in (new_parts) {
        if (!(p in parts))
            parts = [@parts, p];
    }
    .set_message_info(name, replace(current, 4, parts));
.

method eval_message
    arg name, vars, [definer];
    
    if (definer)
        definer = definer[1];
    else
        definer = ._find_message_definer(name);
    vars = dict_add(vars, 'evaluator, (definer.message_info(name))[1]);
    return (.message(name, definer)).eval_ctext(vars);
.

method uninit_has_messages
    messages = 0;
    defined_messages = 0;
.

method _del_message_part
    arg name, part;
    var m, mess;
    
    .debug(sender(), name, part);
    if (messages && (sender() in dict_keys(messages))) {
        mess = messages[sender()];
        .debug(mess);
        if (name in dict_keys(mess)) {
            m = mess[name];
            .debug(m);
            m = m.del_entry(part);
            .debug(m);
            mess = dict_add(mess, name, m);
            messages = dict_add(messages, sender(), mess);
        }
    }
.