#pragma once

#ifndef __MODERN_CPP_11_H
#define __MODERN_CPP_11_H

#include <thread>
#include <vector>
#include <unordered_map>
#include <initializer_list>

// global macros
#define IGNORE_EXCEPTION(METHOD) { try { METHOD(); } catch (...) {} }

typedef double double_t;	// OLD: define double_t as an alias for type double
using double_t = double;	// C++11: exactly SAME as above! use 1 or the other

using namespace std;

// https://www.learncpp.com/cpp-tutorial/b-1-introduction-to-c11/

namespace MODERNCPP
{
	// C++11, tells the compiler not to instantiate the template in this translation unit.
	extern template class std::vector<std::string>;

	// for enum casting
	enum class WeekDay { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

	template <typename Enumeration>
	auto as_integer(Enumeration const value)
		-> typename std::underlying_type<Enumeration>::type
	{
		return static_cast<typename std::underlying_type<Enumeration>::type>(value);
	}

	// for static_assert
	template <class T>
	void swap(T& a, T& b)
	{
		static_assert(std::is_copy_constructible<T>::value,
			"Swap requires copying");
		static_assert(std::is_nothrow_copy_constructible<T>::value
			&& std::is_nothrow_copy_assignable<T>::value,
			"Swap requires nothrow copy/assign");
		auto c = b;
		b = a;
		a = c;
	}

	template <class T>
	struct data_structure
	{
		static_assert(std::is_default_constructible<T>::value,
			"Data Structure requires default-constructible elements");
	};

	// Variadic template
	template<typename T>
	T adder(T v) { return v; }

	template<typename T, typename... Args>
	T adder(T first, Args... args) { return first + adder(args...); }

	// A generic function which finds minimum of two values 
	// return type is type of variable which is minimum 
	template <class A, class B>
	auto findMin(A a, B b) -> decltype(a < b ? a : b)
	{
		return (a < b) ? a : b;
	}

	// ignore exceptions
	template< typename Callable, typename... Arguments > void
		Ingnore_Exceptions(Callable && _method, Arguments && ... args) noexcept
	{
		try { _method(::std::forward< Arguments >(args)...); } catch (...) {}	//  Do nothing.
	}

	// explicit default and delete
	struct no_copy
	{
		no_copy(const no_copy&) = delete;
		no_copy() = default;
	};

	struct no_default
	{
		no_default() = delete;
	};

	class Base {
	public:

		virtual ~Base() noexcept {}					// C++11, compile-time check make sure no throw
		virtual bool BaseMethod() { return false; }
		virtual void BaseMethod(int n) final {}		// not overridable at sub-classes

		virtual const char* getName() const { return "Base"; }
		virtual int getValue() const { return m_value; }
		virtual void setValue(int value) { m_value = value; }

	public:
		int m_value{ 0 };
	};

	class Cpp11 : public Base {
	public:

		explicit Cpp11() noexcept {}			// cannot called implicitly
		Cpp11(Cpp11&&);							// MovableClass, typical declaration of a move constructor.
		Cpp11& operator=(Cpp11&& c);			// MovableClass operator =
		//Cpp11&& operator=(Cpp11&&) = default;	// Forcing a move constructor to be generated by the compiler.
		//Cpp11&& operator=(Cpp11&&) = delete;	// Avoiding implicit move constructor.			
		Cpp11(int id) : Cpp11(id, 1) {}			// Use a delegating constructors to minimize redundant code
		Cpp11(int id1, int id2 = 0) : count(0), member(id2) {}
		Cpp11(const std::initializer_list<int> &);				// using initializer list
		Cpp11& operator=(const std::initializer_list<int> &);	// inializer list assignment made possible

		int& operator[](int);					// overload [] for array access
		
		constexpr int ConstExpr(int x, int y) { return (x * y); }	// const expression

		// overrides
		bool BaseMethod() override { return true; }
		const char* getName() const override { return "Cpp11"; }

		long getLength() { return m_vMap.size(); }
		void ForEachLoop();
		long EnumClass(WeekDay);

		// LAMBDAs
		// https://en.cppreference.com/w/cpp/language/lambda
		int x, y;
		int operator()(int);
		void Lambda();

		void StaticAssert();					// Performs compile-time assertion checking
		long PseudoNumberGenerator(int, int);	// using default engine seed
		long RandomNumberGenerator(int, int);	// using mt19937 engine seed
		void Tuple();							// multiple value returns, instead of struct
		void UniquePtr();						// C++11, most used smart pointer
		void NullPtr(std::nullptr_t ptr) {}		// accepts nullptr, sucessor to NULL
		void ReferenceWrapper();
		void InitializerList();
		void VariadicTemplate();
		void UnorderedContainers();
		void RegularExpression();
		void VariableSizes();
		auto TrailingReturnType() const -> int;
		void DeclType();

		// for threading
		// https://thispointer.com/c-11-multithreading-part-1-three-different-ways-to-create-threads/
		static void DoWork(void*);
		void DoWorkInternal();
		std::thread DoWorkAsync(int);
		void ParallelThreads();
		void ProducerConsumer();
		void DetachedThread();

		// Indicates that the function does not return.
		[[noreturn]] void NoReturn() { throw "error"; }		// OK

		// friend
		friend std::ostream& operator<<(std::ostream&, const Cpp11&);

	private:

		// tuple
		std::tuple<int, double> ReturnTuple();

	private:

		// initialize
		int member = 0;
		int count{};		// uniform initialization, default initialization to 0
		std::vector<int> m_vMap = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

		// Create an unordered_map of three strings (that map to strings)
		std::unordered_map<std::string, std::string> u = {
			{"RED","#FF0000"},
			{"GREEN","#00FF00"},
			{"BLUE","#0000FF"}
		};

		// smart pointers
		// https://www.codeproject.com/Articles/541067/Cplusplus-Smart-Pointers

		//std::auto_ptr<Base> ap;								// C++98, deprecated in C++17
		std::shared_ptr<Base> sp = std::make_shared<Base>();	// C++11, released until all shared pointer out of scope
		std::weak_ptr<Base> wp = sp;							// C++11, shared pointer, resolve cyclic reference
		std::unique_ptr<Base> up;								// C++11, replacement for auto pointer, 1 reference
	};
}

#endif