#include <windows.h>
#include <stdio.h>
#include <locale.h>

#if _MSC_VER >= 1800
/* From VS2012. */
typedef struct localerefcount
{
	char* locale;
	wchar_t* wlocale;
	int* refcount;
	int* wrefcount;
} locrefcount;

typedef struct __crt_locale_data
{
	int refcount;
	unsigned int lc_codepage;
	unsigned int lc_collate_cp;
	unsigned int lc_time_cp;
	locrefcount lc_category[6];
	int lc_clike;
	int mb_cur_max;
	int* lconv_intl_refcount;
	int* lconv_num_refcount;
	int* lconv_mon_refcount;
	struct lconv* lconv;
	int* ctype1_refcount;
	unsigned short* ctype1;
	const unsigned short* pctype;
	const unsigned char* pclmap;
	const unsigned char* pcumap;
	struct __lc_time_data* lc_time_curr;
	wchar_t* locale_name[6];
} threadlocinfo;
#endif

static const LPCWSTR AdditionalLocales[] =
{
	/* Additional supported language strings */
	L"american",
	L"american english",
	L"american-english",
	L"australian",
	L"belgian",
	L"canadian",
	L"chh",
	L"chi",
	L"chinese",
	L"chinese-hongkong",
	L"chinese-simplified",
	L"chinese-singapore",
	L"chinese-traditional",
	L"dutch-belgian",
	L"english-american",
	L"english-aus",
	L"english-belize",
	L"english-can",
	L"english-caribbean",
	L"english-ire",
	L"english-jamaica",
	L"english-nz",
	L"english-south africa",
	L"english-trinidad y tobago",
	L"english-uk",
	L"english-us",
	L"english-usa",
	L"french-belgian",
	L"french-canadian",
	L"french-luxembourg",
	L"french-swiss",
	L"german-austrian",
	L"german-lichtenstein",
	L"german-luxembourg",
	L"german-swiss",
	L"irish-english",
	L"italian-swiss",
	L"norwegian",
	L"norwegian-bokmal",
	L"norwegian-nynorsk",
	L"portuguese-brazilian",
	L"spanish-argentina",
	L"spanish-bolivia",
	L"spanish-chile",
	L"spanish-colombia",
	L"spanish-costa rica",
	L"spanish-dominican republic",
	L"spanish-ecuador",
	L"spanish-el salvador",
	L"spanish-guatemala",
	L"spanish-honduras",
	L"spanish-mexican",
	L"spanish-modern",
	L"spanish-nicaragua",
	L"spanish-panama",
	L"spanish-paraguay",
	L"spanish-peru",
	L"spanish-puerto rico",
	L"spanish-uruguay",
	L"spanish-venezuela",
	L"swedish-finland",
	L"swiss",
	L"uk",
	L"us",
	L"usa",
	/* Additional supported country and region strings */
	L"america",
	L"britain",
	L"china",
	L"czech",
	L"england",
	L"great britain",
	L"holland",
	L"hong-kong",
	L"new-zealand",
	L"nz",
	L"pr china",
	L"pr-china",
	L"puerto-rico",
	L"slovak",
	L"south africa",
	L"south korea",
	L"south-africa",
	L"south-korea",
	L"trinidad & tobago",
	L"united-kingdom",
	L"united-states",
	L"GBR",
	L"CHN",
	L"CZE",
	L"NLD",
	L"HKG",
	L"NZL",
	L"PRI",
	L"SVK",
	L"ZAF",
	L"KOR",
	L"TTO"
};

/* Callback for EnumSystemLocalesEx() */
#define BUFFER_SIZE 512
BOOL CALLBACK FindLocale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
{
	wchar_t		test_locale[BUFFER_SIZE];

	(void)(dwFlags);
	memset(test_locale, 0, sizeof(test_locale));
	if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME,
		test_locale, BUFFER_SIZE) > 0)
	{
		wchar_t* hyphen;
		wchar_t* underscore;
		wchar_t** argv;

		argv = (wchar_t**)lparam;
		hyphen = wcsrchr(pStr, '-');
		underscore = wcsrchr(argv[0], '_');
		if (hyphen == NULL || underscore == NULL)
		{
			if (_wcsicmp(argv[0], test_locale) == 0)
			{
				wcscpy(argv[1], pStr);
				return FALSE;
			}
		}
		else
		{
			size_t		len;

			wcscat(test_locale, L"_");
			len = wcslen(test_locale);
			if (GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME,
				test_locale + len, BUFFER_SIZE - len) > 0)
			{
				if (_wcsicmp(argv[0], test_locale) == 0)
				{
					wcscpy(argv[1], pStr);
					return FALSE;
				}
			}
		}
	}
	return TRUE;
}

/* Callback for EnumSystemLocalesEx() */
#define BUFFER_SIZE 512
BOOL CALLBACK GetLocale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
{
	wchar_t		language[BUFFER_SIZE];
	wchar_t		country[BUFFER_SIZE];
	wchar_t		locale_name[BUFFER_SIZE];
	wchar_t		buffer[BUFFER_SIZE];
	wchar_t*	argv[2];
	wchar_t*	hyphen;
	char		locale[BUFFER_SIZE];
	_locale_t	loct;
	size_t		len;

	(void)(dwFlags);
	memset(language, 0, sizeof(language));
	memset(country, 0, sizeof(country));
	memset(locale_name, 0, sizeof(locale_name));
	memset(buffer, 0, sizeof(buffer));

	GetLocaleInfoEx(pStr, LOCALE_SENGLISHLANGUAGENAME, language, BUFFER_SIZE);
	GetLocaleInfoEx(pStr, LOCALE_SENGLISHCOUNTRYNAME, country, BUFFER_SIZE);
	hyphen = wcsrchr(pStr, '-');
	if (hyphen)
		len = wcslen(hyphen);
	else
		len = 5;
	if (len != 5)
		swprintf(locale_name, BUFFER_SIZE, L"%s_%s", language, country);
	else
		wcscpy(locale_name, language);

	argv[0] = locale_name;
	argv[1] = buffer;
	EnumSystemLocalesEx(FindLocale, LOCALE_ALL, (LPARAM) argv, NULL);

	memset(locale, 0, sizeof(locale));
	wcstombs(locale, locale_name, BUFFER_SIZE);
	loct = _create_locale(LC_CTYPE, locale);
	wprintf(L"%60s|%20s|", locale_name, buffer);
	if (loct)
		wprintf(L"%20s\n", loct->locinfo->locale_name[LC_CTYPE]);
	else
		wprintf(L"\n");
	_free_locale(loct);

	return TRUE;
}

int __cdecl wmain(void)
{
	wchar_t		buffer[BUFFER_SIZE];
	wchar_t		wc_locale_name[BUFFER_SIZE];
	wchar_t* 	argv[2];
	char		locale_name[BUFFER_SIZE];
	int			i;
	_locale_t	loct;

	EnumSystemLocalesEx(GetLocale, LOCALE_WINDOWS, (LPARAM)argv, NULL);

	argv[0] = wc_locale_name;
	argv[1] = buffer;
	for (i = 0; i < sizeof(AdditionalLocales) / sizeof(AdditionalLocales[0]); i++)
	{
		memset(buffer, 0, sizeof(buffer));
		memset(wc_locale_name, 0, sizeof(wc_locale_name));
		wcscpy(wc_locale_name, AdditionalLocales[i]);
		EnumSystemLocalesEx(FindLocale, LOCALE_ALL, (LPARAM)argv, NULL);
		wcstombs(locale_name, AdditionalLocales[i], BUFFER_SIZE);
		loct = _create_locale(LC_CTYPE, locale_name);
		wprintf(L"%60s|%20s|", wc_locale_name, buffer);
		if (loct)
			wprintf(L"%20s\n", loct->locinfo->locale_name[LC_CTYPE]);
		else
			wprintf(L"\n");
		_free_locale(loct);
	}
}