/*
 * original Glib::Dispatcher example -- cross thread signalling
 * by Daniel Elstner  <daniel.elstner@gmx.net>
 * 
 * Modified by Stephan Puchegger <stephan.puchegger@ap.univie.ac.at>
 * to contain 2 mainloops in 2 different threads, that communicate 
 * via cross thread signalling in both directions. The timer thread
 * sends the UI thread a cross thread signal every second, which in turn
 * updates the label stating how many seconds have passed since the start
 * of the program.
 *
 * Modified by J. Abelardo Gutierrez <jabelardo@cantv.net>
 * to cast all gtkmm out and make it glimm only
 *
 * Note:  This example is special stuff that's seldomly needed by the
 * vast majority of applications.  Don't bother working out what this
 * code does unless you know for sure you need 2 main loops running in
 * 2 distinct main contexts.
 *
 * Copyright (c) 2002-2003  Free Software Foundation
 */

#include <glibmm.h>
#include <sstream>
#include <iostream>


namespace
{
Glib::RefPtr<Glib::MainLoop> main_loop;

class ThreadTimer : public sigc::trackable
{
public:
  ThreadTimer();
  ~ThreadTimer();

  void launch();
  void signal_finished_emit();
  void print() const;

  typedef sigc::signal<void> type_signal_end;
  static type_signal_end& signal_end();

private:
  unsigned int      time_;
  Glib::Dispatcher  signal_increment_;  
  Glib::Dispatcher* signal_finished_ptr_;

  Glib::Threads::Mutex       startup_mutex_;
  Glib::Threads::Cond        startup_cond_;
  Glib::Threads::Thread*     thread_;
  
  static type_signal_end signal_end_;

  void timer_increment();
  bool timeout_handler();
  static void finished_handler(Glib::RefPtr<Glib::MainLoop> mainloop);
  void thread_function();
};

//TODO: Rename to avoid confusion with Glib::Dispatcher. murrayc
class Dispatcher : public sigc::trackable
{
public:
  Dispatcher();

  void launch_thread();
  void end();

private:
  ThreadTimer* timer_;
};

ThreadTimer::ThreadTimer() 
:
  time_ (0),
  // Create a new dispatcher that is attached to the default main context,
  signal_increment_ (),
  // This pointer will be initialized later by the 2nd thread.
  signal_finished_ptr_ (NULL)
{
  // Connect the cross-thread signal.
  signal_increment_.connect(sigc::mem_fun(*this, &ThreadTimer::timer_increment));
}

ThreadTimer::~ThreadTimer()
{}

void ThreadTimer::launch()
{
  // Unfortunately, the thread creation has to be fully synchronized in
  // order to access the Dispatcher object instantiated by the 2nd thread.
  // So, let's do some kind of hand-shake using a mutex and a condition
  // variable.
  Glib::Threads::Mutex::Lock lock (startup_mutex_);

  // Create a joinable thread -- it needs to be joined, otherwise it's a memory leak.
  thread_ = Glib::Threads::Thread::create(
      sigc::mem_fun(*this, &ThreadTimer::thread_function), true);

  // Wait for the 2nd thread's startup notification.
  while(signal_finished_ptr_ == NULL)
    startup_cond_.wait(startup_mutex_);
}

void ThreadTimer::signal_finished_emit()
{
  // Cause the 2nd thread's main loop to quit.
  signal_finished_ptr_->emit();

  // wait for the thread to join
  if(thread_ != NULL)
    thread_->join();

  signal_finished_ptr_ = NULL;
}

void ThreadTimer::print() const
{
  std::cout << time_ << " seconds since start" << std::endl;
}

sigc::signal< void >& ThreadTimer::signal_end()
{
  return signal_end_;
}

void ThreadTimer::timer_increment()
{
  // another second has passed since the start of the program
  ++time_;
  print();

  if(time_ >= 10)
    signal_finished_emit();
}

// static
void ThreadTimer::finished_handler(Glib::RefPtr<Glib::MainLoop> mainloop)
{
  // quit the timer thread mainloop
  mainloop->quit();
  std::cout << "timer thread mainloop finished" << std::endl;
  ThreadTimer::signal_end().emit();
}

bool ThreadTimer::timeout_handler()
{
  // inform the printing thread that another second has passed
  signal_increment_();

  // this timer should stay alive
  return true;
}

void ThreadTimer::thread_function()
{
  // create a new Main Context
  Glib::RefPtr<Glib::MainContext> context = Glib::MainContext::create();
  // create a new Main Loop
  Glib::RefPtr<Glib::MainLoop> mainloop = Glib::MainLoop::create(context, true);

  // attach a timeout handler, that is called every second, to the
  // newly created MainContext
  context->signal_timeout().connect(sigc::mem_fun(*this, &ThreadTimer::timeout_handler), 1000);

  // We need to lock while creating the Dispatcher instance,
  // in order to ensure memory visibility.
  Glib::Threads::Mutex::Lock lock (startup_mutex_);

  // create a new dispatcher, that is connected to the newly
  // created MainContext
  Glib::Dispatcher signal_finished (context);
  
  signal_finished.connect(sigc::bind(sigc::ptr_fun(&ThreadTimer::finished_handler), mainloop));

  signal_finished_ptr_ = &signal_finished;

  // Tell the launcher thread that everything is in place now.
  startup_cond_.signal();
  lock.release();

  // start the mainloop
  mainloop->run();
}

// initialize static member:
ThreadTimer::type_signal_end ThreadTimer::signal_end_;

Dispatcher::Dispatcher()
: 
  timer_ (NULL)
{
  std::cout << "Thread Dispatcher Example #2" << std::endl;

  timer_ = new ThreadTimer();
  timer_->signal_end().connect(sigc::mem_fun(*this, &Dispatcher::end));
  timer_->print();
}

void Dispatcher::launch_thread()
{
  // launch the timer thread
  timer_->launch();
}

void Dispatcher::end()
{
  // quit the main mainloop
  main_loop->quit();
}

} // anonymous namespace


int main(int, char**)
{
  Glib::init();
  main_loop = Glib::MainLoop::create();

  Dispatcher dispatcher;

  // Install a one-shot idle handler to launch the threads
  Glib::signal_idle().connect(
      sigc::bind_return(sigc::mem_fun(dispatcher, &Dispatcher::launch_thread), false));

  main_loop->run();

  return 0;
}

