std::string &strToLower(std::string &str) {
    transform(str.begin(), str.end(), str.begin(), towlower);
    return str;
}

/*
 *
 * This file is part of ZLToolKit(http://example.com).
 *
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include <assert.h>
#include <stdlib.h>
#include <mutex>
#include <string>
#include <algorithm>
#include <unordered_map>

#include "util.h"
#include "onceToken.h"
#include "Util/File.h"
#include "Util/logger.h"
#include "Util/uv_errno.h"
#include "Network/sockutil.h"

#if defined(_WIN32)
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
extern "C" const IMAGE_DOS_HEADER __ImageBase;
#endif // defined(_WIN32)

#if defined(__MACH__) || defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
int uv_exepath(char* buffer, int *size) {
    /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
    char abspath[PATH_MAX * 2 + 1];
    char exepath[PATH_MAX + 1];
    uint32_t exepath_size;
    size_t abspath_size;

    if (buffer == NULL || size == NULL || *size == 0)
        return -EINVAL;

    exepath_size = sizeof(exepath);
    if (_NSGetExecutablePath(exepath, &exepath_size))
        return -EIO;

    if (realpath(exepath, abspath) != abspath)
        return -errno;

    abspath_size = strlen(abspath);
    if (abspath_size == 0)
        return -EIO;

    *size -= 1;
    if (*size > abspath_size)
        *size = abspath_size;

    memcpy(buffer, abspath, *size);
    buffer[*size] = '\0';

    return 0;
}
#endif //defined(__MACH__) || defined(__APPLE__)

using namespace std;

namespace toolkit {

string makeRandStr(int sz, bool printable) {
    char *tmp = new char[sz + 1];
    static const char CCH[] =
        "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int i;
    for (i = 0; i < sz; i++) {
        srand((unsigned)time(NULL) + i);
        if (printable) {
            int x = rand() % (sizeof(CCH) - 1);
            tmp[i] = CCH[x];
        }
        else {
            tmp[i] = rand() % 0xFF;
        }
    }
    tmp[i] = 0;
    string ret = tmp;
    delete[] tmp;
    return ret;
}

bool is_safe(uint8_t b) {
    return b >= ' ' && b < 128;
}
string hexdump(const void *buf, size_t len) {
    string ret("\r\n");
    char tmp[8];
    const uint8_t *data = (const uint8_t *)buf;
    for (size_t i = 0; i < len; i += 16) {
        for (int j = 0; j < 16; ++j) {
            if (i + j < len) {
                int sz = snprintf(tmp, sizeof(tmp), "%.2x ", data[i + j]);
                ret.append(tmp, sz);
            }
            else {
                int sz = snprintf(tmp, sizeof(tmp), "   ");
                ret.append(tmp, sz);
            }
        }
        for (int j = 0; j < 16; ++j) {
            if (i + j < len) {
                ret += (is_safe(data[i + j]) ? data[i + j] : '.');
            }
            else {
                ret += (' ');
            }
        }
        ret += ('\n');
    }
    return ret;
}

string hexmem(const void* buf, size_t len) {
	string ret;
	char tmp[8];
	const uint8_t* data = (const uint8_t*)buf;
	for (size_t i = 0; i < len; ++i) {
		int sz = sprintf(tmp, "%.2x ", data[i]);
		ret.append(tmp, sz);
	}
	return ret;
}

string exePath(bool isExe /*= true*/) {
    char buffer[PATH_MAX * 2 + 1] = { 0 };
    int n = -1;
#if defined(_WIN32)
    n = GetModuleFileNameA(isExe?NULL:(HINSTANCE)&__ImageBase, buffer, sizeof(buffer));
#elif defined(__MACH__) || defined(__APPLE__)
    n = sizeof(buffer);
    if (uv_exepath(buffer, &n) != 0) {
        n = -1;
    }
#elif defined(__linux__)
    n = readlink("/proc/self/exe", buffer, sizeof(buffer));
#endif

    string filePath;
    if (n <= 0) {
        filePath = "./";
    }
    else {
        filePath = buffer;
    }

#if defined(_WIN32)
    //windows下把路径统一转换层unix风格，因为后续都是按照unix风格处理的
    for (auto &ch : filePath) {
        if (ch == '\\') {
            ch = '/';
        }
    }
#endif //defined(_WIN32)

    return filePath;
}

string exeDir(bool isExe /*= true*/) {
    auto path = exePath(isExe);
    return path.substr(0, path.rfind('/') + 1);
}

string exeName(bool isExe /*= true*/) {
    auto path = exePath(isExe);
    return path.substr(path.rfind('/') + 1);
}

// string转小写

// string转大写
std::string &strToUpper(std::string &str) {
    transform(str.begin(), str.end(), str.begin(), towupper);
    return str;
}

// string转小写
std::string strToLower(std::string &&str) {
    transform(str.begin(), str.end(), str.begin(), towlower);
    return std::move(str);
}

// string转大写
std::string strToUpper(std::string &&str) {
    transform(str.begin(), str.end(), str.begin(), towupper);
    return std::move(str);
}

vector<string> split(const string& s, const char *delim) {
    vector<string> ret;
    size_t last = 0;
    auto index = s.find(delim, last);
    while (index != string::npos) {
        if (index - last > 0) {
            ret.push_back(s.substr(last, index - last));
        }
        last = index + strlen(delim);
        index = s.find(delim, last);
    }
    if (!s.size() || s.size() - last > 0) {
        ret.push_back(s.substr(last));
    }
    return ret;
}

#define TRIM(s,chars) \
do{ \
    string map(0xFF, '\0'); \
    for (auto &ch : chars) { \
        map[(unsigned char &)ch] = '\1'; \
    } \
    while( s.size() && map.at((unsigned char &)s.back())) s.pop_back(); \
    while( s.size() && map.at((unsigned char &)s.front())) s.erase(0,1); \
}while(0);

//去除前后的空格、回车符、制表符
std::string& trim(std::string &s, const string &chars) {
    TRIM(s, chars);
    return s;
}
std::string trim(std::string &&s, const string &chars) {
    TRIM(s, chars);
    return std::move(s);
}

void replace(string &str, const string &old_str, const string &new_str) {
    if (old_str.empty() || old_str == new_str) {
        return;
    }
    auto pos = str.find(old_str);
    if (pos == string::npos) {
        return;
    }
    str.replace(pos, old_str.size(), new_str);
    replace(str, old_str, new_str);
}

bool start_with(const string &str, const string &substr) {
    return str.find(substr) == 0;
}

bool end_with(const string &str, const string &substr) {
    auto pos = str.rfind(substr);
    return pos != string::npos && pos == str.size() - substr.size();
}

bool isIP(const char *str){
    return INADDR_NONE != inet_addr(str);
}

#if defined(_WIN32)
void sleep(int second) {
    Sleep(1000 * second);
}
void usleep(int micro_seconds) {
    this_thread::sleep_for(std::chrono::microseconds(micro_seconds));
}

int gettimeofday(struct timeval *tp, void *tzp) {
    auto now_stamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
    tp->tv_sec = (decltype(tp->tv_sec))(now_stamp / 1000000LL);
    tp->tv_usec = now_stamp % 1000000LL;
    return 0;
}

const char *strcasestr(const char *big, const char *little){
    string big_str = big;
    string little_str = little;
    strToLower(big_str);
    strToLower(little_str);
    auto pos = strstr(big_str.data(), little_str.data());
    if (!pos){
        return nullptr;
    }
    return big + (pos - big_str.data());
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int vasprintf(char **strp, const char *fmt, va_list ap) {
    // _vscprintf tells you how big the buffer needs to be
    int len = _vscprintf(fmt, ap);
    if (len == -1) {
        return -1;
    }
    size_t size = (size_t)len + 1;
    char *str = (char*)malloc(size);
    if (!str) {
        return -1;
    }
    // _vsprintf_s is the "secure" version of vsprintf
    int r = vsprintf_s(str, len + 1, fmt, ap);
    if (r == -1) {
        free(str);
        return -1;
    }
    *strp = str;
    return r;
}

 int asprintf(char **strp, const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int r = vasprintf(strp, fmt, ap);
    va_end(ap);
    return r;
}

#endif //WIN32

static inline uint64_t getCurrentMicrosecondOrigin() {
#if !defined(_WIN32)
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000LL + tv.tv_usec;
#else
    return  std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
#endif
}
static atomic<uint64_t> s_currentMicrosecond(0);
static atomic<uint64_t> s_currentMillisecond(0);
static atomic<uint64_t> s_currentMicrosecond_system(getCurrentMicrosecondOrigin());
static atomic<uint64_t> s_currentMillisecond_system(getCurrentMicrosecondOrigin() / 1000);

static inline bool initMillisecondThread() {
    static std::thread s_thread([]() {
        setThreadName("stamp thread");
        DebugL << "Stamp thread started!";
        uint64_t last = getCurrentMicrosecondOrigin();
        uint64_t now;
        uint64_t microsecond = 0;
        while (true) {
            now = getCurrentMicrosecondOrigin();
            //记录系统时间戳，可回退
            s_currentMicrosecond_system.store(now, memory_order_release);
            s_currentMillisecond_system.store(now / 1000, memory_order_release);

            //记录流逝时间戳，不可回退
            int64_t expired = now - last;
            last = now;
            if (expired > 0 && expired < 1000 * 1000) {
                //流逝时间处于0~1000ms之间，那么是合理的，说明没有调整系统时间
                microsecond += expired;
                s_currentMicrosecond.store(microsecond, memory_order_release);
                s_currentMillisecond.store(microsecond / 1000, memory_order_release);
            } else if(expired != 0){
                WarnL << "Stamp expired is not abnormal:" << expired;
            }
            usleep(500);
        }
    });
    static onceToken s_token([]() {
        s_thread.detach();
    });
    return true;
}

uint64_t getCurrentMillisecond(bool system_time) {
    static bool flag = initMillisecondThread();
    if(system_time){
        return s_currentMillisecond_system.load(memory_order_acquire);
    }
    return s_currentMillisecond.load(memory_order_acquire);
}

uint64_t getCurrentMicrosecond(bool system_time) {
    static bool flag = initMillisecondThread();
    if(system_time){
        return s_currentMicrosecond_system.load(memory_order_acquire);
    }
    return s_currentMicrosecond.load(memory_order_acquire);
}

string getTimeStr(const char *fmt, time_t time) {
    if (!time) {
        time = ::time(NULL);
    }
    auto tm = getLocalTime(time);
    char buffer[64];
    auto success = std::strftime(buffer, sizeof(buffer), fmt, &tm);
    return 0 == success ? string(fmt) : buffer;
}

struct tm getLocalTime(time_t sec) {
    struct tm tm;
#ifdef _WIN32
    localtime_s(&tm, &sec);
#else
    localtime_r(&sec, &tm);
#endif //_WIN32
    return tm;
}

void setThreadName(const char *name) {
#if defined(__linux) || defined(__linux__)
    assert(strlen(name) < 16); // linux平台下线程名字长度需小于16
    pthread_setname_np(pthread_self(), name);
#elif defined(__MACH__) || defined(__APPLE__)
    pthread_setname_np(name);
#elif defined(_WIN32)
    // SetThreadDescription was added in 1607 (aka RS1). Since we can't guarantee the user is running 1607 or later, we need to ask for the function from the kernel.
    using SetThreadDescriptionFunc = HRESULT(WINAPI * )(_In_ HANDLE hThread, _In_ PCWSTR lpThreadDescription);
    static auto setThreadDescription = reinterpret_cast<SetThreadDescriptionFunc>(::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "SetThreadDescription"));
    if (setThreadDescription) {
        // Convert the thread name to Unicode
        wchar_t threadNameW[MAX_PATH];
        size_t numCharsConverted;
        errno_t wcharResult = mbstowcs_s(&numCharsConverted, threadNameW, name, MAX_PATH - 1);
        if (wcharResult == 0) {
            HRESULT hr = setThreadDescription(::GetCurrentThread(), threadNameW);
            if (!SUCCEEDED(hr)) {
                int i = 0;
                i++;
            }
        }
    } else {
        // For understanding the types and values used here, please see:

        const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
        struct THREADNAME_INFO {
            DWORD dwType = 0x1000; // Must be 0x1000
            LPCSTR szName;         // Pointer to name (in user address space)
            DWORD dwThreadID;      // Thread ID (-1 for caller thread)
            DWORD dwFlags = 0;     // Reserved for future use; must be zero
        };
#pragma pack(pop)

        THREADNAME_INFO info;
        info.szName = name;
        info.dwThreadID = (DWORD) - 1;

        __try{
                RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<const ULONG_PTR *>(&info));
        } __except(GetExceptionCode() == MS_VC_EXCEPTION ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER) {
        }
    }
#endif
}

string getThreadName() {
#if ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) || (defined(__MACH__) || defined(__APPLE__)) || (defined(ANDROID) && __ANDROID_API__ >= 26)
    string ret;
    ret.resize(32);
    auto tid = pthread_self();
    pthread_getname_np(tid, (char *) ret.data(), ret.size());
    if (ret[0]) {
        ret.resize(strlen(ret.data()));
        return ret;
    }
    return to_string((uint64_t) tid);
#elif defined(_WIN32)
    using GetThreadDescriptionFunc = HRESULT(WINAPI * )(_In_ HANDLE hThread, _In_ PWSTR * ppszThreadDescription);
    static auto getThreadDescription = reinterpret_cast<GetThreadDescriptionFunc>(::GetProcAddress(::GetModuleHandleA("Kernel32.dll"), "GetThreadDescription"));

    if (!getThreadDescription) {
        std::ostringstream ss;
        ss << std::this_thread::get_id();
        return ss.str();
    } else {
        PWSTR data;
        HRESULT hr = getThreadDescription(GetCurrentThread(), &data);
        if (SUCCEEDED(hr) && data[0] != '\0') {
            char threadName[MAX_PATH];
            size_t numCharsConverted;
            errno_t charResult = wcstombs_s(&numCharsConverted, threadName, data, MAX_PATH - 1);
            if (charResult == 0) {
                LocalFree(data);
                std::ostringstream ss;
                ss << threadName;
                return ss.str();
            } else {
                return to_string((uint64_t) GetCurrentThreadId());
            }
        } else {
            return to_string((uint64_t) GetCurrentThreadId());
        }
    }
#else
    std::ostringstream ss;
    ss << std::this_thread::get_id();
    return ss.str();
#endif
}

bool setThreadAffinity(int i) {
#if (defined(__linux) || defined(__linux__)) && !defined(ANDROID)
    cpu_set_t mask;
    CPU_ZERO(&mask);
    if(i >= 0){
        CPU_SET(i, &mask);
    }
    if (!pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask)) {
        return true;
    }
    WarnL << "pthread_setaffinity_np failed:" << get_uv_errmsg();
#endif
    return false;
}

}  // namespace toolkit


void DrawTriangle()
{
	g3D->begin(); // Begin the scene
	g3D->clear(); // Clear our viewport

	g3D->render(gTriangle, 3); // Draw our triangle

	g3D->end(); // End the scene (Quit rendering and blit to the screen)
}

// Done by TheTutor

/*
	This tutorial is all about how to make your very own matrix class and a LITTLE bit about the
	linear algebra involved with matrices.

	So first off why make your own?  Doesn't D3D have a built in matrix class?  Well yes, D3D does have
	a built in matrix class, and a slew of math functions that can operate on them.  If you
	were only ever going to use D3D for your 3D API, then you might consider using all the built
	in D3D math functionality.  However, if you ever want to use any other API (say OpenGL) it's
	extremely useful to have your own math classes.  Thus, this tutorial is going an introduction
	to building up your own math library and interfacing it with D3D.

	With that, we enter the matrix... tutorial.
*/

#include <windows.h>
#include "d3d_obj.h"
#include "matrix.h"

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

#define DEG2RAD(x) (x * (D3DX_PI / 180.0f)) // Converts degrees to radians
#define RAD2DEG(x) (x * (180.0f / D3DX_PI)) // Converts radians to degrees

// Width and height of the window
const int kWinWid = 640;
const int kWinHgt = 480;

const char kClassName[] = "GT_D3DMatrixPart2"; // WNDCLASSEX's name

// The R, G, B of our triangle
const int kRed = 20;
const int kGreen = 20;
const int kBlue = 200;

const float kAngleInc = DEG2RAD(2); // Amount to add to the angle of rotation each frame

// Here's where we create our triangle.  By using a little math
// we'll rest the triangle on the X-axis
SVertex gTriangle[] =
{
	{ 1.0f, 0.0f, 0.0f, D3DCOLOR_XRGB(kRed,kGreen,kBlue), },
	{ -1.0f,  0.0f, 0.0f, D3DCOLOR_XRGB(kRed,kGreen,kBlue), },
	{ 0.0f, 2.0f, 0.0f, D3DCOLOR_XRGB(kRed,kGreen,kBlue), },
};

bool LockFrameRate(int frame_rate = 60); // Locks the frame rate
void DrawTriangle(); // Draws our triangle to the screen

// The WinProc
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);

// Main window program
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{
    HWND hwnd = NULL;
	MSG msg = {0};
    WNDCLASSEX wndclassex = {0};

	// Init fields we care about
	wndclassex.cbSize = sizeof(WNDCLASSEX); // Always set to size of WNDCLASSEX
    wndclassex.style = CS_HREDRAW | CS_VREDRAW;
    wndclassex.lpfnWndProc = WinProc;
    wndclassex.hInstance = hinstance;
	wndclassex.lpszClassName = kClassName;
	wndclassex.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW),
											IMAGE_CURSOR, 0, 0, LR_SHARED);

    RegisterClassEx(&wndclassex); // Register the WNDCLASSEX

	RECT rect = { 0, 0, kWinWid, kWinHgt }; // Desired window client rect

	DWORD winStyleEx = WS_EX_CLIENTEDGE; // Window's extended style
	DWORD winStyle = WS_CAPTION | WS_SYSMENU; // Window style

	// Adjust window rect so it gives us our desired client rect when we
	// create the window
	AdjustWindowRectEx(&rect, winStyle, false, winStyleEx);

	// Create the window
    hwnd = CreateWindowEx(winStyleEx,
					      kClassName,
						  "www.GameTutorials.com -- Matrices Part2",
						  winStyle,
						  CW_USEDEFAULT,
						  CW_USEDEFAULT,
						  rect.right - rect.left,
						  rect.bottom - rect.top,
						  NULL,
						  NULL,
						  hinstance,
						  NULL);

	// Init our global 3D object
	if(g3D->init(hwnd) == false)
		return EXIT_FAILURE; // There's been an error, lets get out of this joint

	// Get the client rect and make sure our client is the size we want
	GetClientRect(hwnd, &rect);
	assert(rect.right == kWinWid && rect.bottom == kWinHgt);

	// Set up our projection matrix
	// **NOTE** We only need to do this once cause we're not going to change it ever
	g3D->setProjMatrix(DEG2RAD(60), (float)kWinWid / (float)kWinHgt, 1.0f, 8192.0f);

	// Set up our view matrix
	// **NOTE** We only need to do this once cause we're not going to change it ever
	g3D->setViewMatrix(CPos(0.0f, 0.0f, -5.0f), CPos(0.0f, 0.0f, 0.0f));

	ShowWindow(hwnd, ishow);
	UpdateWindow(hwnd);

	// While the app is running...
    while(1)
	{
		// Handle any messages from the OS
		if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			if(msg.message == WM_QUIT)
				break;

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else if(LockFrameRate()) // If it's time to draw
		{
			static float angle = 0; // Angle of rotation

			// We want to move 2 units every second, since we're locking
			// the frame rate to 60 frames a second, we want to move 5 * (1/60) each frame
			static float moveAmt = 2.0f * (1.0f / 60.0f);
			static float curWorldX = 0.0f; // Current world x-position of triangle

			CMatrix mat1;
			CMatrix mat2;

			// Using our matrix class we'll create a rotation matrix and a translation
			// matrix
			mat1.makeRotX(angle);
			mat2.makeTranslate(CVector(curWorldX, 0.0f, 0.0f));

			// We multiply them together to get a matrix that contains both the rotation
			// and the translation
			CMatrix worldMat = mat1 * mat2;

			// We set our world matrix to the new matrix we created so our triangle
			// we'll be drawn with the updated translation and rotation
			g3D->setWorldMatrix(&(worldMat.getD3DMatrix()));
			DrawTriangle(); // Draw triangle in it's new rotated/translated position

			angle += kAngleInc;
			curWorldX += moveAmt;

			// Update moveAmt so we move side to side
			if(curWorldX > 1.0f)
				moveAmt = -moveAmt;
			else if(curWorldX < -1.0f)
				moveAmt = -moveAmt;
		}
		else
			Sleep(1); // Give the OS a slice of time to process other things

	} // end of while(1)

	g3D->deinit(); // Free up D3D object
	UnregisterClass(kClassName,hinstance); // Free up WNDCLASSEX
	    return EXIT_SUCCESS; // Application was a success
}

// WinProc
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	switch(message)
    {
		case WM_KEYDOWN:

			// If the user presses ESC, close the app
			if(wparam == VK_ESCAPE)
				SendMessage(hwnd, WM_CLOSE, 0, 0);

			return 0;

		case WM_DESTROY:
			PostQuitMessage(0);
				return 0;
    }

    return DefWindowProc(hwnd, message, wparam, lparam);
}

// Locks the frame rate at "frame_rate"
// Returns true when it's okay to draw, false otherwise
bool LockFrameRate(int frame_rate)
{
	static float lastTime = 0.0f;

	// Get current time in seconds (milliseconds * .001 = seconds)
	float currentTime = GetTickCount() * 0.001f;

	// Get the elapsed time by subtracting the current time from the last time
	// If the desired frame rate amount of seconds has passed -- return true (ie Blit())
	if((currentTime - lastTime) > (1.0f / frame_rate))
	{
		// Reset the last time
		lastTime = currentTime;
			return true;
	}

	return false;
}

// This draws our triangle to the screen

// A Matrix is 1/2 Math and 1/2 Irix

/*
	Hopefully you understand a wee bit more about matrices.  This tutorial is NOT a substitute
	for learning linear algebra, this tutorial only serves as a introduction to the
	linear algebra necessary to do 3D graphical programming.  If you are truly interested
	in doing 3D programming, you will immensely help yourself out by learning vector and
	matrix algebra.

	**CHALLENGE**

		The matrix class provided is only the start of something that could be beautiful.  There
		are further optimizations and methods that could be added to make it highly robust and
		user friendly to the 3D programmer.  See if you can't add the following functionality:

			Inverse of a matrix
			Multiplying of a vector
			Making a scaling matrix

		Chances are if you're not a linear algebra guru, you'll have to open a book or two %)

	If you have any questions about the matrices or 3D math in general, be sure to post 'em at
	the forums at:  www.GameTutorials.com.
*/

/*-------------------------*\
|  Programmed by:  TheTutor |
\*-------------------------*/


switch(message)
    {
		case WM_KEYDOWN:

			// If the user presses ESC, close the app
			if(wparam == VK_ESCAPE)
				SendMessage(hwnd, WM_CLOSE, 0, 0);

			return 0;

		case WM_DESTROY:
			PostQuitMessage(0);
				return 0;
    }

// Done by TheTutor

/*
	This tutorial is all about how to make your very own matrix class and a LITTLE bit about the
	linear algebra involved with matrices.

	So first off why make your own?  Doesn't D3D have a built in matrix class?  Well yes, D3D does have
	a built in matrix class, and a slew of math functions that can operate on them.  If you
	were only ever going to use D3D for your 3D API, then you might consider using all the built
	in D3D math functionality.  However, if you ever want to use any other API (say OpenGL) it's
	extremely useful to have your own math classes.  Thus, this tutorial is going an introduction
	to building up your own math library and interfacing it with D3D.

	With that, we enter the matrix... tutorial.
*/

#include <windows.h>
#include "d3d_obj.h"
#include "matrix.h"

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

#define DEG2RAD(x) (x * (D3DX_PI / 180.0f)) // Converts degrees to radians
#define RAD2DEG(x) (x * (180.0f / D3DX_PI)) // Converts radians to degrees

// Width and height of the window
const int kWinWid = 640;
const int kWinHgt = 480;

const char kClassName[] = "GT_D3DMatrixPart2"; // WNDCLASSEX's name

// The R, G, B of our triangle
const int kRed = 20;
const int kGreen = 20;
const int kBlue = 200;

const float kAngleInc = DEG2RAD(2); // Amount to add to the angle of rotation each frame

// Here's where we create our triangle.  By using a little math
// we'll rest the triangle on the X-axis
SVertex gTriangle[] =
{
	{ 1.0f, 0.0f, 0.0f, D3DCOLOR_XRGB(kRed,kGreen,kBlue), },
	{ -1.0f,  0.0f, 0.0f, D3DCOLOR_XRGB(kRed,kGreen,kBlue), },
	{ 0.0f, 2.0f, 0.0f, D3DCOLOR_XRGB(kRed,kGreen,kBlue), },
};

bool LockFrameRate(int frame_rate = 60); // Locks the frame rate
void DrawTriangle(); // Draws our triangle to the screen

// The WinProc
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);

// Main window program
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{
    HWND hwnd = NULL;
	MSG msg = {0};
    WNDCLASSEX wndclassex = {0};

	// Init fields we care about
	wndclassex.cbSize = sizeof(WNDCLASSEX); // Always set to size of WNDCLASSEX
    wndclassex.style = CS_HREDRAW | CS_VREDRAW;
    wndclassex.lpfnWndProc = WinProc;
    wndclassex.hInstance = hinstance;
	wndclassex.lpszClassName = kClassName;
	wndclassex.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW),
											IMAGE_CURSOR, 0, 0, LR_SHARED);

    RegisterClassEx(&wndclassex); // Register the WNDCLASSEX

	RECT rect = { 0, 0, kWinWid, kWinHgt }; // Desired window client rect

	DWORD winStyleEx = WS_EX_CLIENTEDGE; // Window's extended style
	DWORD winStyle = WS_CAPTION | WS_SYSMENU; // Window style

	// Adjust window rect so it gives us our desired client rect when we
	// create the window
	AdjustWindowRectEx(&rect, winStyle, false, winStyleEx);

	// Create the window
    hwnd = CreateWindowEx(winStyleEx,
					      kClassName,
						  "www.GameTutorials.com -- Matrices Part2",
						  winStyle,
						  CW_USEDEFAULT,
						  CW_USEDEFAULT,
						  rect.right - rect.left,
						  rect.bottom - rect.top,
						  NULL,
						  NULL,
						  hinstance,
						  NULL);

	// Init our global 3D object
	if(g3D->init(hwnd) == false)
		return EXIT_FAILURE; // There's been an error, lets get out of this joint

	// Get the client rect and make sure our client is the size we want
	GetClientRect(hwnd, &rect);
	assert(rect.right == kWinWid && rect.bottom == kWinHgt);

	// Set up our projection matrix
	// **NOTE** We only need to do this once cause we're not going to change it ever
	g3D->setProjMatrix(DEG2RAD(60), (float)kWinWid / (float)kWinHgt, 1.0f, 8192.0f);

	// Set up our view matrix
	// **NOTE** We only need to do this once cause we're not going to change it ever
	g3D->setViewMatrix(CPos(0.0f, 0.0f, -5.0f), CPos(0.0f, 0.0f, 0.0f));

	ShowWindow(hwnd, ishow);
	UpdateWindow(hwnd);

	// While the app is running...
    while(1)
	{
		// Handle any messages from the OS
		if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			if(msg.message == WM_QUIT)
				break;

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else if(LockFrameRate()) // If it's time to draw
		{
			static float angle = 0; // Angle of rotation

			// We want to move 2 units every second, since we're locking
			// the frame rate to 60 frames a second, we want to move 5 * (1/60) each frame
			static float moveAmt = 2.0f * (1.0f / 60.0f);
			static float curWorldX = 0.0f; // Current world x-position of triangle

			CMatrix mat1;
			CMatrix mat2;

			// Using our matrix class we'll create a rotation matrix and a translation
			// matrix
			mat1.makeRotX(angle);
			mat2.makeTranslate(CVector(curWorldX, 0.0f, 0.0f));

			// We multiply them together to get a matrix that contains both the rotation
			// and the translation
			CMatrix worldMat = mat1 * mat2;

			// We set our world matrix to the new matrix we created so our triangle
			// we'll be drawn with the updated translation and rotation
			g3D->setWorldMatrix(&(worldMat.getD3DMatrix()));
			DrawTriangle(); // Draw triangle in it's new rotated/translated position

			angle += kAngleInc;
			curWorldX += moveAmt;

			// Update moveAmt so we move side to side
			if(curWorldX > 1.0f)
				moveAmt = -moveAmt;
			else if(curWorldX < -1.0f)
				moveAmt = -moveAmt;
		}
		else
			Sleep(1); // Give the OS a slice of time to process other things

	} // end of while(1)

	g3D->deinit(); // Free up D3D object
	UnregisterClass(kClassName,hinstance); // Free up WNDCLASSEX
	    return EXIT_SUCCESS; // Application was a success
}

// WinProc
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{

    return DefWindowProc(hwnd, message, wparam, lparam);
}

// Locks the frame rate at "frame_rate"
// Returns true when it's okay to draw, false otherwise
bool LockFrameRate(int frame_rate)
{
	static float lastTime = 0.0f;

	// Get current time in seconds (milliseconds * .001 = seconds)
	float currentTime = GetTickCount() * 0.001f;

	// Get the elapsed time by subtracting the current time from the last time
	// If the desired frame rate amount of seconds has passed -- return true (ie Blit())
	if((currentTime - lastTime) > (1.0f / frame_rate))
	{
		// Reset the last time
		lastTime = currentTime;
			return true;
	}

	return false;
}

// This draws our triangle to the screen
void DrawTriangle()
{
	g3D->begin(); // Begin the scene
	g3D->clear(); // Clear our viewport

	g3D->render(gTriangle, 3); // Draw our triangle

	g3D->end(); // End the scene (Quit rendering and blit to the screen)
}

// A Matrix is 1/2 Math and 1/2 Irix

/*
	Hopefully you understand a wee bit more about matrices.  This tutorial is NOT a substitute
	for learning linear algebra, this tutorial only serves as a introduction to the
	linear algebra necessary to do 3D graphical programming.  If you are truly interested
	in doing 3D programming, you will immensely help yourself out by learning vector and
	matrix algebra.

	**CHALLENGE**

		The matrix class provided is only the start of something that could be beautiful.  There
		are further optimizations and methods that could be added to make it highly robust and
		user friendly to the 3D programmer.  See if you can't add the following functionality:

			Inverse of a matrix
			Multiplying of a vector
			Making a scaling matrix

		Chances are if you're not a linear algebra guru, you'll have to open a book or two %)

	If you have any questions about the matrices or 3D math in general, be sure to post 'em at
	the forums at:  www.GameTutorials.com.
*/

/*-------------------------*\
|  Programmed by:  TheTutor |
\*-------------------------*/


CurveColorConfig& CurveColorConfig::operator=(const CurveColorConfig& src) {
  setType(src.type_);
  setAutoColorIndex(src.autoColorIndex_);
  setCustomColor(src.customColor_);

  return *this;
}

/******************************************************************************
 *                                                                            *
 ******************************************************************************/

#include <rqt_multiplot/ColorOperations.h>

#include "rqt_multiplot/CurveColorConfig.h"

namespace rqt_multiplot {

/*****************************************************************************/
/* Constructors and Destructor                                               */
/*****************************************************************************/

CurveColorConfig::CurveColorConfig(QObject* parent, Type type, unsigned char
    autoColorIndex, const QColor& customColor) :
  Config(parent),
  type_(type),
  autoColorIndex_(autoColorIndex),
  customColor_(customColor) {
}

CurveColorConfig::~CurveColorConfig() {
}

/*****************************************************************************/
/* Accessors                                                                 */
/*****************************************************************************/

void CurveColorConfig::setType(Type type) {
  if (type != type_) {
    type_ = type;

    emit typeChanged(type);
    emit currentColorChanged(getCurrentColor());
    emit changed();
  }
}

CurveColorConfig::Type CurveColorConfig::getType() const {
  return type_;
}

void CurveColorConfig::setAutoColorIndex(unsigned char index) {
  if (index != autoColorIndex_) {
    autoColorIndex_ = index;

    emit autoColorIndexChanged(index);
    emit currentColorChanged(getCurrentColor());
    emit changed();
  }
}

unsigned char CurveColorConfig::getAutoColorIndex() const {
  return autoColorIndex_;
}

void CurveColorConfig::setCustomColor(const QColor& color) {
  if (color != customColor_) {
    customColor_ = color;

    emit customColorChanged(color);
    if (type_ == Custom)
      emit currentColorChanged(getCurrentColor());
    emit changed();
  }
}

const QColor& CurveColorConfig::getCustomColor() const {
  return customColor_;
}

QColor CurveColorConfig::getCurrentColor() const {
  if (type_ == Auto)
    return ColorOperations::intToRgb(autoColorIndex_);
  else
    return customColor_;
}

/*****************************************************************************/
/* Methods                                                                   */
/*****************************************************************************/

void CurveColorConfig::save(QSettings& settings) const {
  settings.setValue("type", type_);
  settings.setValue("custom_color", QVariant::fromValue<QColor>(
    customColor_));
}

void CurveColorConfig::load(QSettings& settings) {
  setType(static_cast<Type>(settings.value("type", Auto).toInt()));
  setCustomColor(settings.value("custom_color", QColor(Qt::black)).
    value<QColor>());
}

void CurveColorConfig::reset() {
  setType(Auto);
  setCustomColor(Qt::black);
}

void CurveColorConfig::write(QDataStream& stream) const {
  stream << type_;
  stream << customColor_;
}

void CurveColorConfig::read(QDataStream& stream) {
  int type;
  QColor customColor;

  stream >> type;
  setType(static_cast<Type>(type));
  stream >> customColor;
  setCustomColor(customColor);
}

/*****************************************************************************/
/* Operators                                                                 */
/*****************************************************************************/


SensorPositionTreeItem::SensorPositionTreeItem(Qt3DCore::QEntity *p3DEntityParent, int iType, const QString& text)
: Abstract3DTreeItem(p3DEntityParent, iType, text)
{
    initItem();
}

/**
 * @file     sensorpositiontreeitem.cpp
 * @author   Lars Debor <example@nnthink.com>;
 *           Lorenz Esch <example@nnthink.com>
 * @since    0.1.0
 * @date     March, 2017
 *
 *
 *
 * @brief    SensorPositionTreeItem class definition.
 *
 */

// INCLUDES

#include "sensorpositiontreeitem.h"
#include "../common/metatreeitem.h"
#include "../../3dhelpers/geometrymultiplier.h"
#include "../../materials/geometrymultipliermaterial.h"

#include <fiff/fiff_constants.h>
#include <fiff/fiff_ch_info.h>

// QT INCLUDES

#include <QMatrix4x4>
#include <Qt3DExtras/QCuboidGeometry>
#include <Qt3DCore/QEntity>
#include <Qt3DExtras/QSphereGeometry>

// EIGEN INCLUDES

#include <Eigen/Core>

// USED NAMESPACES

using namespace DISP3DLIB;

// DEFINE MEMBER METHODS

void SensorPositionTreeItem::initItem()
{
    this->setEditable(false);
    this->setCheckable(true);
    this->setCheckState(Qt::Checked);
    this->setToolTip(this->text());
}

void SensorPositionTreeItem::addData(const QList<FIFFLIB::FiffChInfo>& lChInfo,
                                     const QString& sDataType,
                                     const QStringList& bads)
{
    if(sDataType == "MEG") {
        plotMEGSensors(lChInfo, bads);
    } else if (sDataType == "EEG") {
        plotEEGSensors(lChInfo, bads);
    }
}

void SensorPositionTreeItem::plotMEGSensors(const QList<FIFFLIB::FiffChInfo>& lChInfo,
                                            const QStringList& bads)
{
    if(lChInfo.isEmpty()) {
        qDebug() << "SensorPositionTreeItem::plotMEGSensors - Channel data is empty. Returning.";
        return;
    }

    if(!m_pMEGSensorEntity) {
        m_pMEGSensorEntity = new QEntity(this);
    }

    //create geometry
    if(!m_pMEGSensors) {
        if(!m_pMEGSensorGeometry) {
            m_pMEGSensorGeometry = QSharedPointer<Qt3DExtras::QCuboidGeometry>::create();
            m_pMEGSensorGeometry->setXExtent(0.01f);
            m_pMEGSensorGeometry->setYExtent(0.01f);
            m_pMEGSensorGeometry->setZExtent(0.001f);
        }

        m_pMEGSensors = new GeometryMultiplier(m_pMEGSensorGeometry);

        m_pMEGSensorEntity->addComponent(m_pMEGSensors);

        //Add material
        GeometryMultiplierMaterial* pMaterial = new GeometryMultiplierMaterial(false);
        pMaterial->setAmbient(QColor(100,100,100));
        pMaterial->setAlpha(0.75);
        m_pMEGSensorEntity->addComponent(pMaterial);
    }

    //Create transform matrix for each cuboid instance
    QVector<QMatrix4x4> vTransforms;
    vTransforms.reserve(lChInfo.size());
    QVector<QColor> vColorsNodes;
    QVector3D tempPos;

    for(int i = 0; i < lChInfo.size(); ++i) {
        QMatrix4x4 tempTransform;

        tempPos.setX(lChInfo[i].chpos.r0(0));
        tempPos.setY(lChInfo[i].chpos.r0(1));
        tempPos.setZ(lChInfo[i].chpos.r0(2));
        //Set position
        tempTransform.translate(tempPos);

        //Set orientation
        for(int j = 0; j < 4; ++j) {
            tempTransform(j, 0) = lChInfo[i].coil_trans.row(j)(0);
            tempTransform(j, 1) = lChInfo[i].coil_trans.row(j)(1);
            tempTransform(j, 2) = lChInfo[i].coil_trans.row(j)(2);
        }

        if(!vTransforms.contains(tempTransform)) {
            vTransforms.push_back(tempTransform);
        }

        if(bads.contains(lChInfo.at(i).ch_name)) {
            vColorsNodes.push_back(QColor(255,0,0));
        } else {
            vColorsNodes.push_back(QColor(100,100,100));
        }
    }

    //Set instance Transform
    m_pMEGSensors->setTransforms(vTransforms);
    m_pMEGSensors->setColors(vColorsNodes);

    //Update colors in color item
    QList<QStandardItem*> items = this->findChildren(MetaTreeItemTypes::Color);

    for(int i = 0; i < items.size(); ++i) {
        if(MetaTreeItem* item = dynamic_cast<MetaTreeItem*>(items.at(i))) {
            QVariant data;
            data.setValue(QColor(100,100,100));
            item->setData(data, MetaTreeItemRoles::Color);
            item->setData(data, Qt::DecorationRole);
        }
    }
}

void SensorPositionTreeItem::plotEEGSensors(const QList<FIFFLIB::FiffChInfo>& lChInfo,
                                            const QStringList& bads)
{
    if(lChInfo.isEmpty()) {
        qDebug() << "SensorPositionTreeItem::plotEEGSensors - Channel data is empty. Returning.";
        return;
    }

    if(!m_pEEGSensorEntity) {
        m_pEEGSensorEntity = new QEntity(this);
    }

    //create geometry
    if(!m_pEEGSensors) {
        if(!m_pEEGSensorGeometry) {
            m_pEEGSensorGeometry = QSharedPointer<Qt3DExtras::QSphereGeometry>::create();
            m_pEEGSensorGeometry->setRadius(0.001f);
        }

        m_pEEGSensors = new GeometryMultiplier(m_pEEGSensorGeometry);

        m_pEEGSensorEntity->addComponent(m_pEEGSensors);

        //Add material
        GeometryMultiplierMaterial* pMaterial = new GeometryMultiplierMaterial(false);
        pMaterial->setAmbient(QColor(100,100,100));
        pMaterial->setAlpha(0.75);
        m_pEEGSensorEntity->addComponent(pMaterial);
    }

    //Create transform matrix for each cuboid instance
    QVector<QMatrix4x4> vTransforms;
    vTransforms.reserve(lChInfo.size());
    QVector<QColor> vColorsNodes;
    QVector3D tempPos;

    for(int i = 0; i < lChInfo.size(); ++i) {
        QMatrix4x4 tempTransform;

        tempPos.setX(lChInfo[i].chpos.r0(0));
        tempPos.setY(lChInfo[i].chpos.r0(1));
        tempPos.setZ(lChInfo[i].chpos.r0(2));
        //Set position
        tempTransform.translate(tempPos);

        vTransforms.push_back(tempTransform);

        if(bads.contains(lChInfo.at(i).ch_name)) {
            vColorsNodes.push_back(Qt::red);
        } else {
            vColorsNodes.push_back(Qt::gray);
        }
    }

    //Set instance Transform
    m_pEEGSensors->setTransforms(vTransforms);
    m_pEEGSensors->setColors(vColorsNodes);

    //Update colors in color item
    QList<QStandardItem*> items = this->findChildren(MetaTreeItemTypes::Color);

    for(int i = 0; i < items.size(); ++i) {
        if(MetaTreeItem* item = dynamic_cast<MetaTreeItem*>(items.at(i))) {
            QVariant data;
            data.setValue(QColor(100,100,100));
            item->setData(data, MetaTreeItemRoles::Color);
            item->setData(data, Qt::DecorationRole);
        }
    }
}


// RMDOTS-NOT: Inputs

// RUN: mkdir -p %t/test && cd %t/test

// RELPATH: {{[/\\].*(/|\\\\)test(/|\\\\)f1}}.c