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

var $root child_index 0
var $root owners [$scheduler]
var $root fertile 0
var $root inited 1
var $scheduler task_index 1
var $scheduler task_queue []
var $user password 0
var $user connected_at 0
var $user last_command_at 0
var $user connections []
var $user creation_time 758802496
var $user action ""
var $root owned [$scheduler]
var $root info []
var $root manager $scheduler
var $root writable [$scheduler]
var $root readable ['parameters, 'methods, 'code]
var $root dbref 'scheduler
var $scheduler sub_schedulers [$heart]

method remove_first_task
    var len, i, min;
    
    // sender must be an agent, or admin
    catch any {
        if (caller() != this())
            throw(~perm, "Invalid call to private method");
        len = listlen(task_queue);
        i = 1;
        while (i != len) {
            min = len;
            if (((i * 2) < len) && (((task_queue[i * 2])[2]) < ((task_queue[min])[2])))
                min = i * 2;
            if ((((i * 2) + 1) < len) && (((task_queue[(i * 2) + 1])[2]) < ((task_queue[min])[2])))
                min = (i * 2) + 1;
            task_queue = replace(task_queue, i, task_queue[min]);
            i = min;
        }
        task_queue = sublist(task_queue, 1, len - 1);
    } with handler {
        // $sys.log(traceback());
    }
    if (!task_queue)
        task_index = 1;
.

method task_queue
    // sender must be system, for now
    .perms(sender(), 'system);
    return task_queue;
.

method del_task
    arg tid;
    var task;
    
    // sender must be system, for now.
    .perms(sender(), 'system);
    if (type(tid) != 'integer)
        throw(~type, "Task Identification must be an integer");
    for task in (task_queue) {
        if ((task[1]) == tid) {
            task_queue = delete(task_queue, task in task_queue);
            return 1;
        }
    }
    throw(~tasknf, "No task found by that TID");
.

method add_task
    arg time, method, [args];
    var task, i, tid, flags;
    
    // use `@info $scheduler' for more information. 
    // [tid, time, time(), sender(), caller(), method, flags, args]
    // time is seconds from insertion time that it will be run (ie 300 == 5 mins)
    //
    if ((type(time) != 'integer) || ((type(method) != 'symbol) || (type(args) != 'list)))
        throw(~type, "Arguments are not an integer, symbol, and list.");
    if (time < 1)
        throw(~time, "Time is negative.");
    if (time > 31536000)
        throw(~time, "Try to schedule a task LESS than a year from now?");
    
    //
    task_index = task_index + 1;
    tid = task_index;
    
    // flags can be set in a system only add_task, for now set them as 'system
    flags = ['system];
    task = [tid, time, time(), sender(), caller(), method, flags, args];
    task_queue = task_queue + [task];
    i = listlen(task_queue);
    
    //
    while ((i > 1) && ((task[2]) < (((task_queue[i / 2])[2]) + ((task_queue[i / 2])[3])))) {
        task_queue = replace(task_queue, i, task_queue[i / 2]);
        i = i / 2;
    }
    task_queue = replace(task_queue, i, task);
.

method pulse
    var task, sub;
    
    // called by $sys.heartbeat
    if (caller() != $sys)
        throw(~perm, "Sender is not system");
    while (task_queue && (time() > (((task_queue[1])[2]) + ((task_queue[1])[3])))) {
        task = task_queue[1];
        (| ._run_task(task) |);
        .remove_first_task();
    }
    
    // call sub schedulers 
    for sub in (sub_schedulers)
        (| sub.pulse() |);
.

method _run_task
    arg task;
    var code, args, a;
    
    // called by $scheduler only
    if (caller() != this())
        throw(~perm, "Caller is not this");
    catch any {
        // setup the args by hand, becuase we use eval and it expects a string
        args = task[8];
        if (args) {
            for a in [1 .. listlen(args)]
                args = replace(args, a, $data.unparse(args[a]));
            args = $list.to_string(args, ",");
        } else {
            args = "";
        }
        code = ((("." + tostr(task[6])) + "(") + args) + ");";
    
        // run it through eval as the sender():
        (task[5]).eval([code], task[4]);
    } with handler {
        // bounce the errors to the person, or to the system board if it's 'system
        //catch any {
        (| (task[4]).tell(["SCHEDULER ERROR: ", @traceback()]) |);
    
        //  } with handler {
        //  if ('system in task[7]) {
        // $channels.announce('System, traceback()[1]);
        // $channels.announce('System, "task: " + $data.unparse(task));
        // }
        //  }
    }
.

method sys_add_task
    arg time, method, sender, caller, flags, [args];
    var task, i, tid;
    
    // use `@info $scheduler' for more information. 
    // [tid, time, time(), sender(), caller(), method, flags, args]
    //
    if (!($sys.is_agent(sender())))
        throw(~perm, "Sender is not an agent or admin, use .add_task()");
    if ((type(time) != 'integer) || ((type(method) != 'symbol) || (type(args) != 'list)))
        throw(~type, "Arguments are not an integer, symbol, and list.");
    if (time < 1)
        throw(~time, "Time is negative.");
    if (time > 31536000)
        throw(~time, "Try to schedule a task LESS than a year from now?");
    if (!valid(sender))
        throw(~type, "The argument for sender is not a valid object");
    if (!valid(caller))
        throw(~type, "The argument for caller is not a valid object");
    if (type(flags) != 'list)
        throw(~type, "Send flags as a list of symbols");
    
    //
    task_index = task_index + 1;
    tid = task_index;
    
    // flags can be set in a system only add_task, for now set them as 'system
    task = [tid, time, time(), sender, caller, method, flags, args];
    task_queue = task_queue + [task];
    
    //
    i = listlen(task_queue);
    while ((i > 1) && ((task[2]) < (((task_queue[i / 2])[2]) + ((task_queue[i / 2])[3])))) {
        task_queue = replace(task_queue, i, task_queue[i / 2]);
        i = i / 2;
    }
    task_queue = replace(task_queue, i, task);
.

method add_sub_scheduler
    arg object;
    
    if (!($sys.is_admin(sender())))
        throw(~perm, "Only admins may add sub schedulers.");
    if (type(object) != 'dbref)
        throw(~type, "Object must be a dbref.");
    sub_schedulers = [@sub_schedulers, object];
.

method del_sub_scheduler
    arg object;
    var pos, s;
    
    if (!($sys.is_admin(sender())))
        throw(~perm, "Only admins may delete sub schedulers.");
    if (type(object) != 'dbref)
        throw(~type, "Object must be a dbref.");
    pos = object in sub_schedulers;
    if (!pos)
        throw(~objnf, "Object not a sub schedulers.");
    s = [];
    if (pos > 1)
        s = [@s, sublist(sub_schedulers, 1, pos - 1)];
    if (s < listlen(sub_schedulers))
        s = [@s, sublist(sub_schedulers, pos + 1)];
    sub_schedulers = s;
.