//
// ukázka velmi jednoduchého diskrétního simulátoru v C++
//
//  - popis události je funkce
//  - kalendář je implementován standardní priority_queue<>
//

#include <iostream>
#include <queue>        // priority_queue
#include <cstdlib>      // exit()
using namespace std;

/////////////////////////////////////////////////////////////////////////////
// simulátor
/////////////////////////////////////////////////////////////////////////////

void error(const char *msg) {
    cerr << "ERROR: " << msg << endl;
    exit(1);
}

typedef void (*event_ptr_t) (); // ukazatel na funkci popisující událost

// Aktivační záznam události (pro jednoduchost bez priorit)
struct act_record {
    event_ptr_t event_ptr;      // odkaz na událost
    double atime;               // aktivační čas
    // konstruktor naplní položky:
    act_record(event_ptr_t e, double at): event_ptr(e), atime(at) {}
};

// Porovnání aktivačních záznamů z hlediska jejich pořadí v kalendáři.
// Je nutné pro uspořádání kalendáře typu priority_queue<act_record>
bool operator <(const act_record & a, const act_record & b)
{
    return a.atime > b.atime;  // POZOR HACK
    // porovnání je obráceně -- lze opravit, ale
    // musí se použít jiné než implicitní řazení u priority_queue
    // (menší čas ==> vyšší priorita při řazení)
}

// Jednoduchý kalendář událostí
// - na začátku je vždy aktivační záznam s nejmenším časem
priority_queue<act_record> calendar;

// Modelový čas
double Time;

// Plánování události na zadaný čas
void schedule(event_ptr_t event_ptr, double at)
{
    cout << "\t\t+Plánování na čas: " << at << endl;
    if (at < Time)
        error("Plánování do minulosti");
    act_record a(event_ptr, at);
    calendar.push(a);           // zařazení do kalendáře
}

/////////////////////////////////////////////////////////////////////////////
// Popis modelu
/////////////////////////////////////////////////////////////////////////////

void event1()
{
    // popis činnosti
    cout << "provedení popisu události event1" << endl;
    schedule(event1, Time + 100);       // plánuje další výskyt
}

void event2()
{
    // popis činnosti
    cout << "event2" << endl;
    schedule(event2, Time + 200);       // plánuje další výskyt
}

/////////////////////////////////////////////////////////////////////////////
// Popis simulace
/////////////////////////////////////////////////////////////////////////////

const double TSTART = 0;
const double TEND = 450;

int main()
{
    cout << endl;
    cout << "=============== inicializace" << endl;
    // while(!calendar.empty()) calendar.pop(); // inicializace kalendáře
    Time = TSTART;              // inicializace času
    cout << "Time = " << Time << endl;
    schedule(event1, Time);     // první aktivace
    schedule(event2, Time);     // první aktivace
    cout << endl;
    cout << "=============== začátek simulace:  čas = " << Time << endl;
    cout << endl;
    while (!calendar.empty()) {
        act_record a = calendar.top();  // přečteme první záznam
        calendar.pop();         // odstraníme první záznam
        if (a.atime > TEND) {
            Time = TEND;
            break;              // končíme simulaci
        }
        Time = a.atime;         // posuneme čas
        cout << "[Time = " << Time << "]: \t";
        a.event_ptr();          // provedeme událost
    }
    cout << endl;
    cout << "=============== konec simulace:  čas = " << Time << endl;
    cout << endl;
}