/*
 *   Kalendarz Imperialny
 *
 *   /d/Wiz/silvathraec/imp/kalendarz.c
 *
 *   dzielo Silvathraeca 1997
 *
 *   Na podstawie oficjalnych materialow Games Workshop, oraz czesciowo
 *   kalendarza Lorda Elessara <@Genesis>.
 */

#pragma no_clone
#pragma no_inherit
#pragma strict_types
#pragma save_binary

inherit "/std/object";

#include <debug.h>
#include <macros.h>
#include <stdproperties.h>

#define REALTOGAME  120.0       /* godzina Imperialna w sekundach */
#define SAVETIME    1800.0      /* sekundy miedzy nagraniami stanu zegara */
                                /* (pol godziny czasu rzeczywistego) */

#define STAN_ZEGARA             "/d/Imperium/std/room/stan_zegara"
#define IMPERIUM_STD_ROOM       "/d/Imperium/std/room"
#define IMPERIUM_ALARM          "/d/Imperium/obj/alarm"

#define POCZATKOWY_DZIEN_TYGODNIA   7   /* Angestag. Wybralem go w zwiazku */
                                        /* z moimi niewinnymi skojarzeniami */
                                        /* z sabatem... :) W ten dzien */
                                        /* przypadnie bowiem Hexennacht i */
                                        /* Geheimnisnacht. */

#define ROWNONOC_WIOSENNA       67      /* Mitterfruhl */
#define PRZESILENIE_LETNIE      167     /* Sonnenstill */
#define ROWNONOC_JESIENNA       267     /* Mitterherbst */
#define PRZESILENIE_ZIMOWE      367     /* Mondstill */

#define POCZATEK_WIOSNY         18      /* 17. Nachhexen */
#define POCZATEK_LATA           118     /* 18. Sigmarszeit */
#define POCZATEK_JESIENI        218     /* 17. Nachgeheim */
#define POCZATEK_ZIMY           318     /* 17. Nachhexen */

#define CYKL_MANNSLIEBA         25      /* Pierwsza pelnia - Hexennacht. */
#define PELNIA                  1       /* 2 dni w pelni */
#define PO_PELNI                3       /* 2 dni */
#define PIERWSZA_KWADRA         5       /* 6 dni w pierwszej kwadrze */
#define PRZED_NOWIEM            11      /* 3 dni */
#define NOW                     14      /* 1 dzien w nowiu */
#define PO_NOWIU                15      /* 3 dni */
#define OSTATNIA_KWADRA         18      /* 6 dni w ostatniej kwadrze */
#define PRZED_PELNIA            24      /* 2 dni */

#define ROWNONOC_WSCHOD 6
#define ROWNONOC_ZACHOD 19
#define ODCHYLENIE_WSCHODU_ZACHODU 2

#define IMPERIUM_WSCHOD_LATEM 4         /* Dawn at 5:00 and dusk at 21 */
#define IMPERIUM_ZACHOD_LATEM 21        /* Do zmiany */
#define IMPERIUM_WSCHOD_ZIMA 8          /* Dawn at 7:00 and dusk at 19 */
#define IMPERIUM_ZACHOD_ZIMA 17
#define DLUGOSC_SWITU 2                 /* w godzinach */
#define DLUGOSC_ZMIERZCHU 2

#define GODZINY             24
#define POLUDNIE            12
#define DLUGOSC_TYGODNIA    8
#define DLUGOSC_MIESIACA    dlugosci_miesiecy[miesiac - 1]
#define DLUGOSC_ROKU        400     /* suma dlugosci miesiecy */
#define MIESIACE            18      /* Tak naprawde jest ich 12, ale mamy */
                                    /* dodatkowo 6 dni specjalnych. */

static string *dni_tygodnia = ({"Wallentag", "Aubentag", "Markttag",
                                "Baeckertag", "Bezahltag", "Koenigstag",
                                "Angestag", "Festtag"});
static string *nazwy_miesiecy = ({"Hexentag", "Nachhexen", "Jahrdrung",
                                  "Mitterfruhl", "Pflugzeit", "Sigmarszeit",
                                  "Sommerzeit", "Sonnenstill", "Vorgeheim",
                                  "Geheimnistag", "Nachgeheim", "Erntezeit",
                                  "Mitterherbst", "Brauzeit", "Kaltezeit",
                                  "Ulrichszeit", "Mondstill", "Vorhexen"});
static int *dlugosci_miesiecy = ({1, 32, 33, 1, 33, 33, 33, 1, 33,
                                  1, 32, 33, 1, 33, 33, 33, 1, 33});

static int godzina_wschodu,
           godzina_zachodu,
           godzina_12;

static string pora_dnia,
              pora_dnia_b,
              dzien_noc,
              pora_roku,
              faza_mannslieba,
              polozenie_mannslieba;

int dzien_roku,
    dzien_tygodnia,
    miesiac,
    dzien_miesiaca,
    godzina;

/* Prototyp. */

static void wschod_zachod(string fun);

static void
oblicz_pory_dnia()
{
    if (godzina < godzina_wschodu - DLUGOSC_SWITU)
    {
        pora_dnia = "noc";
        pora_dnia_b = "w nocy";
        dzien_noc = "noc";
    }
    else if (godzina < godzina_wschodu)
    {
        pora_dnia = "swit";
        pora_dnia_b = "nad ranem, przed wschodem slonca";
        dzien_noc = "noc";
    }
    else if (godzina < godzina_wschodu + DLUGOSC_SWITU)
    {
        pora_dnia = "wczesny ranek";
        pora_dnia_b = "nad ranem, po wschodzie slonca";
        dzien_noc = "dzien";
    }
    else if (godzina < POLUDNIE)
    {
        pora_dnia = "ranek";
        pora_dnia_b = "rano";
        dzien_noc = "dzien";
    }
    else if (godzina == POLUDNIE)
    {
        pora_dnia = "poludnie";
        pora_dnia_b = "w poludnie";
        dzien_noc = "dzien";
    }
    else if (godzina < godzina_zachodu - DLUGOSC_ZMIERZCHU)
    {
        pora_dnia = "popoludnie";
        pora_dnia_b = "po poludniu";
        dzien_noc = "dzien";
    }
    else if (godzina < godzina_zachodu)
    {
        pora_dnia = "wieczor";
        pora_dnia_b = "wieczorem";
        dzien_noc = "dzien";
    }
    else if (godzina < godzina_zachodu + DLUGOSC_ZMIERZCHU)
    {
        pora_dnia = "pozny wieczor";
        pora_dnia_b = "poznym wieczorem, po zachodzie slonca";
        dzien_noc = "noc";
    }
    else
    {
        pora_dnia = "noc";
        pora_dnia_b = "w nocy";
        dzien_noc = "noc";
    }
}

static void
oblicz_wschod_zachod()
{
    godzina_wschodu = ROWNONOC_WSCHOD;				/* DO ZMIANY */
    godzina_zachodu = ROWNONOC_ZACHOD;
}

static void
oblicz_pore_roku()
{
    if (dzien_roku < POCZATEK_WIOSNY)
        pora_roku = "zima";
    else if (dzien_roku < POCZATEK_LATA)
        pora_roku = "wiosna";
    else if (dzien_roku < POCZATEK_JESIENI)
        pora_roku = "lato";
    else if (dzien_roku < POCZATEK_ZIMY)
        pora_roku = "jesien";
    else
        pora_roku = "zima";
}

static void
oblicz_faze_mannslieba()
{
    int dzien = dzien_roku % CYKL_MANNSLIEBA;

    if (dzien < PELNIA)
        faza_mannslieba = "przed pelnia";
    else if (dzien < PO_PELNI)
        faza_mannslieba = "pelnia";
    else if (dzien < PIERWSZA_KWADRA)
        faza_mannslieba = "po pelni";
    else if (dzien < PRZED_NOWIEM)
        faza_mannslieba = "pierwsza kwadra";
    else if (dzien < NOW)
        faza_mannslieba = "przed nowiem";
    else if (dzien < PO_NOWIU)
        faza_mannslieba = "now";
    else if (dzien < OSTATNIA_KWADRA)
        faza_mannslieba = "po nowiu";
    else if (dzien < PRZED_PELNIA)
        faza_mannslieba = "ostatnia kwadra";
    else
        faza_mannslieba = "przed pelnia";
}

static void
oblicz_polozenie_mannslieba()
{
    int azymut = (POLUDNIE + godzina - POLUDNIE * (2 * dzien_roku - 
                  PELNIA - PO_PELNI) % CYKL_MANNSLIEBA) % GODZINY;

    if (azymut < ROWNONOC_WSCHOD)
        polozenie_mannslieba = "pod horyzontem";
    else if (azymut < ROWNONOC_WSCHOD + 2)
        polozenie_mannslieba = "wschod";
    else if (azymut < POLUDNIE - 1)
        polozenie_mannslieba = "poludniowy wschod";
    else if (azymut < POLUDNIE + 1)
        polozenie_mannslieba = "poludnie";
    else if (azymut < ROWNONOC_ZACHOD - 2)
        polozenie_mannslieba = "poludniowy zachod";
    else if (azymut < ROWNONOC_ZACHOD)
        polozenie_mannslieba = "zachod";
    else
        polozenie_mannslieba = "pod horyzontem";
}

static void
reset_data(string szczegoly)
{
    log_file("zegar", ctime(time()) + ": RESET: " + szczegoly + ".\n");

    dzien_roku = 1;
    dzien_tygodnia = POCZATKOWY_DZIEN_TYGODNIA;
    miesiac = 1;
    dzien_miesiaca = 1;
    godzina = 0;
}

static void
debug_data()
{
    int numer_miesiaca = 0,
        dzien = 0;

    if (dzien_roku < 1 || dzien_roku > DLUGOSC_ROKU)
        return reset_data("dzien_roku = " + dzien_roku);
    if (dzien_tygodnia < 1 || dzien_tygodnia > DLUGOSC_TYGODNIA)
        return reset_data("dzien_tygodnia = " + dzien_tygodnia);
    if (miesiac < 1 || miesiac > MIESIACE)
        return reset_data("miesiac = " + miesiac);
    if (dzien_miesiaca < 1 || dzien_miesiaca > DLUGOSC_MIESIACA)
        return reset_data("miesiac = " + miesiac + ", dzien_miesiaca = "
                        + dzien_miesiaca);
    if (godzina < 0 || godzina >= GODZINY)
        return reset_data("godzina = " + godzina);
    if ((POCZATKOWY_DZIEN_TYGODNIA + dzien_roku - 1 - dzien_tygodnia) %
        DLUGOSC_TYGODNIA)
        return reset_data("dzien_roku = " + dzien_roku + ", dzien_tygodnia = "
                        + dzien_tygodnia);

    while (++numer_miesiaca < miesiac)
        dzien += dlugosci_miesiecy[numer_miesiaca - 1];

    if (dzien + dzien_miesiaca != dzien_roku)
        return reset_data("dzien_roku = " + dzien_roku + ", miesiac = "
                        + miesiac + ", dzien miesiaca = " + dzien_miesiaca);
}

public void
create_object()
{
    ustaw_shorty(({"Kalendarz Imperialny", "Kalendarza Imperialnego",
                   "Kalendarzowi Imperialnemu", "Kalendarz Imperialny",
                   "Kalendarzem Imperialnym", "Kalendarzu Imperialnym"}),
                   allocate(6), PL_MESKI_NZYW);

    seteuid(getuid());
    restore_object(STAN_ZEGARA);

    /* Resetujemy zegar, jesli zachodzi potrzeba. */
    debug_data();

    godzina_12 = (godzina % POLUDNIE ? godzina % POLUDNIE : POLUDNIE);
    oblicz_wschod_zachod();
    oblicz_pory_dnia();
    oblicz_pore_roku();
    oblicz_faze_mannslieba();
    oblicz_polozenie_mannslieba();

    log_file("zegar", ctime(time()) + ": START: "
           + this_object()->zwroc_date() + ".\n");

    set_alarm(REALTOGAME, REALTOGAME, "update_time");
    set_alarm(0.0, SAVETIME, "nagraj_stan_zegara");
}

static void
external_alarm(string pora)
{
    pora_dnia = pora;
    IMPERIUM_ALARM->czas(pora);
}

public void
update_time()
{
    string stara_pora_dnia = pora_dnia;

    godzina++;
    oblicz_pory_dnia();
    oblicz_polozenie_mannslieba();
    if (godzina_12++ == POLUDNIE)
        godzina_12 = 1;
    if (godzina == godzina_wschodu)
        wschod_zachod("wschod_slonca");
    if (godzina == godzina_zachodu)
        wschod_zachod("zachod_slonca");
    if (godzina == GODZINY)
    {
        godzina = 0;
        dzien_roku++;
        oblicz_wschod_zachod();
        oblicz_pore_roku();
        oblicz_faze_mannslieba();
        if (dzien_tygodnia++ == DLUGOSC_TYGODNIA)
            dzien_tygodnia = 1;
        if (dzien_miesiaca++ == DLUGOSC_MIESIACA)
        { 
            dzien_miesiaca = 1;
            if (miesiac++ == MIESIACE)
            {
                miesiac = 1;
                dzien_roku = 1;
            }
        }
        external_alarm("polnoc");
    }
    if (stara_pora_dnia != pora_dnia)
        external_alarm(pora_dnia);
}

public void
nagraj_stan_zegara()
{
    save_object(STAN_ZEGARA);
}

public void
remove_object()
{
    nagraj_stan_zegara();
    if (this_interactive() &&
        this_interactive()->query_real_name() != "silvathraec")
        log_file("destruct", ctime(time()) + ": ti["
               + this_interactive()->query_real_name() + "] -> ZEGAR\n"
               + DEBUG_FMT_CALLS(1));
    ::remove_object();
}

public int
query_prevent_shadow()
{
    return 1;
}

/* FUNKCJE ZWRACAJACE */

public int
zwroc_godzine()
{ 
    return godzina;
}

public int
zwroc_godzine_12()
{
    return godzina_12;
}

public string
zwroc_pore_dnia()
{
    return pora_dnia;
}

public string
zwroc_pore_dnia_b()
{
    return pora_dnia_b;
}

public string
zwroc_dzien_noc()
{
    return dzien_noc;
}

public int
zwroc_dzien_roku()
{
    return dzien_roku;
}

public string
zwroc_pore_roku()
{
    return pora_roku;
}

public string
zwroc_dzien_tygodnia()
{
    return dni_tygodnia[dzien_tygodnia - 1];
}

public int
zwroc_dzien_miesiaca()
{ 
    return dzien_miesiaca;
}

public string
zwroc_miesiac()
{
    return nazwy_miesiecy[miesiac - 1];
}

public int
czy_prawdziwy_miesiac(int numer = miesiac)
{
    return (dlugosci_miesiecy[numer - 1] > 1);
}

public string
zwroc_krotka_date()
{ 
    if (czy_prawdziwy_miesiac())
        return dzien_miesiaca + " " + zwroc_miesiac();
    return zwroc_miesiac();
}

public string
zwroc_date()
{ 
    return zwroc_dzien_tygodnia() + ", " + zwroc_krotka_date();
}

public string
zwroc_dluga_date()
{
    if (czy_prawdziwy_miesiac())
        return zwroc_dzien_tygodnia() + ", "
             + LANG_SORD(dzien_miesiaca, PL_MIA, PL_MESKI_NZYW) + " dzien "
             + "miesiaca " + zwroc_miesiac();
    return zwroc_dzien_tygodnia() + ", dzien " + zwroc_miesiac();
}

public void
sprawdz_date()
{ 
    write("Jest godzina " + godzina_12 + " " + pora_dnia_b + " w "
        + zwroc_date() + " wedlug Kalendarza Imperialnego.\n");
}

public string
zwroc_faze_mannslieba()
{
    return faza_mannslieba;
}

public string
zwroc_polozenie_mannslieba()
{
    return polozenie_mannslieba;
}

int
godzina_switu(int dzien = dzien_roku)
{
/*  return itof(ROWNONOC_SWIT) + sin(0.01570796 * itof(dzien - ROWNONOC_WIOSENNA));
*/
/*  if (dzien < POCZATEK_WIOSNY)
        return("zima");
    if (dzien < POCZATEK_LATA)
        return "wiosna";
    if (dzien < POCZATEK_JESIENI)
        return "lato";
    if (dzien < POCZATEK_ZIMY)
        return "jesien";
    return "zima";
    if (dzien > PRZESILENIE_ZIMOWE) dzien += now + MONTHS * DAYS;
    
    if(now < summer_midth) {
    now = now - winter_midth;
    return DAWN_WINTER - (now * (DAWN_WINTER - DAWN_SUMMER + 1)) /
           (summer_midth - winter_midth + 1);
    }
    else {
    now = now - summer_midth;
    return DAWN_SUMMER + (now * (DAWN_WINTER - DAWN_SUMMER + 1)) /
           (winter_midth2 - summer_midth + 1);
    }*/ return 6;
}

int
godzina_zmierzchu(int dzien = dzien_roku)
{
    int val1, val2, now;
/*
    if(m) val1 = m;
    else val1 = month;
    if(d) val2 = d;
    else val2 = day;
    now = convert_months_days(val1,val2);
    if(now < winter_midth) now = now + MONTHS * DAYS;
    
    if(now < summer_midth) {
    now = now - winter_midth;
    return DUSK_WINTER + (now * (DUSK_SUMMER - DUSK_WINTER + 1)) /
           (summer_midth - winter_midth + 1);
    }
    else {
    now = now - summer_midth;
    return DUSK_SUMMER - (now * (DUSK_SUMMER - DUSK_WINTER + 1)) /
           (winter_midth2 - summer_midth + 1);
    }*/ return 18;
}

static void
brak_inherita(object gracz)
{
    gracz->catch_msg("Rozgladajac sie uwaznie po swoim otoczeniu, masz "
                   + "jakies niejasne wrazenie ze czegos tu brakuje... Tylko "
                   + "czego...? Ta mysl w pierwszej chwili odrzucasz, jako "
                   + "zbyt absurdalna, jednak potworna prawda przebija sie "
                   + "wreszcie do twojej swiadomosci: Miejsce, w ktorym sie "
                   + "znajdujesz, nie dziedziczy standardowej lokacji "
                   + "Imperium!\n");
    if (!gracz->query_wiz_level())
        gracz->catch_msg("Arkadia bedzie ci wdzieczna, jesli zglosisz "
                       + "blad, " + gracz->query_wolacz() + ".\n");
}

static int
czy_w_imperium(object room)
{
    return room && function_exists("create_room", room) == IMPERIUM_STD_ROOM;
}

#define ROWNIEJSI_NIZ_ROWNI(gracz, env) \
            (function_exists("create_room", env) == \
                 "/d/Arkadia/statki/poklad")

static void
wschod_zachod(string fun)
{
    object *gracze = users();
    int n = -1;
    int ilu = sizeof(gracze);
    object env;
    mixed error;

    while (++n < ilu)
    {
        env = environment(gracze[n]);
        if (czy_w_imperium(env))
        {
            if (error = catch(call_other(env, fun, gracze[n])))
                log_file("zegar", ctime(time()) + ": " + file_name(env) + "->"
                                + fun + "(): " + error);
        }
        else if (env->query_domain() == "Imperium" &&
                 !ROWNIEJSI_NIZ_ROWNI(gracze[n], env))
            brak_inherita(gracze[n]);
    }
}
