#include <gmock/gmock.h>
#include "tools/Lazy.h"

using namespace testing;

// Not copyable, no default constructor, movable
struct Foo {
	const int value;
	Foo(int value) : value(value) {}

	Foo() = delete;
	Foo(const Foo&) = delete;
	Foo& operator=(const Foo&) = delete;

	Foo(Foo&&) = default;
	Foo& operator=(Foo&&) = default;
};

TEST(Lazy, basicUsage) {
	bool lambdaCalled = false;
	Lazy<Foo> lazy([&lambdaCalled] { lambdaCalled = true; return Foo(42); });
	EXPECT_FALSE(lambdaCalled);
	EXPECT_FALSE(static_cast<bool>(lazy));
	EXPECT_EQ(42, (*lazy).value);
	EXPECT_EQ(42, lazy.value().value);
	EXPECT_EQ(42, lazy->value);
	EXPECT_TRUE(lambdaCalled);
	EXPECT_TRUE(static_cast<bool>(lazy));
}

TEST(Lazy, constUsage) {
	bool lambdaCalled = false;
	const Lazy<Foo> lazy([&lambdaCalled] { lambdaCalled = true; return Foo(42); });
	EXPECT_FALSE(lambdaCalled);
	EXPECT_FALSE(static_cast<bool>(lazy));
	EXPECT_EQ(42, (*lazy).value);
	EXPECT_EQ(42, lazy.value().value);
	EXPECT_EQ(42, lazy->value);
	EXPECT_TRUE(lambdaCalled);
	EXPECT_TRUE(static_cast<bool>(lazy));
}

TEST(Lazy, copying) {
	Lazy<Foo> a;
	int counter = 0;
	const auto createValue = [&] { return counter++; };
	Lazy<Foo> b(createValue);
	a = b;
	EXPECT_EQ(0, counter);
	EXPECT_FALSE(static_cast<bool>(a));
	EXPECT_FALSE(static_cast<bool>(b));
	EXPECT_EQ(0, a->value);
	EXPECT_EQ(1, counter);
	EXPECT_TRUE(static_cast<bool>(a));
	EXPECT_TRUE(static_cast<bool>(b));
	EXPECT_EQ(0, b->value);
	Lazy<Foo> c(createValue);
	EXPECT_EQ(1, c->value);
}

using Expensive = Foo;
#define member value;

TEST(Lazy, demo) {
	// Constructor takes function
	Lazy<Expensive> lazy([] { return Expensive(42); });

	// Multiple ways to access value
	Expensive& a = *lazy;
	Expensive& b = lazy.value();
	auto c = lazy->member;

	// Check if initialized
	if (lazy) { /* ... */ }
}