/*
 * TestDetector.cpp
 *
 *  Created on: Jun 27, 2018
 *      Author: Andreas Schneider, Johannes Wendel, Jonas Zeunert
 */


#include "catch.hpp"
#include "fakeit.hpp"

#include <map>
#include <typeinfo>
#include <thread>


#define private public

#include "input/EventNotifier.h"
#include "input/DistributingEvent.h"
#include "input/detail/Detector.h"
#include "input/detail/InputPinController.h"
#include "utility/LoggerFactory.h"


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


SCENARIO("Creating a Detector object", "")
{
  GIVEN("An IEventNofifier, IInputGPIOInterface")
  {
    LoggerFactory::CreateInputTestLogger();

    Mock<EventNotifier> event_notifier_mock;
    Mock<InputPinController> gpio_interface_mock;

    Fake(Dtor(gpio_interface_mock));
    When(Method(gpio_interface_mock, read_data)).AlwaysReturn(false);

    Fake(Dtor(event_notifier_mock));
    When(Method(event_notifier_mock, distribute_event)).AlwaysReturn();

    std::vector<std::shared_ptr<DistributingEvent>> events;

    WHEN("Detector is created")
    {
      detail::Detector detector(std::unique_ptr<InputPinController>(&gpio_interface_mock.get()), events);
      THEN("a thread should be created")
      {
        REQUIRE(typeid(detector.detect_thread).hash_code() == typeid(std::thread).hash_code());
      }
    }
  }
}

SCENARIO("There are events at the input", "")
{
  GIVEN("An IEventNofifier, three Events and an InputPinController")
  {
    LoggerFactory::CreateInputTestLogger();

    Mock<EventNotifier> event_notifier_mock;
    Mock<InputPinController> gpio_interface_mock;

    Fake(Dtor(gpio_interface_mock));
    When(Method(gpio_interface_mock, read_data)).AlwaysDo([](char c){return c==2;});

    Fake(Dtor(event_notifier_mock));
    When(Method(event_notifier_mock, distribute_event)).AlwaysReturn();

    std::shared_ptr<EventNotifier> event_notifier;
    DistributingEvent event1(1, '1', "event 1", std::chrono::milliseconds(0), event_notifier);
    DistributingEvent event2(2, '2', "event 2", std::chrono::milliseconds(0), event_notifier);
    DistributingEvent event3(3, '3', "event 3", std::chrono::milliseconds(0), event_notifier);

    std::vector<std::shared_ptr<DistributingEvent>> events;

    auto event2ptr = std::make_shared<DistributingEvent>(event2);
    events.push_back(std::make_shared<DistributingEvent>(event1));
    events.push_back(event2ptr);
    events.push_back(std::make_shared<DistributingEvent>(event3));

    WHEN("an event can be found at gpio interface")
    {
      detail::Detector detector(std::unique_ptr<InputPinController>(&gpio_interface_mock.get()), events);
      std::this_thread::sleep_for(std::chrono::milliseconds(50));
      THEN("the event should be distributed")
      {
        detector.is_running = false;
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
        REQUIRE((bool)Verify(Method(event_notifier_mock, distribute_event).Using(*event2ptr)));
      }
    }
  }
}

SCENARIO("There are events at the input but no suitable event in map", "")
{
  GIVEN("An IEventNofifier, three Events and an InputPinController")
  {
    LoggerFactory::CreateInputTestLogger();

    Mock<EventNotifier> event_notifier_mock;
    Mock<InputPinController> gpio_interface_mock;

    Fake(Dtor(gpio_interface_mock));
    When(Method(gpio_interface_mock, read_data)).AlwaysDo([](char c){return c==4;});

    Fake(Dtor(event_notifier_mock));
    When(Method(event_notifier_mock, distribute_event)).AlwaysReturn();

    std::shared_ptr<EventNotifier> event_notifier;
    DistributingEvent event1(1, '1', "event 1", std::chrono::milliseconds(0), event_notifier);
    DistributingEvent event2(2, '2', "event 2", std::chrono::milliseconds(0), event_notifier);
    DistributingEvent event3(3, '3', "event 3", std::chrono::milliseconds(0), event_notifier);

    std::vector<std::shared_ptr<DistributingEvent>> events;

    events.push_back(std::make_shared<DistributingEvent>(event1));
    events.push_back(std::make_shared<DistributingEvent>(event2));
    events.push_back(std::make_shared<DistributingEvent>(event3));

    WHEN("an event can be found at gpio interface")
    {
      detail::Detector detector(std::unique_ptr<InputPinController>(&gpio_interface_mock.get()), events);
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
      THEN("the event should be distributed")
      {
        detector.is_running = false;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        REQUIRE_FALSE((bool)Verify(Method(event_notifier_mock, distribute_event)));
      }
    }
  }
}
