/////////////////////////////////////////////////////////////////////////////
// File:        language.cpp
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Copyright:   (C) 2010-10 Cesar Mauri Loba - CREA Software Systems
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////
#include "spcore/language.h"

#include "spcore/coreruntime.h"

//#include <locale.h>
#include <clocale>

#include <vector>
#include <string>
#include <iostream>
#include <boost/static_assert.hpp>

#define ARRAY_SIZE(foo) (sizeof(foo)/sizeof(foo[0]))

/*
	Language id's and names
*/
static const char* g_supported_lang_ids[]= { "", "ca_ES", "C", "es_ES", "gl_ES" };
// NOTE: this file is encoded using UTF8
static const char* g_supported_lang_names[]= { N__("System default"), "Català", "English", "Español", "Galego" };
BOOST_STATIC_ASSERT(ARRAY_SIZE(g_supported_lang_ids)== ARRAY_SIZE(g_supported_lang_names));
BOOST_STATIC_ASSERT(ARRAY_SIZE(g_supported_lang_names)== 5);

#ifdef ENABLE_WXWIDGETS
static const int g_supported_lang_wxids[]= { wxLANGUAGE_DEFAULT, wxLANGUAGE_CATALAN, wxLANGUAGE_ENGLISH, wxLANGUAGE_SPANISH, wxLANGUAGE_GALICIAN };
#endif
BOOST_STATIC_ASSERT(ARRAY_SIZE(g_supported_lang_ids)== ARRAY_SIZE(g_supported_lang_wxids));

// Number of entries
static const unsigned int g_supported_lang_num= ARRAY_SIZE(g_supported_lang_ids);

#ifdef WIN32

// Functions in win32env.cpp
extern int win32_putenv(const char *envval);
extern void win32_unsetenv(const char *name);

#ifndef _WIN32_WINNT
	#error "_WIN32_WINNT not defined"
#endif
#include <windows.h>

// Translate lang_ids into Windows language names
// Code borrowed from http://svn.gna.org/viewcvs/wesnoth/trunk/
static void locale_win_name(std::string& win_locale) {
	if (win_locale.empty()) return;

	// Simplified translation table from unix locale symbols to win32 locale strings
	if(win_locale=="af")
		win_locale = "Afrikaans";
	else if(win_locale=="ang")
		win_locale = "C";
	else if(win_locale=="ar")
		win_locale = "Arabic";
	else if(win_locale=="bg")
		win_locale = "Bulgarian";
	else if(win_locale=="bg")
		win_locale = "Bulgarian";
	else if(win_locale=="C")
		return;
	else if(win_locale=="ca")
		win_locale= "Catalan";
	else if(win_locale=="cs")
		win_locale = "Czech";
	else if(win_locale=="da")
		win_locale = "Danish";
	else if(win_locale=="de")
		win_locale = "German";
	else if(win_locale=="el")
		win_locale = "Greek";
	else if(win_locale=="en")
		win_locale = "English";
	else if(win_locale=="eo")
		win_locale = "C";
	else if(win_locale=="es")
		win_locale = "Spanish";
	else if(win_locale=="et")
		win_locale = "Estonian";
	else if(win_locale=="eu")
		win_locale = "Basque";
	else if(win_locale=="fi")
		win_locale = "Finnish";
	else if(win_locale=="fr")
		win_locale = "French";
	else if(win_locale=="fur")
		win_locale = "C";
	else if(win_locale=="ga")
		win_locale = "C";
	else if(win_locale=="gl")
		win_locale = "Galician";
	else if(win_locale=="he")
		win_locale = "Hebrew";
	else if(win_locale=="hr")
		win_locale = "Croatian";
	else if(win_locale=="hu")
		win_locale = "Hungarian";
	else if(win_locale=="id")
		win_locale = "Indonesian";
	else if(win_locale=="is")
		win_locale = "Icelandic";
	else if(win_locale=="it")
		win_locale = "Italian";
	else if(win_locale=="ja")
		win_locale = "Japanese";
	else if(win_locale=="ko")
		win_locale = "Korean";
	else if(win_locale=="la")
		win_locale = "C";
	else if(win_locale=="lt")
		win_locale = "Lithuanian";
	else if(win_locale=="lv")
		win_locale = "Latvian";
	else if(win_locale=="mk")
		win_locale = "Macedonian";
	else if(win_locale=="mr")
		win_locale = "C";
	else if(win_locale=="nb")
		win_locale = "Norwegian";
	else if(win_locale=="nl")
		win_locale = "Dutch";
	else if(win_locale=="pl")
		win_locale = "Polish";
	else if(win_locale=="pt")
		win_locale = "Portuguese";
	else if(win_locale=="racv")
		win_locale = "C";
	else if(win_locale=="ro")
		win_locale = "Romanian";
	else if(win_locale=="ru")
		win_locale = "Russian";
	else if(win_locale=="sk")
		win_locale = "Slovak";
	else if(win_locale=="sl")
		win_locale = "Slovenian";
	else if(win_locale=="sr")
		win_locale = "Serbian";
	else if(win_locale=="sv")
		win_locale = "Swedish";
	else if(win_locale=="tl")
		win_locale = "Filipino";
	else if(win_locale=="tr")
		win_locale = "Turkish";
	else if(win_locale=="vi")
		win_locale = "Vietnamese";
	else if(win_locale=="zh")
		win_locale = "Chinese";
	else {	
		assert(false); 
	}
}

#endif	// WIN32

// Code based on language.cpp from http://svn.gna.org/viewcvs/wesnoth/trunk/
// TODO: conider the use of tinygettext on Win32 due to the problems 
// which gettext has
static 
bool sp_setlocale(int category, std::string const &slocale,
	std::vector<std::string> const *alternates)
{
	std::string locale = slocale;

#ifdef _WIN32	
	{
		// Pick language part (2 or 3 chars)
		char gettextloc[4];
		gettextloc[0]= 0;
		unsigned int i= 0;
		for (; i< 3 && i< locale.size() && locale[i]!='_' && locale[i]!='@' && locale[i]!= '.'; ++i)
			gettextloc[i]= locale[i];
		gettextloc[i]= 0;

		// Translate into Windows locale
		locale= gettextloc;
		locale_win_name(locale);
	
		if(category == LC_MESSAGES) {
			// For windows we need to define the environtment variable LANGUAGE
			std::string env = std::string("LANGUAGE=") + gettextloc;
			win32_putenv(env.c_str());
			//SetEnvironmentVariableA("LANG", locale.c_str());
			return true;
		}
	}
#endif

	char *res = NULL;
	std::vector<std::string>::const_iterator i;
	if (alternates) i = alternates->begin();

	for (;;) {
		std::string lang = locale, extra;
		std::string::size_type pos = locale.find('@');
		if (pos != std::string::npos) {
			lang.erase(pos);
			extra = locale.substr(pos);
		}

		/*
		 * The "" is the last item to work-around a problem in glibc picking
		 * the non utf8 locale instead an utf8 version if available.
		 */
		char const *encoding[] = { ".utf-8", ".UTF-8", "" };
		for (int j = 0; j != 3; ++j) {
			locale = lang + encoding[j] + extra;
			res = std::setlocale(category, locale.c_str());
			if (res) {
				//std::cerr << "Set locale to '" << locale << "' result: '" << res << "'.\n";
				return true; //goto done;
			}
		}

		if (!alternates || i == alternates->end()) break;
		locale = *i;
		++i;
	}

	//std::cerr << "setlocale() failed for '" << slocale << "'.\n";
	/*
#ifndef _WIN32
#ifndef __AMIGAOS4__
	if(category == LC_MESSAGES) {
		std::cerr << "Setting LANGUAGE to '" << slocale << "'.\n";
		setenv("LANGUAGE", slocale.c_str(), 1);
		std::setlocale(LC_MESSAGES, "");
	}
#endif
#endif*/

	//done:
	//std::cerr << "Numeric locale: " << std::setlocale(LC_NUMERIC, NULL) << '\n';
	//std::cerr << "Full locale: " << std::setlocale(LC_ALL, NULL) << '\n';

	return false;
}

#ifdef ENABLE_WXWIDGETS
static
wxLocale& GetWxLocale()
{
	static wxLocale locale;
	return locale;
}
#endif

// Search lang id and returns the array position or -1 if not found
static
int SearchLangId(const char* id)
{
	if (!id) return -1;

	for (unsigned int i= 0; i< g_supported_lang_num; ++i)
		if (strcmp(id, g_supported_lang_ids[i])== 0) return (int) i;
	
	return -1;
}


SPEXPORT_FUNCTION
int spSetLanguage (const char* id)
{
	// Search whether the language id exists
	int langidx= SearchLangId(id);
	if (langidx== -1) return langidx;

#ifdef WIN32
	// Make sure associated runtime library is loaded
	gettext(NULL);
	
#endif

	// configuration for gettext
	// TODO: check errors
	sp_setlocale(LC_COLLATE, id, NULL);
	sp_setlocale(LC_TIME, id, NULL);
	sp_setlocale(LC_MESSAGES, id, NULL);

#ifdef ENABLE_WXWIDGETS
	// configuration for wx
	// TODO: consider using wxLOCALE_LOAD_DEFAULT flags (loads wxstd catalog)
	if (!GetWxLocale().Init(g_supported_lang_wxids[langidx], 0)) return -1;
#endif

	return 0;
}

SPEXPORT_FUNCTION
int spBindTextDomain(const char* domain, const char* dirname)
{
#ifdef ENABLE_WXWIDGETS
	GetWxLocale().AddCatalogLookupPathPrefix(wxString(dirname, wxConvUTF8));
	if (!GetWxLocale().AddCatalog(wxString(domain, wxConvUTF8))) return -1;
#endif

	bindtextdomain( domain, dirname );
	bind_textdomain_codeset (domain, "UTF-8");

	return 0;
}

SPEXPORT_FUNCTION
unsigned int spGetAvailableLanguages()
{
	return g_supported_lang_num;
}

SPEXPORT_FUNCTION
const char* spGetLanguageId (unsigned int idx)
{
	if (idx>= g_supported_lang_num) return NULL;

	return g_supported_lang_ids[idx];
}

SPEXPORT_FUNCTION
const char* spGetLanguageNativeName (unsigned int idx, const char* domain)
{
	if (idx>= g_supported_lang_num) return NULL;

	if (idx== 0) {
		if (domain) return dgettext(domain, g_supported_lang_names[0]);
		else return gettext(g_supported_lang_names[0]);
	}

	return g_supported_lang_names[idx];
}

/*
SPEXPORT_FUNCTION void TestLang()
{
	/// DEBUG
	spSetLanguage ("ca_ES");
	const char* localedir= spGetLocalesDir();
	if (spBindTextDomain("sitplus", localedir)== -1) 
		std::cerr << "Error binding domain";
	
#ifdef WIN32	
	const char* newtext= dgettext("sitplus", "Test message\n");
	BSTR unicodestr= SysAllocStringLen(NULL, lstrlenA(newtext)+1);
	MultiByteToWideChar(CP_UTF8, 0, newtext, -1, unicodestr, lstrlenA(newtext)+1);
	wprintf (unicodestr);
	SysFreeString(unicodestr);
	wprintf (L"Second round\n");
	wprintf (_("Test message\n"));
#else
	#define GETTEXT_DOMAIN "sitplus"
	printf (__("Test message\n"));
#endif
	/// DEBUG
}
*/
