/*******************************************************************************
***  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 <qt-mocks/Matchers.h>
using qt_mocks::equals;
using qt_mocks::isFalse;
using qt_mocks::isTrue;
#include <QString>
using ::QString;
#include <QTest>
// using QTEST_MAIN

#include <string>
using std::string;
#include <utility>
using std::size_t;

#include <foundation/Error.h>
using drn::foundation::Error;
#include <foundation/TypeIndexMap.hpp>
using drn::foundation::TypeIndexMap;


namespace
{

struct Tester0
{};
struct Tester1
{};
struct Str : string
{
	using string::string;
};

QString presentationText(const Str& s)
{
	return QString::fromStdString(s);
}

}

namespace drn
{
namespace foundation
{
namespace tests
{

struct TypeIndexMapUT : QObject
{
	Q_OBJECT

private slots:
	//{ constructor Tests

	void constructor_Default_ShouldBeExpected()
	{
		TypeIndexMap<Str, int> tim{};
		QVERIFY_TRUE(tim.empty());
		QVERIFY_THAT(tim.total(), equals(static_cast<size_t>(0)));
	}

	//}

	//{ add Tests

	void add_doesNotHaveValue_ShouldBeAdded()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		QVERIFY_THAT(tim.total(), equals(static_cast<size_t>(1)));
		QVERIFY_THAT(tim.value<Tester0>("first"), equals(15));
	}

	void add_doesNotHaveMultipleValues_ShouldBeAdded()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		tim.add<Tester1>("first", 30);
		tim.add<Tester0>("third", 45);
		QVERIFY_THAT(tim.total(), equals(static_cast<size_t>(3)));
		QVERIFY_THAT(tim.value<Tester0>("first"), equals(15));
		QVERIFY_THAT(tim.value<Tester1>("first"), equals(30));
		QVERIFY_THAT(tim.value<Tester0>("third"), equals(45));
	}

	void add_doesHaveValue_ShouldBeUpdated()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		tim.add<Tester0>("first", 30);
		QVERIFY_THAT(tim.value<Tester0>("first"), equals(30));
	}

	//}

	//{ value Tests

	void value_doesNotHaveType_ShouldThrow()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		QVERIFY_EXCEPTION_THROWN(tim.value<Tester1>("first"), Error);
	}

	void value_doesNotHavekey_ShouldThrow()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		QVERIFY_EXCEPTION_THROWN(tim.value<Tester0>("second"), Error);
	}

	//}

	//{ has Tests

	void has_doesHaveTypeKey_ShouldBeTrue()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		QVERIFY_THAT(tim.has<Tester0>("first"), isTrue());
	}

	void has_doesNotHaveType_ShouldBeFalse()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		QVERIFY_THAT(tim.has<Tester1>("first"), isFalse());
	}

	void has_doesNotHaveKey_ShouldBeFalse()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		tim.add<Tester1>("second", 15);
		QVERIFY_THAT(tim.has<Tester0>("second"), isFalse());
	}

	//}

	//{ remove Tests

	void remove_doesHaveKeyType_ShouldBeRemoved()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		tim.add<Tester1>("first", 30);
		tim.add<Tester0>("third", 45);
		QVERIFY_NO_THROW(tim.remove<Tester0>("third"));
		QVERIFY_THAT(tim.has<Tester0>("third"), isFalse());
		QVERIFY_THAT(tim.total(), equals(static_cast<size_t>(2)));
	}

	void remove_lastElement_ShouldBeEmpty()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("third", 45);
		QVERIFY_NO_THROW(tim.remove<Tester0>("third"));
		QVERIFY_THAT(tim.has<Tester0>("third"), isFalse());
		QVERIFY_THAT(tim.total(), equals(static_cast<size_t>(0)));
		QVERIFY_TRUE(tim.empty());
	}

	void remove_doesNotHaveType_ShouldThrow()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		tim.add<Tester0>("third", 45);
		QVERIFY_EXCEPTION_THROWN(tim.remove<Tester1>("third"), Error);
		QVERIFY_THAT(tim.total(), equals(static_cast<size_t>(2)));
	}

	void remove_doesNotHaveKey_ShouldThrow()
	{
		TypeIndexMap<Str, int> tim{};
		tim.add<Tester0>("first", 15);
		tim.add<Tester1>("first", 30);
		tim.add<Tester0>("third", 45);
		QVERIFY_EXCEPTION_THROWN(tim.remove<Tester1>("third"), Error);
		QVERIFY_THAT(tim.total(), equals(static_cast<size_t>(3)));
	}

	//}
};

}}}

QTEST_MAIN(drn::foundation::tests::TypeIndexMapUT)
#include "TypeIndexMap-unit-tests.moc"
