
//  This example illustrates the use of threaded ProtoDispatcher operation
//  A "Ticker" is started in separate thread that has a timer that fires off 
//  once per second while the main thread goes about its business (sleeping
//  in 10 second chunks).  The Ticker::Stop() method calls the 
//  ProtoDispatcher::Stop() method which destroys the dispatcher thread
//  Alternatively, the Ticker::timer could have been Deactivated without
//  touch the dispatcher thread, but a new dispatcher thread can be 
//  instantiated by calling ProtoDispatcher::StartThread() ... so there
//  is flexibility here.
//
//  When using a threaded ProtoDispatcher, make sure to call SuspendThread()
//  before manipulating dispatcher objects.  (See Ticker::StartTimer() and
//  Ticker::StopTimer() as an example)

#include "protokit.h"

#ifdef UNIX
#include <unistd.h>  // for sleep() function
#endif // UNIX

class Ticker
{
    public:
        Ticker();
        ~Ticker();
        
        bool Start();
        void Stop();
        
        bool StartTimer()
        {
            bool result = dispatcher.SuspendThread();
            if (result && !timer.IsActive()) 
                dispatcher.ActivateTimer(timer); 
            dispatcher.ResumeThread();
            return result;  
        }
        bool StopTimer()
        {
            bool result = dispatcher.SuspendThread();
            if (result && timer.IsActive()) 
                timer.Deactivate(); 
            dispatcher.ResumeThread();
            return result;
        }
          
    private:
        bool OnTimeout(ProtoTimer& theTimer);
        ProtoDispatcher dispatcher;
        ProtoTimer timer;   
};  // end class Ticker

Ticker::Ticker()
{
    timer.SetListener(this, &Ticker::OnTimeout);
    timer.SetInterval(1.0);
    timer.SetRepeat(-1);   
}

Ticker::~Ticker()
{
    Stop();   
}

bool Ticker::Start()
{
    dispatcher.ActivateTimer(timer);
    if (dispatcher.StartThread())
    {
        return true;
    }
    else
    {
        DMSG(0, "threadExample: Ticker::Start() thread start failure ...\n");
        timer.Deactivate();
        return false;
    }      
}  // end  Ticker::Start()

void Ticker::Stop()
{
    dispatcher.Stop();
    if (timer.IsActive()) timer.Deactivate();
}  // end Ticker::Stop()

bool Ticker::OnTimeout(ProtoTimer& theTimer)
{
    // IMPORTANT NOTE: Remember this timeout handler
    // is being called by the dispatcher thread, _not_
    // the main thread since we're using ProtoDispatcher::StartThread()
    // to run the dispatcher
    DMSG(0, "threadExample: Ticker::OnTimeout() ...\n");
    return true;
}  // end Ticker::OnTimeout()

int main(int argc, char* argv[])
{
    Ticker ticker;
    
    if (!ticker.Start())
    {
        DMSG(0, "threadExample: Ticker::Start() error\n");
        return -1;   
    }
    
    int count = 2;
    while (count--)
    {
#ifdef WIN32
        Sleep(10000);
#else
        sleep(10);
#endif  // if/else WIN32
        DMSG(0, "threadExample: 10 seconds have passed (stopping ticker)\n");   
        ticker.Stop();
        DMSG(0, "threadExample: ticker stopped.\n");
    }
    TRACE("threadExample: Done.\n");
    return 0;
    
}  // end main()
