/*
 * EventNotifier.cpp
 *
 *  Created on: May 17, 2018
 *      Author: Andreas Schneider, Johannes Wendel, Jonas Zeunert
 */

#include <boost/thread.hpp>
#include "utility/config.h"
#include "EventNotifier.h"

namespace flippR_driver
{
namespace input
{
namespace detail
{

EventNotifier::EventNotifier(utility::IBlockingQueue<Event> *queue) :
is_running(true),
event_queue(queue)
{
  this->notify_thread = std::thread(&EventNotifier::notify, this);

  CLOG(DEBUG, INPUT_LOGGER) << "Created EventNotifier and started thread";
}

EventNotifier::~EventNotifier()
{
  this->is_running = false;

  Event end_event(0, 0, "END");
  this->event_queue->push(end_event);

  this->notify_thread.join();

  delete this->event_queue;
}

void EventNotifier::register_event_handler(std::shared_ptr<EventHandler> handler)
{
  std::lock_guard<std::mutex> event_handler_guard(event_handler_mutex);
  this->event_handlers.insert(handler);
  CLOG(INFO, INPUT_LOGGER) << "New Eventhandler at address " << handler << " was registered.";
}

void EventNotifier::unregister_event_handler(std::shared_ptr<EventHandler> handler)
{
  std::lock_guard<std::mutex> event_handler_guard(event_handler_mutex);
  this->event_handlers.erase(handler);
  CLOG(INFO, INPUT_LOGGER) << "Eventhandler at address " << handler << " was unregistered.";
}

void EventNotifier::distribute_event(const Event &event)
{
  this->event_queue->push(event);
}

void EventNotifier::notify()
{
  while(this->is_running)
  {
    Event event = this->event_queue->pop();

    // TODO schoener machen
    if(event.name == "END")
    {
      return;
    }

    // getting a guard and calling all registered handlers
    std::lock_guard<std::mutex> event_handler_guard(this->event_handler_mutex);
    //CLOG(INFO, INPUT_LOGGER) << "Notified " << this->event_handlers.size() << " EventHandlers";
    for(auto handler : this->event_handlers)
    {
      //CLOG(INFO, INPUT_LOGGER) << "Notify " << handler;
      boost::thread handler_caller(boost::bind(&EventHandler::handle, handler, event));

      if(!handler_caller.timed_join(boost::posix_time::milliseconds(INPUT_HANDLER_TIMEOUT_MILLI)))
      {
        CLOG(WARNING, INPUT_LOGGER) << "Handler " << typeid(handler).name() << " didn't finish in "
                                    << INPUT_HANDLER_TIMEOUT_MILLI << " milliseconds. Aborting Execution!";
      }
    }
  }
}

}
}
}
