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

var $root dbref 'has_settings
var $root child_index 0
var $root fertile 1
var $root manager $has_settings
var $root owned [$has_settings]
var $root owners [$]
var $root writable []
var $root readable ['parameters, 'methods, 'code]
var $root inited 1
var $has_settings settings #[]
var $has_settings setting_types #[]
var $has_settings setting_data #[]

method _find_setting
    arg spec, definer, ancestors;
    
    if (definer) {
        if (definer in dict_keys(settings)) {
            if (spec in (settings[definer]))
                return (settings[definer])[spec];
        }
    }
    for definer in (dict_keys(settings)) {
        if (spec in (settings[definer]))
            return (settings[definer])[spec];
    }
    while (ancestors) {
        if ((ancestors[1]).has_ancestor($settings))
            return (ancestors[1])._find_setting(spec, definer, sublist(ancestors, 2));
        else
            ancestors = sublist(ancestors, 2);
    }
    throw(~settingnf, "A matching setting could not be found");
.

method uninit_has_settings
    settings = 0;
    setting_types = 0;
    setting_data = 0;
.

method get_setting
    arg name, definer, [args];
    var info;
    
    if (!definer)
        definer = ._find_definer(name);
    info = definer.get_setting_info(name);
    if (args)
        args = [@info['get_args], @args];
    else
        args = info['get_args];
    args = ._build_args(args, #[['name, name], ['definer, definer]]);
    return .(info['get])(@args);
.

method _build_args
    arg arg_list, values;
    var key, output;
    
    output = [];
    for key in (arg_list) {
        if (key in dict_keys(values)) {
            output = [@output, values[key]];
            values = dict_del(values, key);
        } else {
            output = [@output, key];
        }
    }
    return output;
.

method _find_definers
    arg name, count, [begin];
    var a, matches;
    
    // Find a definer for <name>
    // <count> is a number
    //    1 finds the first possible definer
    //    0 finds all
    // If <begin> is given cound a match_begin as a match
    matches = [];
    for a in (ancestors()) {
        if (a.has_ancestor($has_settings)) {
            if (a.defines_setting(name, begin)) {
                switch (count) {
                    case 0:
                        matches = [@matches, a];
                    case 1:
                        return [@matches, a];
                    default:
                        matches = [@matches, a];
                        count = count - 1;
                }
            }
        }
    }
    return matches;
.

method set_setting
    arg name, definer, value, [args];
    var info, data;
    
    if (!definer)
        definer = ._find_definers(name, 1);
    if (definer)
        definer = definer[1];
    else
        throw(~definernf, "Definer not found.");
    info = definer.get_setting_info(name);
    if (.(info['check])(value)) {
        data = #[['name, name], ['definer, definer], ['style, 'set], ['value, value]];
        if (args)
            args = [@info['set_args], @args];
        else
            args = info['set_args];
        args = ._build_args(args, data);
        .(info['set])(@args);
        return 1;
    } else {
        return 0;
    }
.

method add_setting
    arg name, [args];
    var values, key;
    
    // .add_setting("name",#['type,'check,'get,'set,'del])
    .perms(sender(), 'writers);
    values = (| .get_setting_info(name) |) || (.default_setting_info());
    if (args)
        args = args[1];
    else
        args = #[];
    if (type(args) != 'dictionary)
        throw(~type, "args must be a dictionary");
    for key in (dict_keys(args))
        values = dict_add(values, key, args[key]);
    if (type(name) != 'string)
        throw(~type, "name must be a string");
    if (type(values['type]) != 'string)
        throw(~type, "Type must be a string");
    if (type(values['check]) != 'symbol)
        throw(~type, "Check must be a symbol");
    if (type(values['set]) != 'symbol)
        throw(~type, "set must be a symbol");
    if (type(values['get]) != 'symbol)
        throw(~type, "get must be a symbol");
    if (type(values['del]) != 'symbol)
        throw(~type, "del must be a symbol");
    if (!setting_types)
        setting_types = #[];
    setting_types = dict_add(setting_types, name, values);
    return 1;
.

method defines_setting
    arg name, [begin];
    var n;
    
    // returns 1 if the setting <name> is defined here.
    // if begin is given do a match begin instead of an exact match
    if (setting_types) {
        if (begin) {
            for n in (dict_keys(setting_types)) {
                if (match_begin(name, n))
                    return 1;
            }
        } else {
            return name in dict_keys(setting_types);
        }
    }
    return 0;
.

method find_matching_settings
    arg name, definer;
    var n, a, matches, sets;
    
    // find all settings that match name
    // return a list of [[$definer, "name"],...]
    // A setting matches name if name matches the begining of the setting name.
    // If definer is not "" limit to the search to settings form definer.
    matches = [];
    for a in (.ancestors()) {
        if (a.has_ancestor($has_settings)) {
            if (!definer) {
                sets = a.setting_types();
                for n in (dict_keys(sets)) {
                    if (match_begin(name, n))
                        matches = [@matches, [this(), n]];
                }
            } else if (a.has_ancestor(definer)) {
                sets = a.setting_types();
                for n in (dict_keys(sets)) {
                    if (match_begin(name, n))
                        matches = [@matches, [this(), n]];
                }
            }
        }
    }
    return matches;
.

method del_setting
    arg name;
    var kid, method;
    
    .perms(sender(), 'manager);
    setting_types = dict_del(setting_types, name);
    method = (.get_setting_info(name))['del];
    .(method)(name);
    for kid in (.children())
        (| kid.(method)(name) |);
.

method is_anything
    arg [args];
    
    return 1;
.

method is_boolean
    arg spec, value, [args];
    
    if ((args[1]) == 'boolean) {
        if (value in [0, 1])
            return 1;
    }
    return 0;
.

method is_integer
    arg spec, value, args;
    
    if (type(value) == 'integer)
        return 1;
    if ((type(value) == 'string) && (value.is_numeric()))
        return 1;
    return 0;
.

method is_string
    arg spec, value, args;
    
    if (type(value) == 'string)
        return 1;
    return 0;
.

method setting_data
    return setting_data;
.

method get_inherited_setting
    arg spec, definer, args;
    var fname, a, sets;
    
    fname = ._to_fullname(spec, definer);
    if (fname in dict_keys(setting_data))
        return setting_data[fname];
    for a in (.ancestors()) {
        if ((a.has_ancestor($settings)) && (a.has_ancestor(definer)))
            sets = a.setting_data();
        if (fname in dict_keys(sets))
            return sets[fname];
    }
    return 0;
.

method get_indirect_setting
    arg name, definer, args;
    var fname, val;
    
    fname = ._to_fullname(name, definer);
    if (fname in dict_keys(setting_data)) {
        val = setting_data[fname];
        if (type(val) == 'dbref)
            return (setting_data[fname]).get_setting(spec, definer, args);
        else
            return setting_data[fname];
    } else {
        return (definer.setting_data())[fname];
    }
.

method default_get_setting
    arg name, definer, [args];
    
    catch ~keynf, ~methoderr {
        return (setting_data[definer])[name];
    } with handler {
        return 0;
    }
.

method default_set_setting
    arg name, definer, value, args;
    var sets;
    
    .perms(sender(), this());
    sets = (| setting_data[definer] |) || #[];
    sets = dict_add(sets, name, value);
    setting_data = dict_add(setting_data, definer, sets);
.

method del_direct_setting
    arg name;
    var definer_set;
    
    definer_set = (| setting_data[sender()] |) || 0;
    if (definer_set)
        definer_set = dict_del(definer_set, name) || definer_set;
    setting_data = dict_add(setting_data, sender(), definer_set);
.

method _del_setting
    var spec, definer;
    
    if (definer != sender())
        throw(~perm, "Only the definer of a setting can delete it.");
    setting_data = (| dict_del(setting_data, spec) |) || setting_data;
.

method _to_fullname
    arg spec, definer;
    
    return ((definer.dbref()) + ":") + spec;
.

method matching_settings
    arg name, [begin];
    var n, matches;
    
    //returns a list of all settings defined that match name
    // if begin is true, does a match begin instead of exact match
    if (setting_types) {
        matches = [];
        if (name == " ")
            return dict_keys(setting_types);
        for n in (dict_keys(setting_types)) {
            if ((name == n) || (begin && match_begin(n, name)))
                matches = [@matches, n];
        }
        return matches;
    }
    return [];
.

method all_matching_settings
    arg name, definers, [begin];
    var a, matches, local_matches;
    
    //returns a list of setting that match <name>
    //if begin is true, does a match_begin
    //if definers is not "", limit searches to definer
    if (type(definers) != 'list)
        throw(~type, "definers must be a list of dbrefs");
    matches = [];
    if (definers == []) {
        for a in (.ancestors()) {
            if (a.has_ancestor($has_settings)) {
                local_matches = a.matching_settings(name, begin);
                if (local_matches)
                    matches = [@matches, [a, local_matches]];
            }
        }
        return matches;
    }
    if (listlen(definers) > 1) {
        for a in (ancestors()) {
            if (union(a.ancestors(), definers)) {
                local_matches = a.matching_settings(name, begin);
                if (local_matches)
                    matches = [@matches, [a, local_matches]];
            }
        }
    } else {
        for a in (ancestors()) {
            if (a.has_ancestor(definers)) {
                local_matches = a.matching_settings(name, begin);
                if (local_matches)
                    matches = [@matches, [a, local_matches]];
            }
        }
    }
    return matches;
.

method setting
    arg name, [args];
    var info, definer;
    
    if (args) {
        definer = args[1];
        args = delete(args, 1);
    }
    if (!definer) {
        definer = ._find_definers(name, 1);
        if (definer)
            definer = definer[1];
        else
            return 0;
    
        //  throw(~definernf,"Could not find a definer for "+name);
    }
    info = definer.get_setting_info(name);
    if (args)
        args = [@info['get_args], @args];
    else
        args = info['get_args];
    args = ._build_args(args, #[['name, name], ['definer, definer]]);
    return .(info['get])(@args);
.

method default_setting_info
    return #[['type, "string"], ['check, 'is_anything], ['get, 'get_direct_setting], ['set, 'set_direct_setting], ['del, 'del_direct_setting], ['set_args, ['name, 'definer, 'value, 'style]], ['get_args, ['name, 'definer]]];
.

method setting_types
    return setting_types;
.

method get_direct_setting
    arg name, definer, [args];
    
    catch ~keynf, ~methoderr, ~type {
        return (setting_data[definer])[name];
    } with handler {
        return 0;
    }
.

method get_setting_info
    arg name;
    
    return (| setting_types[name] |) || (.default_setting_info());
.

method set_direct_setting
    arg name, definer, value, [args];
    var sets;
    
    .perms(sender(), this());
    if (!setting_data)
        setting_data = #[];
    sets = (| setting_data[definer] |) || #[];
    sets = dict_add(sets, name, value);
    setting_data = dict_add(setting_data, definer, sets);
.