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

#include "catch.hpp"
#include "fakeit.hpp"
#include "utility/LoggerFactory.h"

// testing purposes
#define private public

#include "input/detail/InputDriver.h"

#include "input/EventNotifier.h"
#include "input/Detector.h"


using namespace fakeit;
using namespace flippR_driver;
using namespace input;
using namespace utility;

SCENARIO("An InputDriver gets created", "[construction}")
{
  GIVEN("An EventNotifier and a Detector")
  {
    LoggerFactory::CreateInputTestLogger();

    Mock<Detector> detector_mock;
    Fake(Dtor(detector_mock));

    Mock<EventNotifier> event_notifier_mock;
    Fake(Dtor(event_notifier_mock));
    WHEN("The InputDriver gets created")
    {
      std::shared_ptr<EventNotifier> event_notifier_ptr(&event_notifier_mock.get());
      std::map<std::string, std::shared_ptr<Event>> events;

      input::detail::InputDriver input_driver(event_notifier_ptr, std::unique_ptr<Detector>(&detector_mock.get()), events);

      THEN("It saves the EventNotifier and the Detector")
      {
        REQUIRE(input_driver.event_notifier == event_notifier_ptr);
        REQUIRE(&(*input_driver.detector) == &detector_mock.get());
      }
    }
  }
}

SCENARIO("An EventHandler [un]registers at the driver", "[un-register]")
{
  GIVEN("An InputDriver, EventHandler and an EventNotifier")
  {
    LoggerFactory::CreateInputTestLogger();

    Mock<flippR_driver::input::Detector> detector_mock;
    Fake(Dtor(detector_mock));

    Mock<input::EventHandler> event_handler_mock;
    Fake(Method(event_handler_mock, handle));
    Fake(Dtor(event_handler_mock));

    Mock<flippR_driver::input::EventNotifier> event_notifier_mock;
    Fake(Method(event_notifier_mock, register_event_handler));
    Fake(Method(event_notifier_mock, unregister_event_handler));
    Fake(Dtor(event_notifier_mock));

    std::shared_ptr<EventNotifier> event_notifier_ptr(&event_notifier_mock.get());

    std::map<std::string, std::shared_ptr<Event>> events;

    input::detail::InputDriver input_driver(event_notifier_ptr, std::unique_ptr<Detector>(&detector_mock.get()), events);

    WHEN("The EventHandler registers at the driver")
    {
      input_driver.register_event_handler(&event_handler_mock.get());

      THEN("The register_event_handler at the event_notifier gets called")
      {
        REQUIRE((bool)Verify(Method(event_notifier_mock, register_event_handler).Using(&event_handler_mock.get())));
      }
    }

    WHEN("The EventHandler then unregisters at the driver")
    {
      input_driver.unregister_event_handler(&event_handler_mock.get());

      THEN("The unregister_event_handler at the event_notifier gets called")
      {
        REQUIRE((bool)Verify(Method(event_notifier_mock, unregister_event_handler).Using(&event_handler_mock.get())));
      }
    }
  }
}


SCENARIO("An Input Driver is created normally", "")
{
  GIVEN("An InputDriver, EventHandler and an EventNotifier and some events")
  {
    LoggerFactory::CreateInputTestLogger();

    Mock<flippR_driver::input::Detector> detector_mock;
    Fake(Dtor(detector_mock));

    Mock<input::EventHandler> event_handler_mock;
    Fake(Dtor(event_handler_mock));

    Mock<flippR_driver::input::EventNotifier> event_notifier_mock;
    Fake(Dtor(event_notifier_mock));

    std::shared_ptr<EventNotifier> event_notifier_ptr(&event_notifier_mock.get());

    Event event1('a', 2, "e1");
    auto event1_ptr = std::make_shared<Event>(event1  );
    Event event2('b', 1, "e2");
    Event event3('c', 2, "e3");
    Event event4('d', 1, "e4");

    std::map<std::string, std::shared_ptr<Event>> events;
    events.emplace("e1", event1_ptr);
    events.emplace("e2", std::make_shared<Event>(event2));
    events.emplace("e3", std::make_shared<Event>(event3));
    events.emplace("e4", std::make_shared<Event>(event4));


    input::detail::InputDriver input_driver(event_notifier_ptr, std::unique_ptr<Detector>(&detector_mock.get()), events);

    WHEN("Someone asks for an event that is registered")
    {
      auto event = input_driver.get_event("e1");

      THEN("The obtained event should be the same as emplaced before")
      {
        REQUIRE(event==event1_ptr);
      }
    }

    WHEN("someone asks for an event that is not registered")
    {
      auto event = input_driver.get_event("no event");

      THEN("the obtained event should be null")
      {
        REQUIRE(event->get()->name == "ERROR");
      }
    }
  }
}