/*******************************************************************************
***  This file is part of Dux Rei Nummariae.                                 ***
***                                                                          ***
***  Copyright (C) 2017, 2018, 2019, 2020, 2021                              ***
***  John Schneiderman <Licensing _AT_ Schneiderman _DOT_ me>                ***
***                                                                          ***
***  This program is free software: you can redistribute it and/or modify it ***
***  under the terms of the GNU Affero General Public License as published   ***
***  by the Free Software Foundation, either version 3 of the License, or    ***
***  (at your option) any later version.                                     ***
***                                                                          ***
***  This program is distributed in the hope that it will be useful, but     ***
***  WITHOUT ANY WARRANTY; without even the implied warranty of              ***
***  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    ***
***  See the GNU Affero General Public License for more details.             ***
***                                                                          ***
***  You should have received a copy of the GNU Affero General Public License***
***  along with this program. If not, see <http://www.gnu.org/licenses/>.    ***
*******************************************************************************/
#include <QTest>
// using QVERIFY2

#include <string>
using namespace std::string_literals;
using std::string;

#include <foundation/Optional.hpp>
using drn::foundation::Optional;
#include <qt-mocks/Matchers.h>
using qt_mocks::equals;
using qt_mocks::isFalse;
using qt_mocks::isTrue;
using qt_mocks::not_;
using qt_mocks::notEquals;
// using QVERIFY_THAT;


namespace drn
{
namespace foundation
{
namespace tests
{

struct OptionalUT : QObject
{
	Q_OBJECT

	struct Test
	{
		Test() :
			Test{0}
		{}
		Test(const qint32& member) :
			member_{member},
			test_{}
		{}
		qint32 constMember() const
		{
			return 12;
		}
		qint32 nonConstMember()
		{
			return 24;
		}

		qint32 member_;
		string test_;
	};

private slots:
	//{ Constructor Tests

	void constructor_Empty_ShouldNotBeSet()
	{
		const Optional<Test> actual{};
		QVERIFY_THAT(actual.hasValue(), isFalse());
	}

	void constructor_GivenValue_ShouldBeSet()
	{
		const Optional<Test> actual{1234};
		QVERIFY_THAT(actual.hasValue(), isTrue());
		QVERIFY_THAT((*actual).member_, equals(1234));
	}

	void copyConstructor_Empty_ShouldNotBeSet()
	{
		const Optional<Test> actual{};
		const auto copy{actual};
		QVERIFY_THAT(copy.hasValue(), isFalse());
	}

	void copyConstructor_GivenValueCopy_ShouldBeSet()
	{
		const Optional<Test> actual{1234};
		const auto copy{actual};
		QVERIFY_THAT(copy.hasValue(), isTrue());
		QVERIFY_THAT((*copy).member_, equals(1234));
	}

	void moveConstructor_Empty_ShouldNotBeSet()
	{
		Optional<Test> actual{};
		const auto move{std::move(actual)};
		QVERIFY_THAT(move.hasValue(), isFalse());
	}

	void moveConstructor_GivenValueCopy_ShouldBeSet()
	{
		Optional<Test> actual{1234};
		const auto move{std::move(actual)};
		QVERIFY_THAT(move.hasValue(), isTrue());
		QVERIFY_THAT((*move).member_, equals(1234));
	}

	//}

	//{ Assignment Operator Tests

	void assignmentOperator_NoValueGivenValue_ShouldBeSet()
	{
		Optional<Test> actual{};
		actual = 1234;
		QVERIFY_THAT((*actual).member_, equals(1234));
	}

	void assignmentOperator_GivenNewValue_ShouldBeSet()
	{
		Optional<Test> actual{1234};
		actual = 5678;
		QVERIFY_THAT(actual.hasValue(), isTrue());
		QVERIFY_THAT(((*actual).member_), equals(5678));
	}

	void assignmentOperator_GivenNoValue_ShouldBeNotSet()
	{
		Optional<Test> actual{1234};
		actual = Optional<Test>{};
		QVERIFY_FALSE(actual.hasValue());
	}

	void assignmentOperator_NoOptionalValueGivenValue_ShouldBeSet()
	{
		Optional<Test> actual{};
		const Optional<Test> expected{1234};
		actual = expected;
		QVERIFY_THAT((*actual).member_, equals((*expected).member_));
	}

	void assignmentOperator_GivenNewOptionalValue_ShouldBeSet()
	{
		Optional<Test> actual{1234};
		const Optional<Test> expected{5678};
		actual = expected;
		QVERIFY_THAT(actual.hasValue(), isTrue());
		QVERIFY_THAT(((*actual).member_), equals((*expected).member_));
	}

	void assignmentOperator_NoOptionalValueGivenValueMoved_ShouldBeSet()
	{
		Optional<Test> actual{};
		Optional<Test> expected{1234};
		actual = std::move(expected);
		QVERIFY_THAT((*actual).member_, equals(1234));
	}

	void assignmentOperator_GivenNewOptionalValueMoved_ShouldBeSet()
	{
		Optional<Test> actual{1234};
		Optional<Test> expected{5678};
		actual = std::move(expected);
		QVERIFY_THAT(actual.hasValue(), isTrue());
		QVERIFY_THAT(((*actual).member_), equals(5678));
	}

	//}

	//{ Arrow Operator Tests

	void arrowOperator_MemberVariable_ShouldUse()
	{
		const Optional<Test> actual{1234};
		QVERIFY_THAT(actual->member_, equals(1234));
	}

	void arrowOperator_MemberFunction_ShouldUse()
	{
		Optional<Test> actual{1234};
		QVERIFY_THAT(actual->nonConstMember(), equals(24));
	}

	void arrowOperator_MemberConstFunction_ShouldUse()
	{
		const Optional<Test> actual{1234};
		QVERIFY_THAT(actual->constMember(), equals(12));
	}

	//}

	//{ Reset Tests

	void rest_HasNoValue_ShouldReset()
	{
		Optional<Test> actual{};
		QVERIFY_NO_THROW(actual.reset());
		QVERIFY_THAT(actual.hasValue(), isFalse());
	}

	void rest_HasValue_ShouldReset()
	{
		Optional<Test> actual{1234};
		QVERIFY_THAT(actual.hasValue(), isTrue());
		QVERIFY_NO_THROW(actual.reset());
		QVERIFY_THAT(actual.hasValue(), isFalse());
	}

	//}

	//{ Equality Operator Tests

	void operatorEquality_ContainedValueHasNoValue_ShouldBeFalse()
	{
		const Optional<qint32> actual{};
		QVERIFY_THAT(actual == 15, isFalse());
		QVERIFY_THAT(15 == actual, isFalse());
	}

	void operatorEquality_ContainedValueHasDifferentValue_ShouldBeFalse()
	{
		const Optional<qint32> actual{1234};
		QVERIFY_THAT(actual == 15, isFalse());
	}

	void operatorEquality_ContainedValueHasSameValue_ShouldBeTrue()
	{
		const Optional<qint32> actual{1234};
		QVERIFY_THAT(actual == 1234, isTrue());
	}

	void operatorEquality_LhsHasNoValue_ShouldBeFalse()
	{
		const Optional<qint32> actual{};
		const Optional<qint32> expected{15};
		QVERIFY_THAT(actual, not_(equals(expected)));
	}

	void operatorEquality_RhsHasNoValue_ShouldBeFalse()
	{
		const Optional<qint32> actual{15};
		const Optional<qint32> expected{};
		QVERIFY_THAT(actual, not_(equals(expected)));
	}

	void operatorEquality_HasDifferentValue_ShouldBeFalse()
	{
		const Optional<qint32> actual{1234};
		const Optional<qint32> expected{15};
		QVERIFY_THAT(actual, not_(equals(expected)));
	}

	void operatorEquality_HasSameValue_ShouldBeTrue()
	{
		const Optional<qint32> actual{1234};
		const Optional<qint32> expected{1234};
		QVERIFY_THAT(actual, equals(expected));
	}

	void operatorEquality_BothNotSet_ShouldBeTrue()
	{
		const Optional<qint32> actual{};
		const Optional<qint32> expected{};
		QVERIFY_THAT(actual, equals(expected));
	}

	//}


	//{ Inequality Operator Tests

	void operatorInequality_ContainedValueHasNoValue_ShouldBeTrue()
	{
		const Optional<qint32> actual{};
		QVERIFY_THAT(actual != 15, isTrue());
		QVERIFY_THAT(15 != actual, isTrue());
	}

	void operatorInequality_ContainedValueHasDifferentValue_ShouldBeTrue()
	{
		const Optional<qint32> actual{1234};
		QVERIFY_THAT(actual != 15, isTrue());
	}

	void operatorInequality_ContainedValueHasSameValue_ShouldBeFalse()
	{
		const Optional<qint32> actual{1234};
		QVERIFY_THAT(actual != 1234, isFalse());
	}

	void operatorInequality_LhsHasNoValue_ShouldBeTrue()
	{
		const Optional<qint32> actual{};
		const Optional<qint32> expected{15};
		QVERIFY_THAT(actual, notEquals(expected));
	}

	void operatorInequality_RhsHasNoValue_ShouldBeTrue()
	{
		const Optional<qint32> actual{15};
		const Optional<qint32> expected{};
		QVERIFY_THAT(actual, notEquals(expected));
	}

	void operatorInequality_HasDifferentValue_ShouldBeTrue()
	{
		const Optional<qint32> actual{1234};
		const Optional<qint32> expected{15};
		QVERIFY_THAT(actual, notEquals(expected));
	}

	void operatorInequality_HasSameValue_ShouldBeFalse()
	{
		const Optional<qint32> actual{1234};
		const Optional<qint32> expected{1234};
		QVERIFY_THAT(actual, not_(notEquals(expected)));
	}

	//}

	//{ less-than operator Tests

	void operatorLessThan_lhsIsNull_ShouldBeTrue()
	{
		const Optional<qint32> lhs{};
		const Optional<qint32> rhs{1234};
		QVERIFY_TRUE(lhs < rhs);
		QVERIFY_TRUE(lhs < *rhs);
	}

	void operatorLessThan_lhsIsLess_ShouldBeTrue()
	{
		const Optional<qint32> lhs{123};
		const Optional<qint32> rhs{1234};
		QVERIFY_TRUE(lhs < rhs);
		QVERIFY_TRUE(*lhs < rhs);
		QVERIFY_TRUE(lhs < *rhs);
	}

	void operatorLessThan_lhsRhsNull_ShouldBeFalse()
	{
		const Optional<qint32> lhs{};
		const Optional<qint32> rhs{};
		QVERIFY_FALSE(lhs < rhs);
	}

	void operatorLessThan_lhsIsEqual_ShouldBeFalse()
	{
		const Optional<qint32> lhs{1234};
		const Optional<qint32> rhs{1234};
		QVERIFY_FALSE(lhs < rhs);
		QVERIFY_FALSE(*lhs < rhs);
		QVERIFY_FALSE(lhs < *rhs);
	}

	void operatorLessThan_rhsIsNull_ShouldBeFalse()
	{
		const Optional<qint32> lhs{1234};
		const Optional<qint32> rhs{};
		QVERIFY_FALSE(lhs < rhs);
		QVERIFY_FALSE(*lhs < rhs);
	}

	void operatorLessThan_rhsIsLess_ShouldBeFalse()
	{
		const Optional<qint32> lhs{1234};
		const Optional<qint32> rhs{123};
		QVERIFY_FALSE(lhs < rhs);
		QVERIFY_FALSE(*lhs < rhs);
		QVERIFY_FALSE(lhs < *rhs);
	}

	//}

	//{ ValueOr Tests

	void valueOr_HasNoValue_ShouldBeSupplied()
	{
		const Optional<qint32> actual{};
		QVERIFY_THAT(actual.valueOr(5678), equals(5678));
	}

	void valueOr_HasValue_ShouldBeValue()
	{
		const Optional<qint32> actual{1234};
		QVERIFY_THAT(actual.valueOr(5678), equals(1234));
	}

	//}

	//{ valueOrDefault Tests

	void valueOrDefault_HasNoPodValue_ShouldBeDefault()
	{
		const Optional<qint32> actual{};
		QVERIFY_THAT(actual.valueOrDefault(), equals(0));
	}

	void valueOrDefault_HasPodValue_ShouldBeValue()
	{
		const Optional<qint32> actual{1234};
		QVERIFY_THAT(actual.valueOrDefault(), equals(1234));
	}

	void valueOrDefault_HasNoObjectalue_ShouldBeDefault()
	{
		const Optional<string> actual{};
		QVERIFY_THAT(actual.valueOrDefault(), equals(string{}));
	}

	void valueOrDefault_HasObjectValue_ShouldBeValue()
	{
		const Optional<string> actual{"1234"s};
		QVERIFY_THAT(actual.valueOrDefault(), equals("1234"s));
	}

	//}
};

}}}

QTEST_MAIN(drn::foundation::tests::OptionalUT)
#include "Optional-unit-tests.moc"

