#pragma once

// Use the C++ standard templated min and max functions.
#define NOMINMAX

#include <xdk.h>
#if _XDK_EDITION >= 170607 || _XDK_EDITION <= 171101
# pragma warning(disable: 5040) // dynamic exception specifications are valid only in C++14 and earlier; treating as noexcept(false)
#endif
#include <wrl/client.h>
#include <d3d11_x.h>
#include <DirectXMath.h>
#include <DirectXColors.h>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <unordered_set>

#include <pix.h>

#include "winrt/Windows.ApplicationModel.h"
#include "winrt/Windows.ApplicationModel.Core.h"
#include "winrt/Windows.ApplicationModel.Activation.h"
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Foundation.Collections.h"
#include "winrt/Windows.UI.Core.h"
#include "winrt/Windows.XBox.Input.h"

#if _XDK_EDITION >= 170607 || _XDK_EDITION <= 171101
# pragma warning(default: 5040)
#endif

#ifdef _UNICODE
# define to_tstring std::to_wstring
using tistream= std::wistream;
using tistringstream= std::wistringstream;
using tofstream= std::wofstream;
using tostream= std::wostream;
using tostringstream= std::wostringstream;
using tstringstream= std::wstringstream;
#else
# define to_tstring std::to_string
using tistream= std::istream;
using tistringstream= std::istringstream;
using tofstream= std::ofstream;
using tostream= std::ostream;
using tostringstream= std::ostringstream;
using tstringstream= std::stringstream;
#endif

namespace DX {
	inline void ThrowIfFailed(HRESULT hr) {
		if(FAILED(hr)) {
			// Set a breakpoint on this line to catch DirectX API errors
			throw std::exception();
		}
	}
}

#define FromPlatformError HRESULT_FROM_WIN32
#define _T(s) L##s

#include "../../lib/Shared/Internal.h"
#include "../Win32LibTest/Utilities.h"

tostream& operator<<(tostream& os, TwitchInGames::Stream::StreamType const type);

#define TEST(caseName,testName) void caseName::testName()
#define EXPECT_TRUE(expr) do { bool result_= PrintResult((expr) ? nullptr : #expr); if(!result_) return; } while(false)
#define EXPECT_FALSE(expr) do { bool result_= PrintResult((expr) ? "!" #expr : nullptr); if(!result_) return; } while(false)
#define EXPECT_EQ(actual,expected) do { auto actual_= (actual); auto expected_= (expected); bool result_= PerformTest(actual_, expected_, [&actual_, &expected_] { return actual_ == expected_; }, "=="); if(!result_) return; } while(false)
#define EXPECT_GE(actual,expected) do { auto actual_= (actual); auto expected_= (expected); bool result_= PerformTest(actual_, expected_, [&actual_, &expected_] { return actual_ >= expected_; }, ">="); if(!result_) return; } while(false)
#define EXPECT_LE(actual,expected) do { auto actual_= (actual); auto expected_= (expected); bool result_= PerformTest(actual_, expected_, [&actual_, &expected_] { return actual_ <= expected_; }, "<="); if(!result_) return; } while(false)
#define EXPECT_NE(actual,expected) do { auto actual_= (actual); auto expected_= (expected); bool result_= PerformTest(actual_, expected_, [&actual_, &expected_] { return actual_ != expected_; }, "!="); if(!result_) return; } while(false)
#define EXPECT_STREQ(actual,expected) do { auto actual_= (actual); auto expected_= (expected); bool result_= PerformTest(actual_, expected_, [&actual_, &expected_] { return strcmp(actual_, expected_) == 0; }, "=="); if(!result_) return; } while(false)
#define EXPECT_ANY_THROW(statement) do { try { statement; PrintResult(#statement " did not throw"); } catch(...) { PrintResult(nullptr); } } while(false)

extern char const* testName;

inline bool PrintResult(char const* s) {
	using namespace TwitchInGames;
	tstringstream ss;
	ss << "TEST CASE " << testName << ' ';
	if(s == nullptr) {
		ss << "SUCCEEDED";
	} else {
		ss << "FAILED: " << s;
	}
	DebugWriteLine(ss.str().c_str());
	return s == nullptr;
}

template<typename FN, typename U, typename T>
inline bool PerformTest(T const& actual, U const& expected, FN fn, char const* s) {
	if(fn()) {
		return PrintResult(nullptr);
	}
	tstringstream ss;
	ss << std::endl << "expected: " << expected << std::endl << s << std::endl << "  actual: " << actual;
	return PrintResult(TwitchInGames::FromTstring(ss.str()).c_str());
}

template<typename FN, typename U>
inline bool PerformTest(std::string const& actual, U const& expected, FN fn, char const* s) {
	if(fn()) {
		return PrintResult(nullptr);
	}
	tstringstream ss;
	ss << std::endl << "expected: " << expected << std::endl << s << std::endl << "  actual: " << TwitchInGames::ToTstring(actual);
	return PrintResult(TwitchInGames::FromTstring(ss.str()).c_str());
}

template<typename FN>
inline bool PerformTest(std::string const& actual, std::string const& expected, FN fn, char const* s) {
	if(fn()) {
		return PrintResult(nullptr);
	}
	tstringstream ss;
	ss << std::endl << "expected: " << ToTstring(expected) << std::endl << s << std::endl << "  actual: " << TwitchInGames::ToTstring(actual);
	return PrintResult(TwitchInGames::FromTstring(ss.str()).c_str());
}
