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

#include <DevicePowerSwitchSpy.hpp>
#include <MockI2C.hpp>
#include "test_helpers.hpp"
#include <iostream>

#define EXPECT_NO_ERROR(x) CHECK_EQUAL(static_cast<uint8_t>(BError::NONE), static_cast<uint8_t>(x))
#define EXPECT_CFG_FAILURE(x) \
	CHECK_EQUAL(static_cast<uint8_t>(BError::NOT_CONFIGURED), static_cast<uint8_t>(x))
#define EXPECT_WAITING_FOR_DATA(x) \
	CHECK_EQUAL(static_cast<uint8_t>(BError::WAITING_FOR_DATA), static_cast<uint8_t>(x))
#define EXPECT_I2C_TRANSFER_AND_RETURN(dev, op, ret)                \
	mock()                                                          \
		.expectOneCall("transfer")                                  \
		.onObject(dev)                                              \
		.withOutputParameterOfTypeReturning("I2C::op_t", "op", &op) \
		.andReturnValue(ret)

TEST_GROUP(BellowsTestGroup)
{
	MockI2C* i2c_mock = nullptr;
	I2C* i2c = nullptr;
	op_tCopier copier;

	DevicePowerSwitchSpy* powerSwitchSpy = nullptr;
	DevicePowerSwitch* powerSwitch = nullptr;

	std::string name = "bellows";

	float lowLimit = 50.0;
	float highLimit = 100.0;

	BellowsPositionADC2485::bellowsADCConfig_t defaultCfg = {
		.vRefADC_volts = 5.0, .slope_mm_per_volt = 10.0, .offset_mm = 75.0};

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

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

		powerSwitchSpy = new DevicePowerSwitchSpy();
		powerSwitch = powerSwitchSpy;
	}

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

		delete i2c_mock;
		delete powerSwitchSpy;
	}
};

TEST(BellowsTestGroup, ctor)
{
	BellowsPositionADC2485 bellows(*i2c_mock, name, *powerSwitch, lowLimit, highLimit,
								   LTC2485_ADDRESS_HIGH_HIGH);
}

TEST(BellowsTestGroup, ctorDoesntChagnePowerState)
{
	powerSwitchSpy->spy_enablePower(false);
	BellowsPositionADC2485 bellows(*i2c_mock, name, *powerSwitch, lowLimit, highLimit);

	CHECK_FALSE(powerSwitch->isPowerOn());

	powerSwitchSpy->spy_enablePower(true);
	BellowsPositionADC2485 bellows2(*i2c_mock, name, *powerSwitch, lowLimit, highLimit);

	CHECK(powerSwitch->isPowerOn());
}

TEST(BellowsTestGroup, initRequired)
{
	powerSwitchSpy->spy_enablePower(false);
	BellowsPositionADC2485 bellows(*i2c_mock, name, *powerSwitch, lowLimit, highLimit);

	BError err = bellows.start();
	CHECK_FALSE(powerSwitch->isPowerOn());

	// Since not configured...
	EXPECT_CFG_FAILURE(err);

	err = bellows.init(defaultCfg);

	// Expect success
	err = bellows.start();

	EXPECT_NO_ERROR(err);
}

TEST(BellowsTestGroup, powerSwitchActuation)
{
	powerSwitchSpy->spy_enablePower(false);
	BellowsPositionADC2485 bellows(*i2c_mock, name, *powerSwitch, lowLimit, highLimit,
								   LTC2485_ADDRESS_HIGH_HIGH);

	bellows.init(defaultCfg);
	EXPECT_NO_ERROR(bellows.start());

	CHECK(powerSwitch->isPowerOn());

	EXPECT_NO_ERROR(bellows.sleep());

	CHECK_FALSE(powerSwitch->isPowerOn());
}

TEST(BellowsTestGroup, readVoltageSequence)
{
	BellowsPositionADC2485 bellows(*i2c_mock, name, *powerSwitch, lowLimit, highLimit,
								   LTC2485_ADDRESS_HIGH_HIGH);

	bellows.init(defaultCfg);
	bellows.start();

	// simulated voltage
	float simulatedVin = 1.25f;
	// equivalent number of adc counts
	int32_t counts = static_cast<int32_t>((simulatedVin / defaultCfg.vRefADC_volts) * 2147483648);
	// invert the MSB, since that's what the LTC2485 does...
	counts ^= 0x80000000;

	uint8_t b0 = (uint8_t)((counts)&0xFF);
	uint8_t b1 = (uint8_t)((counts >> 8) & 0xFF);
	uint8_t b2 = (uint8_t)((counts >> 16) & 0xFF);
	uint8_t b3 = (uint8_t)((counts >> 24) & 0xFF);

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

	const uint8_t tx_buf = LTC2485::EXT_INPUT_60HZ_AUTOCAL;

	// Each operation to the LTC2485 should start with sending the
	// desired configuration, followed by a read of the 4 bytes
	// contained in the output register.
	uint8_t rx_buf[4] = {b3, b2, b1, b0};
	I2C::op_t expected_operation = {.address = LTC2485_ADDRESS_HIGH_HIGH,
									.op = I2C::operation::writeRead,
									.tx_buffer = &tx_buf,
									.tx_size = sizeof(tx_buf),
									.rx_buffer = rx_buf,
									.rx_size = sizeof(rx_buf)};

	float percentFull = 0.0f;

	BError err = BError::UNKNOWN_ERROR;

	EXPECT_I2C_TRANSFER_AND_RETURN(i2c, expected_operation, ret_desired);

	// We dont' expect data on the first call given the callback nature of the driver
	err = bellows.getPosition_pctFull(percentFull);
	EXPECT_WAITING_FOR_DATA(err);
	// DOUBLES_EQUAL(0.0, percentFull, 1E-7);

	// But, we expect data on the second call!
	err = bellows.getPosition_pctFull(percentFull);

	// Compute our expected fullness
	float expectedPosition_mm = simulatedVin * defaultCfg.slope_mm_per_volt + defaultCfg.offset_mm;
	float expectedPctFull =
		100.0f * (1.0f - (highLimit - expectedPosition_mm) / (highLimit - lowLimit));

	DOUBLES_EQUAL(expectedPctFull, percentFull, 1E-6);
	EXPECT_NO_ERROR(err);
}
