#include "test.h"

#include <list>
#include <string>

#include "event_bus.h"
#include "i_log_lines_events.h"
#include "line.h"

using namespace std;
using namespace std::placeholders;

class MockEventReceiver : public ILogLinesEvents {
public:
	virtual ~MockEventReceiver() {
		CHECK(_expects.empty());
	}

	void expect(const string& s) {
		_expects.push_back(s);
	}
        // an existing line was changed
        virtual void edit_line([[maybe_unused]] LogLines* ll,
			       size_t pos, Line* line) {
		CHECK(pos == 10);
		CHECK(line->view() == "edit");
	}

        // a new line was added
        virtual void append_line([[maybe_unused]] LogLines* ll,
				 [[maybe_unused]] size_t pos,
				 Line* line) {
		CHECK(!_expects.empty());
		CHECK(line->view() == _expects.front());
		_expects.pop_front();
	}

        // an insertion event
        virtual void insertion([[maybe_unused]] LogLines* ll,
			       [[maybe_unused]] size_t pos,
			       [[maybe_unused]] size_t amount) {}

        // a deletion event
        virtual void deletion([[maybe_unused]] LogLines* ll,
			      [[maybe_unused]] size_t pos,
			      [[maybe_unused]] size_t amount) {}

	// clearing event
	virtual void clear_lines([[maybe_unused]] LogLines* ll) {}

protected:
	list<string> _expects;
};

TEST_CASE("eventbus enroll") {
	MockEventReceiver m, m_other;
	LogLines* ll = (LogLines*) 12;
	LogLines* ll_other = (LogLines*) 13;
	EventBus::_()->enregister(ll, &m);
	EventBus::_()->enregister(ll_other, &m_other);

	m.expect("hello");
	m.expect("there");
	m_other.expect("another");
	Line l1(string("hello"));
	Line l2(string("another"));
	Line l3(string("there"));
	Line l4(string("world"));
	Line l5(string("ignore"));
	EventBus::_()->append_line(ll, 0, &l1);
	EventBus::_()->append_line(ll_other, 1, &l2);
	EventBus::_()->append_line(ll, 1, &l3);
	m.expect("world");
	EventBus::_()->append_line(ll, 2, &l4);
	EventBus::_()->deregister(ll, &m);
	EventBus::_()->append_line(ll, 3, &l5);
	EventBus::_()->deregister(ll_other, &m_other);
}

TEST_CASE("eventbus eventmaker end") {
	MockEventReceiver m;
	LogLines* ll = (LogLines*) 42;
	EventBus::_()->enregister(ll, &m);
	m.expect("hello");
	m.expect("there");
	Line l1(string("hello"));
	Line l2(string("there"));
	Line l3(string("world"));
	EventBus::_()->append_line(ll, 3, &l1);
	EventBus::_()->append_line(ll, 4, &l2);
	EventBus::_()->eventmaker_finished(ll);
	EventBus::_()->append_line(ll, 5, &l3);
	EventBus::_()->deregister(ll, &m);

}

TEST_CASE("eventbus eventmaker end and deregister") {
	MockEventReceiver m;
	LogLines* ll = (LogLines*) 42;
	EventBus::_()->enregister(ll, &m);
	m.expect("hello");
	m.expect("there");
	Line l1(string("hello"));
	Line l2(string("there"));
	Line l3(string("world"));
	EventBus::_()->append_line(ll, 3, &l1);
	EventBus::_()->append_line(ll, 4, &l2);
	EventBus::_()->eventmaker_finished(ll);
	EventBus::_()->append_line(ll, 5, &l3);
	EventBus::_()->deregister(ll, &m);
}

TEST_CASE("eventbus deregister and eventmaker end") {
	MockEventReceiver m;
	LogLines* ll = (LogLines*) 42;
	EventBus::_()->enregister(ll, &m);
	m.expect("hello");
	m.expect("there");
	Line l1(string("hello"));
	Line l2(string("there"));
	Line l3(string("world"));
	EventBus::_()->append_line(ll, 3, &l1);
	EventBus::_()->append_line(ll, 4, &l2);
	EventBus::_()->deregister(ll, &m);
	EventBus::_()->append_line(ll, 4, &l3);
	EventBus::_()->eventmaker_finished(ll);
	EventBus::_()->deregister(ll, &m);
}

TEST_CASE("eventbus multiple enroll") {
	MockEventReceiver m1;
	MockEventReceiver m2;
	MockEventReceiver m3;
	LogLines* s1 = (LogLines*) 42;
	LogLines* s2 = (LogLines*) 44;
	CHECK(!EventBus::_()->present(&m1));
	EventBus::_()->enregister(s1, &m1);
	CHECK(EventBus::_()->present(&m1));
	EventBus::_()->enregister(s1, &m2);
	EventBus::_()->enregister(s1, &m3);
	EventBus::_()->enregister(s2, &m2);
	EventBus::_()->enregister(s2, &m3);

	m1.expect("s1_1");
	m1.expect("s1_2");
	m1.expect("s1_3");
	m2.expect("s1_1");
	m2.expect("s2_1");
	m2.expect("s1_2");
	m2.expect("s1_3");
	m2.expect("s2_2");
	m3.expect("s1_1");
	m3.expect("s2_1");
	m3.expect("s1_2");
	m3.expect("s1_3");
	m3.expect("s2_2");

	Line l1(string("s1_1"));
	Line l2(string("s2_1"));
	Line l3(string("s1_2"));
	Line l4(string("edit"));
	Line l5(string("s1_3"));
	Line l6(string("s2_2"));

	EventBus::_()->append_line(s1, 2, &l1);
	EventBus::_()->append_line(s2, 3, &l2);
	EventBus::_()->append_line(s1, 3, &l3);
	EventBus::_()->edit_line(s1, 10, &l4);
	EventBus::_()->append_line(s1, 4, &l5);
	EventBus::_()->append_line(s2, 4, &l6);

        EventBus::_()->deregister(s1, &m1);
	CHECK(!EventBus::_()->present(&m1));
	CHECK(EventBus::_()->present(&m2));

        EventBus::_()->deregister(s1, &m2);
        EventBus::_()->deregister(s1, &m3);
        EventBus::_()->deregister(s2, &m2);
        EventBus::_()->deregister(s2, &m3);
}

