/*    /daemon/seasons.c
 *    from the Dead Souls LPC Library
 *    handles game-based time
 *    created by Descartes of Borg 950508
 *    Version: @(#) seasons.c 1.7@(#)
 *    Last modified: 96/10/27
 */
#include <lib.h>
#include <cfg.h>
#include <function.h>
#include "include/seasons.h"
inherit LIB_DAEMON;
private static int CurrentDay, CurrentYear; 
private static int Dawn, Morning, Noon, Twilight, Night;
private static int ticktock;
private static string CurrentSeason, TimeOfDay;
private static mapping Moons;
private static class month CurrentMonth;
private static string *Days;
private static function *DawnCalls, *MorningCalls, *NoonCalls;
private static function *TwilightCalls, *NightCalls, *MidnightCalls;
private static class month *Months;
int eventTickTock(int tick){
    if(!tick) tick = 0;
    ticktock = tick;
    eventConfigure();
    return GetCurrentTime()/1200;
}
int GetTickTock(){ return ticktock; }
int *GetMudTime(){
    //return ({ GetHour(GetCurrentTime()), GetMinutes(GetCurrentTime()) });
    return ({ GetHour(time()), GetMinutes(time()) });
}
static void create() {
    string *lines;
    int i, maxi;
    daemon::create();
    DawnCalls = ({});
    MorningCalls = ({});
    NoonCalls = ({});
    TwilightCalls = ({});
    NightCalls = ({});
    MidnightCalls = ({});
    ticktock = 0;
    maxi = sizeof(lines = filter(explode(read_file(CFG_MONTHS), "\n"),
                (: $1 && $1 != "" && $1[0] != '#' :)));
    Months = allocate(maxi);
    for(i=0; i<maxi; i++) {
        Months[i] = new(class month);
        sscanf(lines[i], "%s:%s:%d:%d",((class month)Months[i])->Name,
                ((class month)Months[i])->Season,
                ((class month)Months[i])->Days,
                ((class month)Months[i])->DaylightHours);
    }
    Days = filter(explode(read_file(CFG_DAYS), "\n"),
            (: $1 && $1 != "" && $1[0] != '#' :));
    maxi = sizeof(lines = filter(explode(read_file(CFG_MOONS), "\n"),
                (: $1 && $1 != "" && $1[0] != '#' :)));
    Moons = allocate_mapping(maxi);
    for(i=0; i<maxi; i++) {
        string nom, id, desc, lnom;
        int phase;
        sscanf(lines[i], "%s:%d:%s:%s", nom, phase, id, desc);
        lnom = convert_name(nom);
        Moons[lnom] = new(class moon);
        ((class moon)Moons[lnom])->Name = nom;
        ((class moon)Moons[lnom])->Phase = phase;
        ((class moon)Moons[lnom])->Id = id;
        ((class moon)Moons[lnom])->Description = desc;
    }
    eventConfigure();
}
static void eventConfigure() {
    int i, x, days, tot, maxi;
    days = GetTime(time()) / (DAY_LENGTH * HOUR_LENGTH);
    for(tot=0, i=0, maxi = sizeof(Months); i<maxi; i++)
        tot += ((class month)Months[i])->Days;
    CurrentYear = days / tot + 1;
    days = (days % tot) + 1;
    for(i=0, maxi = sizeof(Months); i<maxi; i++) {
        if( days <= ((class month)Months[i])->Days ) {
            CurrentMonth = (class month)Months[i];
            CurrentSeason = ((class month)Months[i])->Season;
            CurrentDay = days;
            break;
        }
        else days -= ((class month)Months[i])->Days;
    }
    x = CurrentMonth->DaylightHours * HOUR_LENGTH;
    Morning = ((DAY_LENGTH * HOUR_LENGTH) - x) / 2;
    Twilight = Morning + x;
    Noon = (DAY_LENGTH * HOUR_LENGTH) / 2;
    if( Morning < HOUR_LENGTH ) {
        Dawn = Morning/2;
        Night = Twilight + Morning/2;
    }
    else {
        Dawn = Morning - HOUR_LENGTH;
        Night = Twilight + HOUR_LENGTH;
    }
    x = GetCurrentTime();
    if( x < Dawn ) {
        TimeOfDay = "night";
        call_out( (: eventDawn :), Dawn - x);
    }
    else if( x < Morning ) {
        TimeOfDay = "dawn";
        call_out( (: eventMorning :), Morning - x);
    }
    else if( x < Noon ) {
        TimeOfDay = "day";
        call_out( (: eventNoon :), Noon - x);
    }
    else if( x < Twilight ) {
        TimeOfDay = "day";
        call_out( (: eventTwilight :), Twilight - x);
    }
    else if( x < Night ) {
        TimeOfDay = "twilight";
        call_out( (: eventNight :), Night - x );
    }
    else {
        TimeOfDay = "night";
        call_out( (: eventMidnight :), (DAY_LENGTH * HOUR_LENGTH) - x);
    }
}
static void eventDawn() {
    object *obs;
    int i;
    call_out( (: eventMorning :), Morning - GetCurrentTime() );
    TimeOfDay = "dawn";
    obs = filter(users(), (: environment($1) &&
                environment($1)->GetClimateExposed() &&
                inherits(LIB_ROOM,environment($1)) &&
                !((int)environment($1)->GetProperty("no time")) :));
    message("environment",
            "%^YELLOW%^The sun appears just over the horizon.%^RESET%^",
            obs );
    i = sizeof(DawnCalls);
    while(i--) catch(evaluate(DawnCalls[i]));
}
static void eventMorning() {
    object *obs;
    int i;
    call_out( (: eventNoon :), Noon - GetCurrentTime());
    TimeOfDay = "day";
    obs = filter(users(), (: environment($1) &&
                (string)environment($1)->GetClimateExposed() &&
                inherits(LIB_ROOM,environment($1)) &&
                !((int)environment($1)->GetProperty("no time")) :));
    message("environment", "%^BOLD%^YELLOW%^The sun now shines completely "
            "on a new day.%^RESET%^", obs);
    i = sizeof(MorningCalls);
    while(i--) catch(evaluate(MorningCalls[i]));
}
static void eventNoon() {
    int i;
    call_out( (: eventTwilight :), Twilight - GetCurrentTime());
    TimeOfDay = "day";
    /* debug("Noon Time!"); */
    i = sizeof(NoonCalls);
    while(i--) catch(evaluate(NoonCalls[i]));
}
static void eventTwilight() {
    object *obs;
    int i;
    call_out( (: eventNight :), Night - GetCurrentTime() );
    TimeOfDay = "twilight";
    obs = filter(users(), (: environment($1) &&
                (string)environment($1)->GetClimateExposed() &&
                inherits(LIB_ROOM,environment($1)) &&
                !((int)environment($1)->GetProperty("no time")) :));
    message("environment", "%^CYAN%^The sun begins to fall away into "
            "twilight.%^RESET%^", obs);
    i = sizeof(TwilightCalls);
    while(i--) catch(evaluate(TwilightCalls[i]));
}
static void eventNight() {
    object *obs;
    int i,x;
    call_out( (: eventMidnight :),
            (DAY_LENGTH * HOUR_LENGTH) - GetCurrentTime() );
    TimeOfDay = "night";
    obs = filter(users(), (: environment($1) &&
                (string)environment($1)->GetClimateExposed() &&
                inherits(LIB_ROOM,environment($1)) &&
                !((int)environment($1)->GetProperty("no time")) :));
    message("environment",
            "%^BOLD%^BLUE%^Night darkens all that is real.%^RESET%^", obs);
    i = sizeof(NightCalls);
    while(i--){
        mixed f = NightCalls[i];
        if((x = functionp(f)) && !(x & FP_OWNER_DESTED)){
            catch(evaluate(f));
        }
    }
}
static void eventMidnight() {
    int i;
    CurrentDay++;
    i = CurrentMonth->Days;
    if( CurrentDay > i ) {
        int y;
        y = CurrentYear;
        eventConfigure();
        if( y != CurrentYear )
            message("shout", "Happy New Year!!!\nIt is now the year " +
                    GetYearString(CurrentYear) + "!!!!!", users());
        return;
    }
    call_out( (: eventDawn :), Dawn);
    TimeOfDay = "night";
    /* debug("midnight!"); */
    i = sizeof(MidnightCalls);
    while(i--) catch(evaluate(MidnightCalls[i]));
}
int GetCurrentDay() { return CurrentDay; }
string GetCurrentDayName() { return GetDayName(time()); }
string GetCurrentMonth() { return CurrentMonth->Name; }
string GetCurrentSeason() { return CurrentSeason; }
// get seconds from the start of the day.
int GetCurrentTime() { return GetTime(time()) % (DAY_LENGTH * HOUR_LENGTH) + ticktock; }
int GetCurrentYear() { return CurrentYear; }
varargs int GetDay(int x) {
    int tot, days, i, maxi;
    if(!x) x = time();
    days = absolute_value(GetTime(x) / (DAY_LENGTH * HOUR_LENGTH));
    for(tot=0, i=0, maxi = sizeof(Months); i<maxi; i++)
        tot += ((class month)Months[i])->Days;
    days = (days % tot) + ( (x < DAY_ONE) ? 0 : 1 );
    if( x < DAY_ONE ) {
        i = sizeof(Months);
        while(i--) {
            if( days < ((class month)Months[i])->Days )
                return ((class month)Months[i])->Days - days;
            else days -= ((class month)Months[i])->Days;
        }
        return 0;
    }
    for(i=0, maxi = sizeof(Months); i<maxi; i++) {
        if( days <= ((class month)Months[i])->Days ) return days;
        else days -= ((class month)Months[i])->Days;
    }
    return 0;
}
varargs string GetDayName(int x) {
    int days;
    if(!x) x = time();
    days = absolute_value(GetTime(x) / (DAY_LENGTH * HOUR_LENGTH));
    return Days[days % sizeof(Days)];
}
string *GetDays() { return Days + ({}); }
int GetDaylightHours(string mon) {
    int i;
    i = sizeof(Months);
    while(i--) {
        if( ((class month)Months[i])->Name == mon )
            return ((class month)Months[i])->DaylightHours;
    }
    return 0;
}
varargs int GetHour(int x) {
    int y;
    if(!x)
        x = time();
    y = absolute_value(GetTime(x));
    y = y % (DAY_LENGTH * HOUR_LENGTH);
    if( x < DAY_ONE ) y = (DAY_LENGTH * HOUR_LENGTH) - y;
    return (y / HOUR_LENGTH);
}
varargs int GetMinutes(int x) {
    int y;
    if(!x)
        x = time();
    y = absolute_value(GetTime(x));
    y = y % (DAY_LENGTH * HOUR_LENGTH);
    if( x < DAY_ONE ) y = (DAY_LENGTH * HOUR_LENGTH) - y;
    return (y % HOUR_LENGTH) / (HOUR_LENGTH/60);
}
string GetMonth(int x) {
    int monthIndex;
    monthIndex = GetMonthIndex(x);
    return Months[monthIndex]->Name;
}
private varargs int GetMonthIndex(int x) {
    int tot, days, i, maxi;
    if(!x) x = time();
    if( x < DAY_ONE ) days = (DAY_ONE - x) / (DAY_LENGTH * HOUR_LENGTH);
    else days = GetTime(x) / (DAY_LENGTH * HOUR_LENGTH);
    for(tot=0, i=0, maxi = sizeof(Months); i<maxi; i++)
        tot += ((class month)Months[i])->Days;
    days = (days % tot) + ((x >= DAY_ONE) ? 1 : 0);
    if( x < DAY_ONE ) {
        i = sizeof(Months);
        while(i--) {
            if( days < ((class month)Months[i])->Days )
                return i;
            else days -= ((class month)Months[i])->Days;
        }
        return 0;
    }
    for(i=0, maxi = sizeof(Months); i<maxi; i++) {
        if( days <= ((class month)Months[i])->Days )
            return i;
        else days -= ((class month)Months[i])->Days;
    }
    return 0;
}
string *GetMonths() {
    string *ret = ({});
    int i, maxi;
    for(i=0, maxi = sizeof(Months); i<maxi; i++)
        ret += ({ ((class month)Months[i])->Name });
    return ret;
}
string GetSeason(int x) {
    int monthIndex;
    monthIndex = GetMonthIndex(x);
    return Months[monthIndex]->Season;
}
varargs int GetTime(int x) { if(!x) x = time(); return (x - DAY_ONE); }
string GetTimeOfDay() { return TimeOfDay; }
function AddTimeEvent(string tod, function f) {
    switch(tod) {
        case "dawn": DawnCalls += ({ f }); break;
        case "morning": MorningCalls += ({ f }); break;
        case "noon": NoonCalls += ({ f }); break;
        case "twilight": TwilightCalls += ({ f }); break;
        case "night": NightCalls += ({ f }); break;
        case "midnight": MidnightCalls += ({ f }); break;
        default: return 0;
    }
    return f;
}
mapping GetTimeEvents() {
    return ([ "dawn" : DawnCalls + ({}), "morning" : MorningCalls + ({}),
            "noon" : NoonCalls + ({}), "twilight" : TwilightCalls + ({}),
            "night" : NightCalls + ({}), "midnight" : MidnightCalls + ({}) ]);
}
varargs int GetYear(int x) {
    int i, tot;
    if(!x) x = time();
    i = sizeof(Months);
    while(i--) tot += ((class month)Months[i])->Days;
    i = (GetTime(x) / (DAY_LENGTH * HOUR_LENGTH)) / tot;
    if( x < DAY_ONE ) return i;
    else return i + 1;
}
string GetYearString(int x) {
    if( x < 0 ) return (x * (-1)) + " BN";
    else return x + " NM";
}
/*
 * Moon functions
 */
string array GetMoons() {
    return map(keys(Moons), (: ((class moon)Moons[$1])->Name :));
}
string GetPhaseName(mixed val) {
    if( stringp(val) ) {
        val = GetPhase(val);
    }
    switch(val) {
        case 0: return "new";
        case 1: return "waxing";
        case 2: return "waning";
        case 3: return "full";
        default: return "error";
    }
}
int GetPhase(string m) {
    int x, y, z;
    m = convert_name(m);
    x = ((class moon)Moons[m])->Phase;
    y = GetTime(time()) % x;
    z = x / 4;
    return y / z;
}
int GetRadiantLight() {
    switch( TimeOfDay ) {
        case "night": return (GetMoonLight() * 2);
        case "day": return 60;
        default: return 30;
    }
}
int GetMoonLight() {
    string *moons;
    int i, y = 0;
    i = sizeof(moons = keys(Moons));
    while(i--) {
        int z;
        z = GetPhase(moons[i]);
        if( z == 4) z = 2;
        y += z;
    }
    return y;
}
string GetLong(string arg) {
    string array arr, mn;
    object env;
    string tmp;
    int i;
    if( !(env = environment(this_player())) )
        return "You are in serious trouble.";
    switch(arg) {
        case "sun":
            switch(GetTimeOfDay()) {
                case "dawn":
                    return "The sun is hanging low in the eastern sky.";
                case "day":
                    return "The sun is shining brightly in the daytime sky.";
                case "twilight":
                    return "The sun is sinking into the western sky.";
                case "night":
                    return "There is no sun to be seen.";
            }
        case "moon": case "moons":
            if( GetTimeOfDay() != "night" ) return "During the day?";
            else {
                string *moons;
                int x = 0;
                i = sizeof(moons = keys(Moons));
                while(i--) {
                    int y;
                    if( (y = GetPhase(moons[i])) == 0 ) continue;
                    else if( tmp )
                        tmp += (((class moon)Moons[moons[i]])->Name) +
                            " is " + GetPhaseName(y) + ".\n";
                    else tmp = "\n" +
                        "You gaze up at the sky, and see the moons...\n"+
                            (((class moon)Moons[moons[i]])->Name) +
                            " is " +
                            GetPhaseName(y) + ".\n";
                    x = 1;
                }
                if( !x ) return 0;
                else return tmp;
            }
        case "sky":
            if( GetTimeOfDay() == "night" ) {
                tmp =  GetLong("moon");
                if( !tmp ) {
                    return "The sky is filled only with the glitter of stars.";
                }
                else {
                    return tmp;
                }
            }
            else {
                string sky;
                tmp = GetLong("sun");
                if( this_player() ) {
                    env = environment(this_player());
                }
                if( sky = env->GetSky() ) {
                    env = find_object(sky);
                    if( env ) {
                        object array obs = filter(all_inventory(env),
                                (: living($1) &&
                                 !$1->GetInvis(this_player()) :));
                        /* function(object ob) { */
                        /* if( ob->GetInvis(this_player()) ) { */
                        /*     return 0; */
                        /* } */
                        /* if( living(ob) ) { */
                        /*     return 1; */
                        /* } */
                        /*     return 0; */
                        /* }); */
                        if( sizeof(obs) ) {
                            int maxi = sizeof(obs);
                            sky = obs[i]->GetName();
                            if( maxi == 1 ) {
                                sky += " is flying in the sky.";
                            }
                            else {
                                for(i=1; i<maxi; i++) {
                                    if( i == maxi-1 ) {
                                        if( maxi == 2 ) {
                                            sky += " and ";
                                        }
                                        else {
                                            sky += ", and ";
                                        }
                                    }
                                    else {
                                        sky += ", ";
                                    }
                                    sky += obs[i]->GetName();
                                }
                                sky += " are flying in the sky.";
                            }
                            tmp = sky + "\n" + tmp;
                        }
                    }
                }
                return tmp;
            }
        default:
        if( Moons[arg] ) return ((class moon)Moons[arg])->Description;
        arr = map(mn = keys(Moons), (: ((class moon)Moons[$1])->Id :));
        if( (i = member_array(arg, arr)) != -1 )
            return ((class moon)Moons[mn[i]])->Description;
        else return 0;
    }
}
int eventShow(object who, string args) {
    string str;
    if( !who || !sizeof(args) ) return 0;
    if( !str = GetLong(args) ) return 0;
    if( !(string)environment(who)->GetClimateExposed() ) {
        who->eventPrint("You can't see that from here!");
        return 1;
    }
    who->eventPrint(str);
    environment(who)->eventPrint((string)who->GetName() + " gazes toward the sky.", who);
    return 1;
}