/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
new object $command_cache: $has_commands;

var $command_cache interfaces = 0;
var $command_cache local_cache = 0;
var $command_cache remote_cache = 0;
var $command_cache shortcut_cache = 0;
var $has_commands shortcuts = #[];
var $root created_on = 796605573;
var $root fertile = 1;
var $root flags = ['methods, 'code, 'fertile, 'variables, 'core];
var $root inited = 1;
var $root managed = [$command_cache];
var $root manager = $command_cache;

public method .add_command_interface() {
    arg interface;
    
    if (!(interface.has_flag('command_cache)))
        throw(~perm, ("Command interface " + interface) + " is not setup as a cache.");
    interfaces = setadd(interfaces || [], interface);
    interface.interface_link();
};

private method .add_object_to_local_cache() {
    arg obj;
    var info, thing, element, part;
    
    info = (| obj.all_local_commands() |);
    if (info) {
        for element in (info) {
            for part in (element[2])
                .add_to_local_cache(part[1]);
        }
    }
};

public method .add_object_to_remote_cache() {
    arg obj;
    var info, thing, element, part;
    
    info = (| obj.all_remote_commands() |);
    if (info) {
        for element in (info) {
            for part in (element[2])
                .add_to_remote_cache(part[1], element[1]);
        }
    }
};

protected method .add_to_local_cache() {
    arg command;
    var part, cmd;
    
    if (type(local_cache) != 'dictionary)
        local_cache = #[];
    
    // if this dies, it will also fail on explode_template_word
    cmd = (| (command.explode())[1] |);
    for part in (command.explode_template_word())
        local_cache = local_cache.setadd_elem(part, cmd);
};

protected method .add_to_remote_cache() {
    arg command, definer;
    var part, cmd, value, cmds, defs, refs;
    
    if (type(remote_cache) != 'dictionary)
        remote_cache = #[];
    
    // if this dies, it will also fail on explode_template_word
    cmd = (| (command.explode())[1] |);
    for part in (command.explode_template_word()) {
        if ((value = (| remote_cache[part] |))) {
            cmds = (value[1]).setadd(cmd);
            refs = ((| (value[2])[definer] |) || 0) + 1;
            defs = (value[2]).add(definer, refs);
            remote_cache = remote_cache.add(part, [cmds, defs]);
        } else {
            remote_cache = remote_cache.add(part, [[cmd], #[[definer, 1]]]);
        }
    }
};

public method .cache_init() {
    if (!(sender().has_ancestor(definer())))
        throw(~nochild, ((sender() + " is not a descendant of ") + definer()) + ".");
    if (.is_command_cache()) {
        if ((!local_cache) || (!remote_cache))
            .rehash_caches();
    }
};

public method .cache_uninit() {
    var user;
    
    // if (!.is($user))
    //    throw(~notuser, this() + " is not a user object.");
    if (!(sender().has_ancestor(this())))
        throw(~nochild, ((sender() + " is not a descendant of ") + this) + ".\n");
    if (.is_command_cache()) {
        // see if anybody still needs us, otherwise purge the caches
        for user in ($user_db.connected()) {
            if (user.has_ancestor(this()))
                return;
        }
        .purge_caches();
    }
};

root method .core_command_cache() {
    if (this() == definer()) {
        interfaces = (local_cache = (remote_cache = (shortcut_cache = 0)));
    } else {
        (| clear_var('interfaces) |);
        (| clear_var('local_cache) |);
        (| clear_var('remote_cache) |);
        (| clear_var('shortcut_cache) |);
    }
};

public method .del_command_interface() {
    arg interface;
    
    if (!(interface.has_flag('command_cache)))
        throw(~perm, ("Command interface " + interface) + " is not setup as a cache.");
    interfaces = setremove(interfaces || [], interface);
    interface.interface_unlink();
};

protected method .del_from_remote_cache() {
    arg command, definer;
    var part, cmd, value, cmds, defs, refs;
    
    if (type(remote_cache) != 'dictionary)
        return;
    
    // if this dies, it will also fail on explode_template_word
    cmd = (| (command.explode())[1] |);
    for part in (command.explode_template_word()) {
        if ((value = (| remote_cache[part] |))) {
            refs = ((| (value[2])[definer] |) || 1) - 1;
            if (!refs) {
                remote_cache = remote_cache.del(part);
                if (!remote_cache)
                    (| clear_var('remote_cache) |);
            } else {
                [cmds, defs] = value;
                defs = defs.add(definer, refs);
                remote_cache = remote_cache.add(part, [cmds, defs]);
            }
        }
    }
};

public method .del_object_from_remote_cache() {
    arg obj;
    var info, thing, element, part;
    
    info = (| obj.all_remote_commands() |);
    if (info) {
        for element in (info) {
            for part in (element[2])
                .del_from_remote_cache(part[1], element[1]);
        }
    }
};

protected method .find_in_command_cache() {
    arg cmd_word, cache;
    var matches, match, obj, objs;
    
    matches = #[];
    for obj in ([this()] + parents()) {
        match = (| obj.(cache)()[cmd_word] |);
        if (match)
            matches = matches.union(match);
    }
    return matches;
};

public method .find_in_local_cache() {
    arg cmd;
    
    return (> local_cache[cmd] <);
};

protected method .find_in_local_caches() {
    arg cmd_word;
    var matches, match, obj, objs;
    
    matches = [];
    for obj in (([this()] + parents()) + (interfaces || [])) {
        if ((match = (| obj.find_in_local_cache(cmd_word) |)))
            matches = matches.union(match);
    }
    return matches;
};

public method .find_in_remote_cache() {
    arg cmd;
    
    return (> remote_cache[cmd] <);
};

public method .find_in_remote_caches() {
    arg cmd_word;
    var matches, match, obj, objs;
    
    matches = [];
    for obj in ([this()] + parents()) {
        if ((match = (| obj.find_in_remote_cache(cmd_word) |)))
            matches = matches.union(match);
    }
    return matches;
};

public method .interfaces() {
    return interfaces || [];
};

public method .is_command_cache() {
    return 'command_cache in (.flags());
};

public method .local_cache() {
    return local_cache;
};

public method .match_in_local_cache() {
    arg str, cmd, args;
    var command, match, matched, templates, info, cdef, def;
    
    templates = (matched = []);
    for command in (.find_in_local_caches(cmd)) {
        info = .get_command_info('local, command);
        if (!info)
            continue;
        for cdef in (info) {
            match = match_template(args, cdef[2]);
            if (match != 0)
                matched += [[match.length(), [str, cmd, @match], @cdef.subrange(3)]];
        }
        templates = templates.union(info.slice(3));
    }
    if (matched) {
        info = [matched[1]];
        matched = matched.delete(1);
        for match in (matched) {
            if ((match[1]) > ((info[1])[1]))
                info = [match];
            else if ((match[1]) == ((info[1])[1]))
                info += [match];
        }
        return ['local, info];
    }
    if (!templates)
        return 0;
    return ['partial, templates];
};

public method .match_in_remote_cache() {
    arg str, cmd, args;
    var cache, definer, command, info, cdef, match, matched, templates;
    
    if (!(cache = (| .find_in_remote_caches(cmd) |)))
        return 0;
    templates = [];
    matched = [];
    for command in (cache[1]) {
        for definer in ((cache[2]).keys()) {
            info = definer.get_command_info('remote, command);
            if (!info)
                continue;
            for cdef in (info) {
                match = args.match_template(cdef[2]);
                if (match != 0)
                    matched += [[match.length(), definer, [str, cmd, @match], @cdef.subrange(3)]];
            }
            templates = templates.union(info.slice(3));
        }
    }
    if (matched) {
        info = [matched[1]];
        matched = matched.delete(1);
        for match in (matched) {
            if ((match[1]) > ((info[1])[1]))
                info = [match];
            else if ((match[1]) == ((info[1])[1]))
                info += [match];
        }
        return ['remote, info];
    }
    return ['partial, templates];
};

public method .match_in_shortcut_cache() {
    arg str, cmd, args;
    var shortcut, match, obj, shorts;
    
    for obj in ([this()] + parents()) {
        if ((shorts = obj.shortcut_cache())) {
            for shortcut in (shorts) {
                match = match_pattern(str, shortcut[1]);
                if (match != 0)
                    return ['shortcut, [(shortcut[2])[1], [str, @$command_lib.handle_shortcut_fields((shortcut[2])[2], match)]]];
            }
        }
    }
    return 0;
};

public method .purge_caches() {
    (> .perms(sender()) <);
    (| clear_var('shortcut_cache) |);
    (| clear_var('remote_cache) |);
    (| clear_var('local_cache) |);
};

public method .rehash_caches() {
    var cmd, obj, part, element;
    
    (> .perms(sender()) <);
    (| .purge_caches() |);
    if (!(.is_command_cache())) {
        // if we are not an official 'cache', just cache commands defined on us
        for cmd in (.local_commands()) {
            for part in (cmd[2])
                .add_to_local_cache(part[1]);
        }
        shortcut_cache = (.shortcuts()).to_list();
    } else {
        // otherwise cache all defined commands
        (> .add_object_to_local_cache(this()) <);
        shortcut_cache = .all_shortcuts();
    }
    
    // remote caches are different, and HAVE to be specific to the user
    if (.is($location)) {
        for obj in ([this()] + (.contents()))
            (> .add_object_to_remote_cache(obj) <);
    }
};

public method .remote_cache() {
    return remote_cache;
};

public method .set_as_command_cache() {
    arg makecache;
    
    (> .perms(sender(), 'manager) <);
    if (makecache)
        (> .add_flag('command_cache) <);
    else
        (> .del_flag('command_cache) <);
};

public method .shortcut_cache() {
    return shortcut_cache;
};

root method .uninit_command_cache() {
    var i;
    
    (| .purge_caches() |);
    for i in (interfaces || [])
        i.interface_unlink();
};