/*    /daemon/events.c
 *    from Nightmare IV
 *    an event monitoring daemon, for call outs across time
 *    created by Descartes of Borg in late 1993
 */
#include <std.h>
#include <daemons.h>
#include <objects.h>
#include <clock.h>
#include <save.h>
#include <commands.h>
#include <rooms.h>
inherit DAEMON;
private int __RebootInterval;
private mapping __Events;
private static int __InReboot;
private static string __TimeOfDay;
private static mapping __Almanac;
private static function *__TimeChange;
static void Save();
void change_sky();
mapping almanac_data();
int query_minutes(int x);
int query_day(int x);
string query_day_name(int x);
int query_hour(int x);
int query_year(int x);
int query_month(int x);
string query_month_name(int x);
int query_now();
int query_week(int x);
string query_time_of_day();
void next_change();
void configure_day();
static void reboot();
void create() {
    daemon::create();
    set_no_clean(1);
    unguarded((: restore_object, SAVE_EVENTS :));
    if(!__RebootInterval) __RebootInterval = 24;
    if(!__Events) __Events = ([]);
    __TimeChange = ({});
    Save();
    configure_day();
    call_out("check_events", 60);
}
void set_reboot_interval(int x) {
    if(file_name(previous_object(0)) != CMD_SETREBOOT) return;
    if(x >1) __RebootInterval = x;
    Save();
}
varargs void add_events(object ob, string fun, int when, mixed *args, int reg) {
    if(file_name(previous_object()) != OB_SIMUL_EFUN) return;
    __Events[file_name(ob)] =
      (["time":time()+when, "function":fun, "regular":(reg?when:0), 
      "args":(pointerp(args) ? args : ({})) ]);
    Save();
}
static void check_events() {
    string *events;
    int i, x;
    call_out("check_events", 60);
    x = time();
    i = sizeof(events = keys(__Events));
    while(i--) {
        if(__Events[events[i]]["time"] <= x) {
            catch(call_other(events[i], __Events[events[i]]["function"],
              __Events[events[i]]["args"]));
            if(__Events[events[i]]["regular"] > 3599)
              __Events[events[i]]["time"] = x + __Events[events[i]]["regular"];
            else if(events[i] != file_name(this_object()))
              map_delete(__Events, events[i]);
        }
    }
    if(uptime() > __RebootInterval*3600 && !__InReboot) {
      __InReboot = 1;
        reboot();
    }
    Save();
}
static void Save() {
    unguarded((: save_object, SAVE_EVENTS :));
}
static void reboot() {
    catch(SHUT_D->reboot_mud(20));
    map_delete(__Events, file_name(this_object()));
    Save();
}
void configure_day() {
    int temps, x, y;
    __Almanac = almanac_data();
    y = query_minutes(temps = time());
    if((x=query_hour(temps)) < __Almanac["sunrise"][0]) __TimeOfDay = "night";
    else if(x==__Almanac["sunrise"][0]) {
        if(y < __Almanac["sunrise"][1]) __TimeOfDay = "night";
        else __TimeOfDay = "dawn";
    }
    else if(x < __Almanac["morning"][0]) __TimeOfDay = "dawn";
    else if(x == __Almanac["morning"][0]) {
        if(y < __Almanac["morning"][1]) __TimeOfDay = "dawn";
        else __TimeOfDay = "day";
    }
    else if(x < __Almanac["twilight"][0]) __TimeOfDay = "day";
    else if(x == __Almanac["twilight"][0]) {
        if(y < __Almanac["twilight"][1]) __TimeOfDay = "day";
        else __TimeOfDay = "twilight";
    }
    else if(x < __Almanac["sunset"][0]) __TimeOfDay = "twilight";
    else if(x == __Almanac["sunset"][0]) {
        if(y < __Almanac["sunset"][1]) __TimeOfDay = "twilight";
        else __TimeOfDay = "night";
    }
    else __TimeOfDay = "night";
    next_change();
}
void next_change() {
    int now, next, tot;
    string str;
    now = query_hour(time())*60 + query_minutes(time());
    switch(__TimeOfDay) {
        case "night":
            str = "sunrise";
            break;
        case "dawn":
            str = "morning";
            break;
        case "day":
            str = "twilight";
            break;
        case "twilight":
            str = "sunset";
            break;
    }
    next = (60*__Almanac[str][0]) + __Almanac[str][1];
    if(next > now) tot = next-now;
    else {
        now = (1200)-now;
        tot = next + now;
    }
    __Events[file_name(this_object())] =
      (["time":time()+(20*tot),"function":"change_sky","args":({}),"regular":0]);
}
void change_sky() {
    string msg;
    object *who;
    int i;
    switch(__TimeOfDay) {
        case "night":
            __TimeOfDay = "dawn";
            msg = "%^ORANGE%^The sun appears over the horizon of our reality.%^RESET%^";
            break;
        case "dawn":
            __TimeOfDay = "day";
            msg = "%^YELLOW%^The sun now shines completely on a new day.%^RESET%^";
            break;
        case "day":
            __TimeOfDay = "twilight";
           msg = "%^BOLD%^CYAN%^The sun begins to fall away into twilight.%^RESET%^";
            break;
        case "twilight":
            __TimeOfDay = "night";
            msg = "%^BOLD%^BLUE%^Night darkens the whole of our reality.%^RESET%^";
            break;
    }
    message("environment", msg, users());
    if(__TimeOfDay == "night") __Almanac = almanac_data();
    ASTRONOMY_D->set_moon_light();
    next_change();
    i = sizeof(__TimeChange);
    while(i--)
      if((int)master()->valid_function(__TimeChange[i]))
        catch((*__TimeChange[i])(__TimeOfDay));
}
mapping almanac_data() {
    int temps, days, x, morn, even, tmp, hour, min;
    mapping ret;
    if((days= ((query_month(temps = time())*20) + query_day(temps))) == 
      LONGEST_DAY)
        return (["sunrise":({4,0}),"morning":({5,0}),"sunset":({18,0}),
          "twilight":({17,0}) ]);
    else if(days == SHORTEST_DAY) 
      return (["sunrise":({6,0}),"morning":({7,0}),"sunset":({16,0}),
        "twilight":({15,0}) ]);
    else if(days > LONGEST_DAY) x = days-LONGEST_DAY;
    else x = days;
    tmp = 2*to_int(percent(x, LONGEST_DAY));
    if(days > LONGEST_DAY) {
        morn = 400+tmp;
        even = 1800-tmp;
    }
    else {
        morn = 600-tmp;
        even = 1600+tmp;
    }
    hour = to_int(("0"+morn)[0..1]);
    min = (to_int(("0"+morn)[2..3])*60)/100;
    ret = ([]);
    ret["sunrise"] = ({ hour, min });
    ret["morning"] = ({ hour+1, min });
    hour = to_int((even+"")[0..1]);
    min = (to_int((even+"")[2..3])*60)/100;
    ret["sunset"] = ({ hour, min });
    ret["twilight"] = ({ hour-1, min });
    return ret;
}
int query_minutes(int x) { return CURRENT_MINUTE(x); }
int query_day(int x) { return CURRENT_DAY(x)+1; }
string query_day_name(int x) { return DAYS[CURRENT_DAY(x)]; }
int query_hour(int x) { return CURRENT_HOUR(x); }
int query_year(int x) { return CURRENT_YEAR(x); }
int query_month(int x) { return CURRENT_MONTH(x)+1; }
string query_month_name(int x) { return MONTHS[(CURRENT_MONTH(x))]; }
int query_now() { return NOW(time()); }
int query_week(int x) { return CURRENT_WEEK(x)+1; }
string query_time_of_day() { return __TimeOfDay; }
int *query_sunrise() { return __Almanac["sunrise"]; }
int *query_sunset() { return __Almanac["sunset"]; }
int query_next_reboot() { return (time()-uptime())+(__RebootInterval*3600); }
mapping query_events() { return __Events; }
void sky_event(function f, int x) {
    if(base_name(previous_object()) != OB_SIMUL_EFUN) return;
    if(!x) __TimeChange = distinct_array(__TimeChange + ({ f }));
    else __TimeChange -= ({ f });
}
function *query_sky_events() { return copy(__TimeChange); }