#include "pch.h"
#include "Cpp14.h"

#include <string>
#include <iostream>
#include <algorithm>

namespace MODERNCPP
{
	void Cpp14::AggregateMemberInitialization()
	{
		std::cout << x.length << " " << x.width << endl;
	}

	void Cpp14::BinaryLiterals()
	{
		// C++98
		int bin(0);
		bin = 0x01;		// assign binary 0000 0001 to the variable
		bin = 0x02;		// assign binary 0000 0010 to the variable
		bin = 0x04;		// assign binary 0000 0100 to the variable
		bin = 0x08;		// assign binary 0000 1000 to the variable
		bin = 0x10;		// assign binary 0001 0000 to the variable
		bin = 0x20;		// assign binary 0010 0000 to the variable
		bin = 0x40;		// assign binary 0100 0000 to the variable
		bin = 0x80;		// assign binary 1000 0000 to the variable
		bin = 0xFF;		// assign binary 1111 1111 to the variable
		bin = 0xB3;		// assign binary 1011 0011 to the variable
		bin = 0xF770;	// assign binary 1111 0111 0111 0000 to the variable
		std::cout << bin << endl;

		// C++14
		// In C++14, we can assign binary literals by using the 0b prefix:
		bin = 0;
		bin = 0b1;						// assign binary 0000 0001 to the variable
		bin = 0b11;						// assign binary 0000 0011 to the variable
		bin = 0b1010;					// assign binary 0000 1010 to the variable
		bin = 0b11110000;				// assign binary 1111 0000 to the variable
		std::cout << bin << endl;

		bin = 0b1011'0010;				// assign binary 1011 0010 to the variable
		long value = 2'132'673'462;		// much easier to read than 2132673462
		std::cout << bin << endl;
	}

	void Cpp14::DigitSeparators()
	{
		auto integer_literal = 1'000'000;
		std::cout << integer_literal << endl;

		auto floating_point_literal = 0.000'015'3;
		std::cout << floating_point_literal << endl;

		auto binary_literal = 0b0100'1100'0110;
		std::cout << binary_literal << endl;

		auto silly_example = 1'0'0'000'00;
		std::cout << silly_example << endl;
	}

	void Cpp14::GenericLambdaExpr()
	{
		// For example, this generic lambda-expression containing statement:
		auto L = [](const auto& x, auto& y) { return x + y; };

		// might result in the creation of a closure type, and object that behaves similar to the struct below:
		/*
		struct // anonymous
		{
			template <typename T, typename U>
			auto operator()(const T& x, U& y) const // N3386 Return type deduction
			{
				return x + y;
			}
		} L;
		*/
		std::cout << typeid(L).name() << endl;
	}

	void Cpp14::LambdaCaptureExpr()
	{
		// use of an initializer expression:
		auto lambda1 = [value = 1]{ return value; };
		std::cout << lambda1() << endl;

		// capture by move, via the use of the standard std::move function
		std::unique_ptr<int> ptr(new int(10));
		auto lambda2 = [value = std::move(ptr)]{ return *value; };
		std::cout << lambda2() << endl;
	}

	void Cpp14::ConstantExprRestriction()
	{
		std::vector<int> a, b;
		//This does not compile but as my understadnding of `constexpr` this should
		//std::vector::size is not marked constexpr
		//int array[std::max(a.size(), b.size()];

		//This is trivial use that does compile
		int array[std::max(1, 2)];
	}

	void Cpp14::VariableTemplate()
	{
		auto x = pi<double>;		// can be int, double, float, etc
		std::cout << x << endl;

		auto y = pi<const char*>;	// can be int, double, float, etc
		std::cout << y << endl;

		auto z = circular_area<double>(3.0);	// can be int, double, float, etc
		std::cout << z << endl;		
	}

	void Cpp14::TupleAddressingByType()
	{
		std::tuple<string, string, int> t("foo", "bar", 7);
		int i = get<int>(t);        // i == 7
		int j = get<2>(t);          // Same as before: j == 7
		//string s = get<string>(t);  // Compile-time error due to ambiguity
		string s = get<0>(t);

		std::cout << i << j << s << endl;
	}

	void Cpp14::MakeUnique()
	{
		// https://www.learncpp.com/cpp-tutorial/15-5-stdunique_ptr/

		// Create a single dynamically allocated Fraction with numerator 3 and denominator 5
		std::unique_ptr<Fraction> f1 = std::make_unique<Fraction>(3, 5);
		std::cout << *f1 << '\n';

		// Create a dynamically allocated array of Fractions of length 4
		// We can also use automatic type deduction to good effect here
		auto f2 = std::make_unique<Fraction[]>(4);
		std::cout << f2[0] << '\n';
	}

	void Cpp14::HeterogeneousLookupAssocContainers()
	{
		// less
		int foo[] = { 10,20,5,15,25 };
		int bar[] = { 15,10,20 };
		std::sort(foo, foo + 5, std::less<int>());  // 5 10 15 20 25
		std::sort(bar, bar + 3, std::less<int>());  //   10 15 20
		if (std::includes(foo, foo + 5, bar, bar + 3, std::less<int>()))
			std::cout << "foo includes bar.\n";

		// greater
		int numbers[] = { 20,40,50,10,30 };
		std::sort(numbers, numbers + 5, std::greater<int>());
		for (int i = 0; i < 5; i++)
			std::cout << numbers[i] << ' ';
		std::cout << '\n';
	}

	void Cpp14::SharedMutexesLocking()
	{
		ThreadSafeCounter counter;

		auto increment_and_print = [&counter]() 
		{
			for (int i = 0; i < 3; i++) 
			{
				counter.increment();
				std::cout << std::this_thread::get_id() << ' ' << counter.get() << '\n';

				// Note: Writing to std::cout actually needs to be synchronized as well
				// by another std::mutex. This has been omitted to keep the example small.
			}
		};

		std::thread thread1(increment_and_print);
		std::thread thread2(increment_and_print);

		thread1.join();
		thread2.join();
	}

	void Cpp14::DeprecatedMethod()
	{
		std::cout << "DeprecatedMethod should display a compile warning!\n"
			<< "But somehow not really working, need to figure out why"
			<< endl;
	}
}