// 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: GetIdentification, GetAllRanges, GetAllUnits, GetDivisisor, GetImmediateMeasurementPeriod

#include "gtest/gtest.h"
#include "THM1176TestUtilities.h"
#include <regex>

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

/// \brief Test THM1176 API: GetIdentification, GetAllRanges, GetAllUnits, GetDivisisor
class CTHM1176GetUtilitiesTest : 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;
	}
};
THM1176_TEST_RESOURCE_MANAGER_CLASS * CTHM1176GetUtilitiesTest::pResourceManager = nullptr;
CTHM1176Instrument<THM1176_TEST_INSTRUMENT_CLASS, THM1176_TEST_RESOURCE_MANAGER_CLASS> * CTHM1176GetUtilitiesTest::pTHM1176 = nullptr;

/// \test Test GetIdentification method: string variant.
TEST_F(CTHM1176GetUtilitiesTest, GetIdentificationString)
{
	std::string l_Identification;
	ASSERT_EQ(true, pTHM1176->GetIdentification(l_Identification));
	
	std::regex l_Regex("([^,]+),([^,]+),([^,]+),([^,]+)");
	std::smatch l_Match;
	ASSERT_EQ(true, std::regex_match(l_Identification, l_Match, l_Regex));
	EXPECT_EQ("Metrolab Technology SA",	l_Match[1].str());
	EXPECT_EQ("THM1176",				l_Match[2].str().substr(0, 7));
	
	std::string l_SerialNumber =			l_Match[3].str();
	std::string l_Versions =				l_Match[4].str();
	EXPECT_EQ(true, std::regex_match(l_SerialNumber, std::regex("^0[0-9]+$")));
	EXPECT_EQ(true, std::regex_match(l_Versions, l_Match, std::regex("^el[A-Z][0-9]-pr[A-Z][0-9]-fw[0-9]+\\.[0-9]+\\n$")));
}

/// \test Test GetIdentification method: parsed variant.
TEST_F(CTHM1176GetUtilitiesTest, GetIdentificationStruct)
{
	struct sIdentifier l_Identification;
	ASSERT_EQ(true, pTHM1176->GetIdentification(l_Identification));
	
	EXPECT_EQ("Metrolab Technology SA",	l_Identification.Manufacturer);
	EXPECT_EQ("THM1176",				l_Identification.Model.substr(0, 7));
	EXPECT_LT(0U,						l_Identification.SerialNumber);
	EXPECT_LT(0,						l_Identification.ElectronicsVersion.Major);
    EXPECT_LE(0,						l_Identification.ElectronicsVersion.Minor);
	EXPECT_LT(0,						l_Identification.ProbeVersion.Major);
    EXPECT_LE(0,						l_Identification.ProbeVersion.Minor);
	EXPECT_LT(0,						l_Identification.FirmwareVersion.Major);
	EXPECT_LT(0,						l_Identification.FirmwareVersion.Minor);
	EXPECT_LE(0,						l_Identification.ModelRevision);
	EXPECT_LE(0,						l_Identification.InstrModel);
}

/// \test Test GetAllRanges method.
TEST_F(CTHM1176GetUtilitiesTest, GetAllRanges)
{
	CFluxList l_Ranges;
	ASSERT_EQ(true, pTHM1176->GetAllRanges(l_Ranges));
	EXPECT_LT(0, l_Ranges.size());
	for (auto l_Range : l_Ranges)
		EXPECT_LT(0., l_Range);
}

/// \test Test GetAllUnits method.
TEST_F(CTHM1176GetUtilitiesTest, GetAllUnits)
{
	CUnitsList l_Units;
	ASSERT_EQ(true, pTHM1176->GetAllUnits(l_Units));
	EXPECT_LT(0, l_Units.size());
	for (auto l_Unit : l_Units)
	{
		EXPECT_LE(kT, l_Unit);
		EXPECT_GE(kMHzp, l_Unit);
	}
}

/// \test Test GetDivisor method.
TEST_F(CTHM1176GetUtilitiesTest, GetDivisor)
{
	CUnitsList l_Units;
	ASSERT_EQ(true, pTHM1176->GetAllUnits(l_Units));
	for (auto l_Unit : l_Units)
	{
		U32 l_Divisor;
		ASSERT_EQ(true, pTHM1176->GetDivisor(l_Unit, l_Divisor));
		EXPECT_LT(0U, l_Divisor);
	}
}

/// \test Test GetRotationMatrix method.
TEST_F(CTHM1176GetUtilitiesTest, GetRotationMatrix)
{
	// We expect the rotation matrix more or less to be an identity matrix.
	Matrix3f l_Matrix;
	ASSERT_EQ(true, pTHM1176->GetRotationMatrix(l_Matrix));
	std::cout << l_Matrix << std::endl;
	for (int i = 0; i< 3; i++)
		for (int j = 0; j < 3; j++)
			EXPECT_NEAR(l_Matrix(i,j), (i == j) ? 1. : 0., 0.1);
}

/// \test Test GetImmediateMeasurementPeriod method.
TEST_F(CTHM1176GetUtilitiesTest, GetImmediateMeasurementPeriod)
{
	sAveraging<uParm> l_Avg;
	F64 l_Period1, l_Period2, l_Period3;
	struct sIdentifier l_Identification;
	pTHM1176->GetIdentification(l_Identification)

	// We expect that 0 averaging points to give an error.
	l_Avg.NoPoints = 0;
	EXPECT_EQ(false, pTHM1176->GetImmediateMeasurementPeriod(l_Avg, l_Identification.ModelRevision, l_Period1));
	EXPECT_EQ(0., l_Period1);
	
	// We expect that the  time increases linearly with # averaging points.
	l_Avg.NoPoints = 1;
	EXPECT_EQ(true, pTHM1176->GetImmediateMeasurementPeriod(l_Avg, l_Identification.ModelRevision, l_Period1));
	l_Avg.NoPoints = 2;
	EXPECT_EQ(true, pTHM1176->GetImmediateMeasurementPeriod(l_Avg, l_Identification.ModelRevision, l_Period2));
	l_Avg.NoPoints = 3;
	EXPECT_EQ(true, pTHM1176->GetImmediateMeasurementPeriod(l_Avg, l_Identification.ModelRevision, l_Period3));
	EXPECT_NEAR((l_Period2 - l_Period1), (l_Period3 - l_Period2), 1E-8);
}
