// Copyright (c) 2020 Metrolab Technology S.A., Geneva, Switzerland (www.metrolab.com)
// See the included file LICENSE.txt for the licensing conditions.

//////////////////////////////////////////////////////////////////////////
/// \file
/// \brief Test THM1176 API: High-level measurement methods

#include "gtest/gtest.h"
#include "THM1176TestUtilities.h"

#include <ctime>

using namespace MTL::Instrument;
using namespace MTL::Instrument::THM1176Types;

/// \brief Test THM1176 API: High-level measurement methods
class CTHM1176HighLevelMeasurementsTest : public ::testing::Test
{
protected:
	static THM1176_TEST_RESOURCE_MANAGER_CLASS * pResourceManager;
	static CTHM1176Instrument<THM1176_TEST_INSTRUMENT_CLASS, THM1176_TEST_RESOURCE_MANAGER_CLASS> * pTHM1176;

	static void SetUpTestCase()
	{
		ASSERT_EQ(true, ConnectToTHM1176(pResourceManager, pTHM1176));
		ASSERT_NE(nullptr, pResourceManager);
		ASSERT_NE(nullptr, pTHM1176);
		ASSERT_EQ(true, pTHM1176->IsOpen());
		ASSERT_EQ(true, pTHM1176->Reset());
	}
	
	static void TearDownTestCase()
	{
		delete pTHM1176;
		pTHM1176 = nullptr;
		delete pResourceManager;
		pResourceManager = nullptr;
	}
	
	virtual void SetUp()
	{
		// Reset the instrument.
		ASSERT_NE(pTHM1176, nullptr);
		ASSERT_EQ(true, pTHM1176->Reset());
	}
};
THM1176_TEST_RESOURCE_MANAGER_CLASS * CTHM1176HighLevelMeasurementsTest::pResourceManager = nullptr;
CTHM1176Instrument<THM1176_TEST_INSTRUMENT_CLASS, THM1176_TEST_RESOURCE_MANAGER_CLASS> * CTHM1176HighLevelMeasurementsTest::pTHM1176 = nullptr;

/// \test Test Measure method: single measurement.
TEST_F(CTHM1176HighLevelMeasurementsTest, MeasureSingle)
{
	tFlux			l_Bx, l_By, l_Bz;
	uOPERation		l_Operation;
	eUnits			l_Units;

	// Set lowest range, and read OPERation Event register to clear it.
	sRange<sBoundedParm> l_RangeBounds;
	ASSERT_EQ(true, pTHM1176->ParmRangeGet(l_RangeBounds));
	sRange<uParm> l_Range;
	l_Range.Auto = false;
	l_Range.Range = l_RangeBounds.Range.Min;
	ASSERT_EQ(true, pTHM1176->ParmRangeSet(l_Range));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));

	// Measure with DefaultParms=true.
	// This should re-select auto-ranging, so the RANGing bit should be set.
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(1, l_Operation.OPERation.RANGing);

	// Set lowest range, and read OPERation Event register to clear it.
	ASSERT_EQ(true, pTHM1176->ParmRangeSet(l_Range));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));

	// Measure with DefaultParms=false.
	// This should leave manual ranging, and not set the RANGing bit.
	// Units should be T, the default.
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz, false));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(0, l_Operation.OPERation.RANGing);
	ASSERT_EQ(true, pTHM1176->ParmUnitsGet(l_Units));
	EXPECT_EQ(kT, l_Units);
	
	// Measure with non-default Units.
	CUnitsList l_AllUnits;
	ASSERT_EQ(true, pTHM1176->GetAllUnits(l_AllUnits));
	ASSERT_LT(1, l_AllUnits.size());
	ASSERT_NE(kT, l_AllUnits[1]);
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz, false, l_AllUnits[1]));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(0, l_Operation.OPERation.RANGing);
	ASSERT_EQ(true, pTHM1176->ParmUnitsGet(l_Units));
	EXPECT_EQ(l_AllUnits[1], l_Units);

	// Measure with non-default ExpectedField, which should cause RANGing.
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz, false, l_AllUnits[1], l_RangeBounds.Range.Max / 2.0f));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(1, l_Operation.OPERation.RANGing);

	// Measure with different number of Digits.
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, 0., 0));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, 0., 1));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, 0., 2));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, 0., 3));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, 0., 4));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, 0., 5));
	EXPECT_EQ(false,	pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, 0., 6));
	{
		std::vector<sError> l_ErrorList = pTHM1176->CurrentErrorList();
		EXPECT_EQ(3, l_ErrorList.size());
		EXPECT_EQ(-222, l_ErrorList[0].Code);
		EXPECT_EQ(-222, l_ErrorList[1].Code);
		EXPECT_EQ(-222, l_ErrorList[2].Code);
	}

	// Measure with non-default ExpectedField and different number of Digits.
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, l_RangeBounds.Range.Max / 2.0f, 0));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, l_RangeBounds.Range.Max / 2.0f, 1));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, l_RangeBounds.Range.Max / 2.0f, 2));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, l_RangeBounds.Range.Max / 2.0f, 3));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, l_RangeBounds.Range.Max / 2.0f, 4));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, l_RangeBounds.Range.Max / 2.0f, 5));
	EXPECT_EQ(false,	pTHM1176->Measure(l_Bx, l_By, l_Bz, true, kT, l_RangeBounds.Range.Max / 2.0f, 6));
	{
		std::vector<sError> l_ErrorList = pTHM1176->CurrentErrorList();
		EXPECT_EQ(3, l_ErrorList.size());
		EXPECT_EQ(-222, l_ErrorList[0].Code);
		EXPECT_EQ(-222, l_ErrorList[1].Code);
		EXPECT_EQ(-222, l_ErrorList[2].Code);
	}
}

/// \test Test Measure method: multiple measurements.
TEST_F(CTHM1176HighLevelMeasurementsTest, MeasureMultiple)
{
	CFluxList			l_Bx, l_By, l_Bz;
	uOPERation			l_Operation;
	eUnits				l_Units;
	
	// Set lowest range, and read OPERation Event register to clear it.
	sRange<sBoundedParm> l_RangeBounds;
	ASSERT_EQ(true, pTHM1176->ParmRangeGet(l_RangeBounds));
	sRange<uParm> l_Range;
	l_Range.Auto = false;
	l_Range.Range = l_RangeBounds.Range.Min;
	ASSERT_EQ(true, pTHM1176->ParmRangeSet(l_Range));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	
	// Measure with DefaultParms=true.
	// This should re-select auto-ranging, so the RANGing bit should be set.
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz));
	EXPECT_EQ(1, l_Bx.size());
	EXPECT_EQ(1, l_By.size());
	EXPECT_EQ(1, l_Bz.size());
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(1, l_Operation.OPERation.RANGing);
	
	// Set lowest range, and read OPERation Event register to clear it.
	ASSERT_EQ(true, pTHM1176->ParmRangeSet(l_Range));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	
	// Measure with DefaultParms=true, NoMeasurements=2.
	// This should re-select auto-ranging, so the RANGing bit should be set.
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz, 2));
	EXPECT_EQ(2, l_Bx.size());
	EXPECT_EQ(2, l_By.size());
	EXPECT_EQ(2, l_Bz.size());
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(1, l_Operation.OPERation.RANGing);
	
	// Set lowest range, and read OPERation Event register to clear it.
	ASSERT_EQ(true, pTHM1176->ParmRangeSet(l_Range));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	
	// Measure with DefaultParms=false.
	// This should leave manual ranging, and not set the RANGing bit.
	// Units should be T, the default.
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, false));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(0, l_Operation.OPERation.RANGing);
	ASSERT_EQ(true, pTHM1176->ParmUnitsGet(l_Units));
	EXPECT_EQ(kT, l_Units);
	
	// Measure with non-default Units.
	CUnitsList l_AllUnits;
	ASSERT_EQ(true, pTHM1176->GetAllUnits(l_AllUnits));
	ASSERT_LT(1, l_AllUnits.size());
	ASSERT_NE(kT, l_AllUnits[1]);
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, false, l_AllUnits[1]));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(0, l_Operation.OPERation.RANGing);
	ASSERT_EQ(true, pTHM1176->ParmUnitsGet(l_Units));
	EXPECT_EQ(l_AllUnits[1], l_Units);
	
	// Measure with non-default ExpectedField, which should cause RANGing.
	ASSERT_EQ(true, pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, false, l_AllUnits[1], l_RangeBounds.Range.Max / 2.0f));
	ASSERT_EQ(true, pTHM1176->StatusGet(sStatusRegister(kStatusOperationStatusRegister, kStatusEvent), l_Operation.RawOPER));
	EXPECT_EQ(1, l_Operation.OPERation.MEASuring);
	EXPECT_EQ(1, l_Operation.OPERation.RANGing);
	
	// Measure with different number of Digits.
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, 0., 0));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, 0., 1));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, 0., 2));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, 0., 3));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, 0., 4));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, 0., 5));
	EXPECT_EQ(false,	pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, 0., 6));
	{
		std::vector<sError> l_ErrorList = pTHM1176->CurrentErrorList();
		EXPECT_EQ(3, l_ErrorList.size());
		EXPECT_EQ(-222, l_ErrorList[0].Code);
		EXPECT_EQ(-222, l_ErrorList[1].Code);
		EXPECT_EQ(-222, l_ErrorList[2].Code);
	}

	// Measure with non-default ExpectedField and different number of Digits.
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, l_RangeBounds.Range.Max / 2.0f, 0));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, l_RangeBounds.Range.Max / 2.0f, 1));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, l_RangeBounds.Range.Max / 2.0f, 2));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, l_RangeBounds.Range.Max / 2.0f, 3));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, l_RangeBounds.Range.Max / 2.0f, 4));
	EXPECT_EQ(true,		pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, l_RangeBounds.Range.Max / 2.0f, 5));
	EXPECT_EQ(false,	pTHM1176->Measure(l_Bx, l_By, l_Bz, 2, true, kT, l_RangeBounds.Range.Max / 2.0f, 6));
	{
		std::vector<sError> l_ErrorList = pTHM1176->CurrentErrorList();
		EXPECT_EQ(3, l_ErrorList.size());
		EXPECT_EQ(-222, l_ErrorList[0].Code);
		EXPECT_EQ(-222, l_ErrorList[1].Code);
		EXPECT_EQ(-222, l_ErrorList[2].Code);
	}
}
