// Slawomir Kolowiecki TIZ nr albumu: 199193
//
//      Praca domowa na ZPR
//      Temat: boost::signals
//      Autor: Slawomir Kolowiecki
//      Grupa dziekanska/specjalnosc: TIZ
//

#include <boost/signals.hpp>
#include <iostream>
#include <vector>
//wykorzystywana tylko przy 'dynamicznych' polaczeniach
#include <boost/bind.hpp>


/// Poruszane kwestie:
///
///  PRZYKLAD 1. ->  kolejnosc wywolan slotow
///  PRZYKLAD 2. ->  pobieranie, zwracania argumentow oraz 
///                  przetwarzanie zwracanych argumentow przez sygnal 
///  PRZYKLAD 3. ->  usuwanie i blokowanie polaczen miedzy slotem a sygnalem 
///                  oraz scoping (tymczasowe/w ramach bloku polaczenia)
///  PRZYKLAD 4. ->  dynamiczne polaczenia


class Display
{
    int nr; int activ;
    static int obj_counter;
    public:
        Display() : activ(false) { obj_counter++; nr=obj_counter; }
        ~Display(){obj_counter--;}
        void activate() { activ=true; }
        // splot dla przykladu 2
        int operator()(float fl) {             
            std::cout<<"  argument:"<<fl<<std::endl;
            if(activ==true) return 1; else return 0; 
        }
        // splot dla przykladu 1
        void operator()() const { 
            std::cout << "  Display "<<nr<< std::endl;
        }
};
int Display::obj_counter=0;

// przykladowy "combiner" wartosci zwracanych przez sloty. Dzieki niemu mozemy 
// je przetwarzac, a wynik przetworzenia zwracac za pomoca wywolaniu sygnalu.
// 
// Ponizszy combiner,zwraca sume wartosci zwaracanych przez polaczone sloty:
template<typename T>
struct sum
{
    //mowi o typie zwracanym - ustalona skladnia: typedef typ_zwracany result_type;
    typedef T result_type;
    //obsluga wartosci zwracanych przez poszczegolne sloty.
    // UWAGA: slot(funkcja) zostanie wywolany tylko wtedy gdy 
    //        sie do niego odwolamy! poprzez iterator
    template<typename InputIterator> T operator()(InputIterator first,InputIterator last) const
    {
        //gdy brak slotow:
        if(first==last)
            return T();
        //gdy 1 slot
        T sum=*first;
        first++;
        //gdy wiecej slotow niz 1
        for(;first !=last;first++)
            sum+=*first;
        return sum;
    }
};

// klasa(moze to byc tez struktura), ktora implementuje interfejs trackable.
// Dzieki niemu mozliwe jest automatyczne rozlaczanie slotow i sygnalow.
class Controllpanel : public boost::signals::trackable
{
    std::vector<Display> displays;
    public:
        Controllpanel(int number){ 
            for(int i=0;i<number;i++) displays.push_back( *(new Display()) ); } 
    
        // metoda bedaca slotem (patrz Przyklad 4)
        void give_stats(int a) const {
            std::cout<<"  some stats about displays...: "<<displays.size()<<std::endl;
        }
};

//przykladowe funkcje (patrz Przyklad 3)
void show_pi()          { std::cout<<"    pi = 3.14"                <<std::endl; }
void show_ex_pi()       { std::cout<<"    exact pi = 3.147"         <<std::endl; }
void show_super_ex_pi() { std::cout<<"    super exact pi =3.147.."  <<std::endl; }

/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////

int main()
{

//////////
///  PRZYKLAD 1. ->  kolejnosc wywolan slotow
//////////
    
    std::cout<<"--> Przyklad 1 <--"<< std::endl;    
    //stworzenie sygnalu,ktory bedzie uruchamial funkcje zwracajace i pobierajce 'void'
    boost::signal<void (void)> simple_sig;
    //skladnia: signal< wartosc_zwracana_przez_slot (lista argumentow)>

    // przykladowe obiekty
    Display d1,d2,d3,d4,d5;
    // a) polaczenie slotow z sygnalem w dowolnej kolejnosci 
    //    => najmniejszy priorytet wywolania + FIFO (First In, First out).
    simple_sig.connect(d2);
    simple_sig.connect(d1);
    // kolejnosc wywolania bedzie zgodna z FIFO: d2,d1

    // b) polaczenie slotow przy uzyciu grup wywolan 
    //    UWAGA: to polaczenie ma wiekszy priorytet niz FIFO z podpunktu a) 
    //    Grupy: mniejszy numer grupy ma pierwszenstwo
    simple_sig.connect(2,d3);
    simple_sig.connect(1,d4);
    simple_sig.connect(1,d5);
    // kolejnosc wywolania dla podpunkty b) : d4,d5,d3
    
    //wywowalnie sygnalu
    simple_sig();
    //ostateczna kolejnosc wywolan: d4,d5,d3,d2,d1, poniewaz:
    //          najpierw polaczenia z podana grupa: d4,d5,d3,
    //          potem polaczenia bez zdefiniowanej grupy d2,d1


//////////
///  PRZYKLAD 2. ->  pobieranie, zwracania argumentow oraz 
///                  przetwarzanie zwracanych argumentow przez sygnal 
//////////

    std::cout<<"\n--> Przyklad 2 <--"<<std::endl;    
    //sygnal,ktory bedzie uruchamial funkcje pobierajaca vector<Display> oraz zwracajace int
    boost::signal<int (float),sum<int> > sig02;
    // skladnia: signal<wartosc_zwraca_przez_slot (argumenty slotu i sygnalu), combiner>
    // gdzie:    combiner - specjalna struktura ktora moze przetwarzac wynik.
    // Gdy brak combinera, to sygnal zwroci wartosc ostatniego wywolanego slotu

    //przykladowe obiekty
    Display d02_1,d02_2,d02_3,d02_4;

    // aktywowanie 3 przykladowych obiektow 
    d02_1.activate();
    d02_2.activate();
    d02_3.activate();  
    //polaczenie slotow z sygnalem
    sig02.connect(d02_1);
    sig02.connect(d02_2);
    sig02.connect(d02_3);
    sig02.connect(d02_4);

    //Wywolanie sygnalu, ktory zwroci obiekt typu podanego jako "resul_type".
    //=> W tym przykladzie zwroci liczbe 'aktywynych' obiektow Display, czyli 3
    //=> kazdy slot zostanie wywolany z argumentem podanym jako argument sygnalu
    std::cout<<"\n  Wynik sygnalu sig02: "<<sig02(110)<<std::endl;

//////////
///  PRZYKLAD 3. ->  usuwanie i blokowanie polaczen miedzy slotem a sygnalem 
///                  oraz scoping (tymczasowe/w ramach bloku polaczenia)
//////////
    
    std::cout<<"\n--> Przyklad 3 <--"<<std::endl;
    //przykladowy sygnal:
    boost::signal<void (void)> sig03;
    //blok 1.:
    {
        // tworzymy obiekt 'connection', 
        // bedacy reprezentacja polaczenie sygnalu z danym slotem:
        boost::signals::connection c1 = sig03.connect(&show_pi);
        
        // tworzymy obiekt 'scoped_connection', ktory oznacza, ze dane polaczenie
        // dziala tylko w danym bloku 1.
        boost::signals::scoped_connection c2=sig03.connect(&show_ex_pi);

        // zablokowanie polaczenia c1
        c1.block();
        std::cout<<"  Pierwsze wywolanie sygnalu w bloku 1.:\n";
        // wywoalnie sygnalu
        sig03();
        // wyswietli sie wynik show_ex_pi (tylko polaczenie c2 jest aktywne), 
        // poniewaz polaczenie c1 jest zablokowane, 
        // wiec slot show_pi nie zostanie uruchomiony

        // UWAGA funkcja connected zwroci prawde
        if (c1.connected()) {
            std::cout<<"\n  uwaga: polaczenie c1 nadal istnieje\n";
        }
        //odblokowanie polaczenie c1
        c1.unblock();
        std::cout<<"  Drugie wywolanie sygnalu w bloku 1.:\n";
        sig03();
        // wyswietli sie wynik show_pi() i show_ex_pi(),
        // oba polaczenie istnieja i sa aktywne
    }
    //koniec bloku 1. - polaczenie c2 przestaje istniec
    std::cout<<"\n  Wywolanie sygnalu po zakonczeniu sie bloku 1.:\n";
    sig03();
    //wyswietli sie wynik show_pi()

    //stworzenie nowego polaczenia:
    boost::signals::connection c3 = sig03.connect(&show_super_ex_pi);

    //sprawdzenie czy c3 jest polaczone
    if (c3.connected()) {
        std::cout<<"\n  => polaczenie c3 istnieje"<<std::endl;
    }
    // rozlaczenie polaczenia
    c3.disconnect();
    // sprawdzenie czy c3 jest polaczone zwroci false!!
    if (c3.connected()) {
        std::cout<<"  wiadomosc ktora sie nie wyswietli :)"<<std::endl;
    }
    std::cout<<"  Wywolanie sygnalu po rozlaczeniu c3"<<std::endl;
    sig03();
    // wyswietli sie wynik show_pi();

    /// inna metoda rozlaczania
    std::cout<<"\n  =========\n\n  Pokaz innej mozliwosci rozlaczania:\n"<<std::endl;;
    //nowe polaczenie 
    sig03.connect(&show_ex_pi);

    // rozlaczenie show_pi 
    // (do tego wykorzystywany jest operator ==, rozlaczony zostaje ten 
    // slot dla ktorego == zwroci prawde, czyli w tym przypadku gdy oba 
    // wskazniki na funkcje sa sobie rowne)
    sig03.disconnect(&show_pi);
    std::cout<<"  Wywolanie sig03, po wykonaniu disconnect(&show_pi"<<std::endl;
    sig03();
    //wyswietli wynik funkcji show_ex_pi()

//////////
///  PRZYKLAD 4. -> dynamiczne polaczenia
//////////

    std::cout<<"\n--> Przyklad 4 <--"<<std::endl;
    // sygnal pobierajacy int, zwracajcy void
    boost::signal<void (int)> sig04;

    // dynamiczny obiekt z ktorym bedize nawiazane polaczenie
    // (musi implementowac interfejs 'trackable'
    Controllpanel *pc=new Controllpanel(5);
    
    // dynamiczne polaczenie przy pomocy boost:bind
    sig04.connect(boost::bind(&Controllpanel::give_stats,pc,_1));
    
    std::cout<<"  Wywolanie sygnalu sig04:"<<std::endl;
    // wywolanie sygnalu
    sig04(8);
    // wyswietli wynik funkcji Controllpanel::give_stats(int)
    
    // zniszczenie obiektu: (nastepuje rowniez automatyczne przerwanie polaczenia)
    delete pc;
    std::cout<<"  Drugie wywolanie sygnalu sig04:"<<std::endl;
    //wywoalnie sygnalu
    sig04(4);
    std::cout<<std::endl;
    //wyswietli: nic, poniewaz brak polaczen :)
}
