#define _WIN32_WINNT 0x0510

#include <windows.h>
#include <shlwapi.h>

#include "spket_jni.h"
#include "resource.h"

static HINSTANCE spket_handle = NULL;

extern "C" {

#define SPKET_FUC(rc, func) JNIEXPORT rc JNICALL Java_com_spket_internal_win32_Theme##func

#define CONVERT(atype, carray, jarray) \
  atype carray = NULL; \
  if (jarray) if ((carray = (atype) env->GetPrimitiveArrayCritical(jarray, JNI_FALSE)) == NULL) goto done;
 
#define RELEASE(jarray, carray) if (jarray && carray) env->ReleasePrimitiveArrayCritical(jarray, carray, JNI_ABORT);
#define RELEASE_COPY(jarray, carray) if (jarray && carray) env->ReleasePrimitiveArrayCritical(jarray, carray, 0);

typedef HANDLE (JNICALL *CreateActCtxWProc)(PACTCTXW);
typedef HANDLE (JNICALL *CreateActCtxAProc)(PACTCTXA);
typedef BOOL (JNICALL *ActivateActCtxProc)(HANDLE, ULONG_PTR*);
typedef BOOL (JNICALL *DeactivateActCtxProc)(DWORD, ULONG_PTR);
typedef void (JNICALL *ReleaseActCtxProc)(HANDLE);

SPKET_FUC(jboolean, _activeTheming)
  (JNIEnv *env, jclass, jboolean isUnicode, jintArray ctxArray)
{
	HINSTANCE themeLib = LoadLibrary("UxTheme");
	if (themeLib == NULL)
		return JNI_FALSE;
		
	FreeLibrary(themeLib);
	
	//IsThemeActive && IsAppThemed not work as expected
	BOOL isAppThemed = FALSE;
	HINSTANCE comctLib = LoadLibrary("Comctl32");
	if (comctLib != NULL) {
		DLLGETVERSIONPROC dllProc = (DLLGETVERSIONPROC) GetProcAddress(comctLib, "DllGetVersion");
		if (dllProc != NULL) {
			DLLVERSIONINFO verInfo;
			ZeroMemory(&verInfo, sizeof(verInfo));
			verInfo.cbSize = sizeof(verInfo);
			HRESULT hr = dllProc(&verInfo);
			if (SUCCEEDED(hr) && verInfo.dwMajorVersion >= 6) {
				isAppThemed = TRUE;
			}
		}
		FreeLibrary(comctLib);
	}
	
	BOOL rc = FALSE;
	if (isAppThemed == FALSE) {
		DWORD nSize = 0;
		HANDLE* hActCtx = NULL;
		ULONG_PTR* ulpActivationCookie = NULL;
		
		HINSTANCE kernel = LoadLibrary("Kernel32");
		if (kernel == NULL)
			return JNI_FALSE;
			
		hActCtx = new HANDLE;
		*hActCtx = INVALID_HANDLE_VALUE;
		ulpActivationCookie = new ULONG_PTR;
		
		ActivateActCtxProc activateActCtxProc = (ActivateActCtxProc) GetProcAddress(kernel, "ActivateActCtx");
		if (activateActCtxProc != NULL) {
			if (isUnicode) {
				CreateActCtxWProc createActCtxProc = (CreateActCtxWProc) GetProcAddress(kernel, "CreateActCtxW");
				if (createActCtxProc != NULL) {
					ACTCTXW actctx;
					DWORD length = MAX_PATH;
					WCHAR filename[MAX_PATH];
					LPWSTR lpFilename = filename;
					
					do {
						nSize = GetModuleFileNameW(spket_handle, lpFilename, length);
						if (nSize < length)
							break;
						
						nSize = 0;
						if (lpFilename != filename)
							delete[] lpFilename;
							
						length += MAX_PATH;
						lpFilename = new WCHAR[length];
						if (lpFilename == NULL)
							break;
					} while (true);
					
					if (nSize > 0) {
						memset(&actctx, 0, sizeof(actctx));
						actctx.cbSize = sizeof(actctx);
						actctx.lpSource = lpFilename;
						actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
						actctx.lpResourceName = MAKEINTRESOURCEW(MANIFEST_ID);
						*hActCtx = createActCtxProc(&actctx);
					}
					if (lpFilename != NULL && lpFilename != filename)
						delete[] lpFilename;
				}
			} else {
				CreateActCtxAProc createActCtxProc = (CreateActCtxAProc) GetProcAddress(kernel, "CreateActCtxA");
				if (createActCtxProc != NULL) {
					ACTCTXA actctx;
					DWORD length = MAX_PATH;
					char filename[MAX_PATH];
					char* lpFilename = filename;
					
					do {
						nSize = GetModuleFileNameA(spket_handle, lpFilename, length);
						if (nSize < length)
							break;
						
						nSize = 0;
						if (lpFilename != filename)
							delete[] lpFilename;
							
						length += MAX_PATH;
						lpFilename = new char[length];
						if (lpFilename == NULL)
							break;
					} while (true);
					
					if (nSize > 0) {
						memset(&actctx, 0, sizeof(actctx));
						actctx.cbSize = sizeof(actctx);
						actctx.lpSource = lpFilename;
						actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
						actctx.lpResourceName = MAKEINTRESOURCEA(MANIFEST_ID);
						*hActCtx = createActCtxProc(&actctx);
					}
					if (lpFilename != NULL && lpFilename != filename)
						delete[] lpFilename;
				}
			}
		
			if (*hActCtx != INVALID_HANDLE_VALUE) {
				rc = activateActCtxProc(*hActCtx, ulpActivationCookie);
				if (rc == TRUE) {
					CONVERT(jint*, ctx, ctxArray)
					
					ctx[0] = reinterpret_cast<jint>(hActCtx);
					ctx[1] = reinterpret_cast<jint>(ulpActivationCookie);
					
				done:
					RELEASE_COPY(ctxArray, ctx)
				} else { // release
					ReleaseActCtxProc releaseActCtxProc = (ReleaseActCtxProc) GetProcAddress(kernel, "ReleaseActCtx");
					if (releaseActCtxProc != NULL)
						releaseActCtxProc(*hActCtx);
				}
			}
		}
		
		FreeLibrary(kernel);
	} else {
		rc = TRUE;
	}
	
	return (rc ? JNI_TRUE : JNI_FALSE);
}

SPKET_FUC(jboolean, _deactiveTheming)
  (JNIEnv *, jclass, jint handle, jint cookie)
{
	if (handle) {
		HINSTANCE kernel = LoadLibrary("Kernel32");
		if (kernel == NULL)
			return JNI_FALSE;
			
		HANDLE* hActCtx = reinterpret_cast<HANDLE*>(handle);
		ULONG_PTR* ulpActivationCookie = reinterpret_cast<ULONG_PTR*>(cookie);
		DeactivateActCtxProc deactivateActCtxProc = (DeactivateActCtxProc) GetProcAddress(kernel, "DeactivateActCtx");
		if (deactivateActCtxProc != NULL) {
			deactivateActCtxProc(0, *ulpActivationCookie);
			
			ReleaseActCtxProc releaseActCtxProc = (ReleaseActCtxProc) GetProcAddress(kernel, "ReleaseActCtx");
			if (releaseActCtxProc != NULL)
				releaseActCtxProc(*hActCtx);
		}
		
		FreeLibrary(kernel);
		
		delete hActCtx;
		delete ulpActivationCookie;
	}
	
	return JNI_TRUE;
}

BOOL APIENTRY DllMain (HINSTANCE hInst,
                       DWORD reason,
                       LPVOID reserved)
{
	spket_handle = hInst;
	
	return TRUE;
}

/*
static bool IsThemeSupport()
{
	if (theme_status == 0) {
		theme_status = 1;
		HINSTANCE theme = LoadLibrary("UxTheme");
		if (theme != NULL) {
			theme_status = 2;
			FreeLibrary(theme);
		}
	}
	
	return (theme_status == 2);
}

static bool IsAppThemed()
{
	if (!IsThemeSupport())
		return false;
		
	BOOL isAppThemed = FALSE;
	HINSTANCE comctLib = LoadLibrary("Comctl32");
	if (comctLib != NULL) {
		DLLGETVERSIONPROC dllProc = (DLLGETVERSIONPROC) GetProcAddress(comctLib, "DllGetVersion");
		if (dllProc != NULL) {
			DLLVERSIONINFO verInfo;
			ZeroMemory(&verInfo, sizeof(verInfo));
			verInfo.cbSize = sizeof(verInfo);
			HRESULT hr = dllProc(&verInfo);
			if (SUCCEEDED(hr) && verInfo.dwMajorVersion >= 6) {
				HINSTANCE themeLib = LoadLibrary("UxTheme");
				if (themeLib != NULL) {
					IsThemeActiveProc actProc = (IsThemeActiveProc) GetProcAddress(themeLib, "IsThemeActive");
					if (actProc && actProc() == TRUE) {
						IsAppThemedProc themedProc = (IsAppThemedProc) GetProcAddress(themeLib, "IsAppThemed");
						if (themedProc != NULL)
							isAppThemed = themedProc();
					}
			
					FreeLibrary(themeLib);
				}
			}
		}
		FreeLibrary(comctLib);
	}
	return (isAppThemed == TRUE ? true : false);
}

static bool EnableTheming(bool enable)
{
	if (!IsThemeSupport())
		return false;
	
	bool rc = false;
	HINSTANCE themeLib = LoadLibrary("UxTheme");
	if (themeLib != NULL) {
		EnableThemingProc themingProc = (EnableThemingProc) GetProcAddress(themeLib, "EnableTheming");
		if (themingProc != NULL)
			rc = themingProc(enable ? TRUE : FALSE) == S_OK;
		
		FreeLibrary(themeLib);
	}
	
	return rc;
}
*/

}
