#include <sys/types.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

HWND	hwnd;
time_t	t0;

typedef struct
{
	LCID	lcidSender;
	LCID	lcidRecipient;
	unsigned char achTests[60];
	WCHAR	awchTests[30];
} TransmissionTest;

static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	time_t t;

	switch (message)
	{
	case WM_IME_CHAR:
		time(&t);
		printf("\t\t%05d IMECHAR 0x%04x\b", t - t0, LOWORD(wParam));
		break;

	case WM_CHAR:
		time(&t);
		printf("\t\t%05d WM_CHAR 0x%04x\n", t - t0, LOWORD(wParam));
		break;
	}
	return DefWindowProcA(hwnd, message, wParam, lParam);
}

void
ShowCPInfo(	char const *side)
{
	CPINFOEX cex;

	GetCPInfoEx(CP_THREAD_ACP, 0, &cex);

	printf("    %s Code Page: %d\n", side, cex.CodePage);
}

DWORD WINAPI
MessagePoster(LPVOID lpParam)
{
	int	i;
	TransmissionTest *ptt = lpParam;

	SetThreadLocale(ptt->lcidSender);
	ShowCPInfo("Send");

	printf("\tSending ANSI\n");
	for (i = 0; ptt->achTests[i]; ++i)
	{
		time_t t;

		time(&t);
		printf("\t\t%05d AS----> 0x%04x\n", t - t0, ptt->achTests[i]);
		SendMessageA(hwnd, WM_CHAR, ptt->achTests[i], 0);
		Sleep(5000);
	}
	printf("\tPosting ANSI\n");
	for (i = 0; ptt->achTests[i]; ++i)
	{
		time_t t;

		time(&t);
		printf("\t\t%05d AP----> 0x%04x\n", t - t0, ptt->achTests[i]);
		PostMessageA(hwnd, WM_CHAR, ptt->achTests[i], 0);
		Sleep(5000);
	}
	printf("\tSending Wide\n");
	for (i = 0; ptt->awchTests[i]; ++i)
	{
		time_t t;

		time(&t);
		printf("\t\t%05d WS----> 0x%04x\n", t - t0, ptt->awchTests[i]);
		SendMessageW(hwnd, WM_CHAR, ptt->awchTests[i], 0);
		Sleep(5000);
	}
	printf("\tPosting Wide\n");
	for (i = 0; ptt->awchTests[i]; ++i)
	{
		time_t t;

		time(&t);
		printf("\t\t%05d WP----> 0x%04x\n", t - t0, ptt->awchTests[i]);
		PostMessageW(hwnd, WM_CHAR, ptt->awchTests[i], 0);
		Sleep(5000);
	}
	return 0;
}

void
StartTestW(TransmissionTest *ptt)
{
	MSG msg;
	HANDLE hThread;
	DWORD dwRet;
	WNDCLASSW cls;

	printf("Wide window: Send LCID: %08x   Recv LCID: %08x\n", ptt->lcidSender, ptt->lcidRecipient);

	SetThreadLocale(ptt->lcidRecipient);
	ShowCPInfo("Recv");

	cls.style = 0;
	cls.lpfnWndProc = MsgCheckProcA;
	cls.cbClsExtra = 0;
	cls.cbWndExtra = 0;
	cls.hInstance = GetModuleHandleA(0);
	cls.hIcon = 0;
	cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
	cls.hbrBackground = NULL;
	cls.lpszMenuName = NULL;
	cls.lpszClassName = L"TestWindowClass";
	if(!RegisterClassW(&cls)) return;


	hwnd = CreateWindowW(L"TestWindowClass", L"caption", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
					0, 0, 100, 100, NULL, NULL, GetModuleHandleA(0), NULL);
	time(&t0);
	hThread = CreateThread(NULL, 0, MessagePoster, ptt, 0, 0);

	while (GetMessageW(&msg, NULL, 0, 0))
	{
		DispatchMessageW(&msg);
		GetExitCodeThread(hThread, &dwRet);
		if (dwRet != STILL_ACTIVE)
		{
			CloseHandle(hThread);
			break;
		}
	}

	DestroyWindow(hwnd);

	UnregisterClassW(cls.lpszClassName, cls.hInstance);

}

void
StartTestA(TransmissionTest *ptt)
{
	MSG msg;
	HANDLE hThread;
	DWORD dwRet;
	WNDCLASSA cls;

	printf("ANSI window: Send LCID: %08x   Recv LCID: %08x\n", ptt->lcidSender, ptt->lcidRecipient);

	SetThreadLocale(ptt->lcidRecipient);
	ShowCPInfo("Recv");

	cls.style = 0;
	cls.lpfnWndProc = MsgCheckProcA;
	cls.cbClsExtra = 0;
	cls.cbWndExtra = 0;
	cls.hInstance = GetModuleHandleA(0);
	cls.hIcon = 0;
	cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
	cls.hbrBackground = NULL;
	cls.lpszMenuName = NULL;
	cls.lpszClassName = "TestWindowClass";
	if(!RegisterClassA(&cls)) return;


	hwnd = CreateWindowA("TestWindowClass", "caption", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
					0, 0, 100, 100, NULL, NULL, GetModuleHandleA(0), NULL);
	time(&t0);
	hThread = CreateThread(NULL, 0, MessagePoster, ptt, 0, 0);

	while (GetMessageW(&msg, NULL, 0, 0))
	{
		DispatchMessageW(&msg);
		GetExitCodeThread(hThread, &dwRet);
		if (dwRet != STILL_ACTIVE)
		{
			CloseHandle(hThread);
			break;
		}
	}

	DestroyWindow(hwnd);

	UnregisterClassA(cls.lpszClassName, cls.hInstance);

}

#define	LCID_JAPANESE		MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), SORT_DEFAULT)
#define	LCID_CHIN_TRAD		MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL), SORT_DEFAULT)
#define	LCID_CHIN_SIMP		MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT)
#define LCID_DEF		MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), SORT_DEFAULT)

TransmissionTest att[] =
{
	{
		LCID_JAPANESE /* CP 932 */,			LCID_CHIN_TRAD /* CP 950 */,
		{
			0x88,	0xB2,		/* CP932 0x88B2 -> UNICODE 0x6893 -> CP950 0xB1EA */
			0x81,	0x57,		/* CP932 0x8157 -> UNICODE 0x4EDD -> CP950 0xC969 */
			0x8d,	0x8f,		/* CP932 0x8D8F -> UNICODE 0x523B -> CP950 0xA8E8 */
			0x90,	0x9d,		/* CP932 0x909D -> UNICODE 0x96C0 -> CP950 0xB3B6 */
			0
		},
		{
			0x6893, 0x4EDD,
			0
		}
	},
	{
		LCID_CHIN_TRAD /* CP 950 */,			LCID_JAPANESE /* CP 932 */,	
		{
			0xB1,	0xEA,		/* CP932 0x88B2 -> UNICODE 0x6893 -> CP950 0xB1EA */
			0
		},
		{
			0x6893, 0x4EDD,
			0
		}
	},
	{
		LCID_CHIN_TRAD /* CP 950 */,			LCID_CHIN_TRAD /* CP 950 */,	
		{
			0xB1,	0xEA,		/* CP932 0x88B2 -> UNICODE 0x6893 -> CP950 0xB1EA */
			0
		},
		{
			0x6893, 0x4EDD,
			0
		}
	}
};

#define rangeof(x) ((sizeof(x)) / sizeof(x[0]))

void
RunLoop(void)
{
	int	i;

	SetTimer(0, 0, 100, 0);

	for (i = 0; i < rangeof(att); ++i)
	{
		StartTestA(att + i);
		StartTestW(att + i);
	}
}

BOOL
ChangeACP(int iCodePage)
{
	HMODULE hmKernel;
	VOID (WINAPI *pSetCPGlobal)(UINT iVal);

	hmKernel = LoadLibrary("KERNEL32.DLL");
	if (hmKernel)
	{
		pSetCPGlobal = (VOID (WINAPI *)(UINT)) GetProcAddress(hmKernel, "SetCPGlobal");

		FreeLibrary(hmKernel);
		if (pSetCPGlobal)
		{
			(*pSetCPGlobal)(iCodePage);
			printf("Changed CP_ACP to %d\n", GetACP());
			return TRUE;
		}
	}
	return FALSE;
}

int
main(int argc, char **argv)
{
	printf("Initial CP_ACP: %d\n", GetACP());
	RunLoop();
	if (ChangeACP(932))
		RunLoop();
	if (ChangeACP(950))
		RunLoop();
	return 0;
}

