#include "MockI2C.hpp"

#include <string.h>

#include <CppUTest/CommandLineTestRunner.h>
#include <CppUTest/TestHarness.h>
#include <CppUTestExt/MockSupport.h>

TEST_GROUP(MockI2CTestGroup)
{
	MockI2C* i2c_mock = nullptr;
	I2C* i2c = nullptr;
	op_tComparitor comparator;
	op_tCopier copier;

	void setup()
	{
		i2c_mock = new MockI2C();
		i2c = i2c_mock;

		// mock().installComparator("op_t", comparator);

		mock().installCopier("I2C::op_t", copier);
	}

	void teardown()
	{
		mock().checkExpectations();
		mock().clear();
		mock().removeAllComparatorsAndCopiers();

		delete i2c_mock;
	}
};

TEST(MockI2CTestGroup, retValCorrect_andRetObject)
{
	// What we want the mocked call to return
	uint8_t ret_desired = static_cast<uint8_t>(I2C::status::ok);

	bool callback_success = false;

	// setup the object we want the mock to return
	const uint8_t tx_buf[2] = {0x01, 0x02};
	uint8_t rx_buf[4] = {};
	I2C::op_t op_to_return = {.address = 0x06,
							  .op = I2C::operation::read,
							  .tx_buffer = tx_buf,
							  .tx_size = sizeof(tx_buf),
							  .rx_buffer = rx_buf,
							  .rx_size = sizeof(rx_buf)};

	// This setup the mock to return the desired value, as well as the
	// the desired output parameter.
	mock()
		.expectOneCall("transfer")
		.onObject(i2c_mock)
		.withOutputParameterOfTypeReturning("I2C::op_t", "op", &op_to_return)
		.andReturnValue(ret_desired);

	// Modify the object to we pass into the mock, so we can test weather
	// it returns the desired object, specified above.
	I2C::op_t op_sent = op_to_return;
	op_sent.op = I2C::operation::ping;
	uint8_t ret_got =
		static_cast<uint8_t>(i2c->transfer(op_sent, [&callback_success](I2C::status result) {
			if(result == I2C::status::ok)
			{
				callback_success = true;
			}
		}));

	// Confirm the mock returned the expected return value
	CHECK_EQUAL(ret_desired, ret_got);

	CHECK(callback_success);

	// The mocking framework should have altered `op_sent` to match `op_to_return`
	CHECK_EQUAL(static_cast<uint8_t>(I2C::operation::read), static_cast<uint8_t>(op_sent.op));
}