/* Determine the user's language preferences.
   Copyright (C) 2004-2007, 2015-2016 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* Written by Bruno Haible <bruno@clisp.org>.
   Win32 code originally by Michele Cicciotti <hackbunny@reactos.com>.  */

#ifdef _LIBC
# include "lib.h"
# else
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#endif

#include <stdlib.h>
#include "gettextP.h"

#ifdef HAVE_CFPREFERENCESCOPYAPPVALUE
# include <string.h>
# include <CoreFoundation/CFPreferences.h>
# include <CoreFoundation/CFPropertyList.h>
# include <CoreFoundation/CFArray.h>
# include <CoreFoundation/CFString.h>
#endif

#if defined _WIN32 || defined __WIN32__
# define WIN32_NATIVE
#endif

#ifdef WIN32_NATIVE
# define WIN32_LEAN_AND_MEAN
# include <windows.h>

# ifndef MUI_LANGUAGE_NAME
# define MUI_LANGUAGE_NAME 8
# endif
# ifndef STATUS_BUFFER_OVERFLOW
# define STATUS_BUFFER_OVERFLOW 0x80000005
# endif


/* Get the preferences list through the MUI APIs. This works on Windows Vista
   and newer.  */
static const char *
_nl_language_preferences_win32_mui (HMODULE kernel32)
{
  /* DWORD GetUserPreferredUILanguages (ULONG dwFlags,
                                        PULONG pulNumLanguages,
                                        PWSTR pwszLanguagesBuffer,
                                        PULONG pcchLanguagesBuffer);  */
  typedef DWORD (WINAPI *GetUserPreferredUILanguages_func) (ULONG, PULONG, PWSTR, PULONG);
  GetUserPreferredUILanguages_func p_GetUserPreferredUILanguages;

  p_GetUserPreferredUILanguages =
   (GetUserPreferredUILanguages_func)
   GetProcAddress (kernel32, "GetUserPreferredUILanguages");
  if (p_GetUserPreferredUILanguages != NULL)
    {
      ULONG num_languages;
      ULONG bufsize;
      DWORD ret;

      bufsize = 0;
      ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,
                                           &num_languages,
                                           NULL, &bufsize);
      if (ret == 0
          && GetLastError () == STATUS_BUFFER_OVERFLOW
          && bufsize > 0)
        {
          WCHAR *buffer = (WCHAR *) malloc (bufsize * sizeof (WCHAR));
          if (buffer != NULL)
            {
              ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,
                                                   &num_languages,
                                                   buffer, &bufsize);
              if (ret)
                {
                  /* Convert the list from NUL-delimited WCHAR[] Win32 locale
                     names to colon-delimited char[] Unix locale names.
                     We assume that all these locale names are in ASCII,
                     nonempty and contain no colons.  */
                  char *languages =
                    (char *) malloc (bufsize + num_languages * 10 + 1);
                  if (languages != NULL)
                    {
                      const WCHAR *p = buffer;
                      char *q = languages;
                      ULONG i;
                      for (i = 0; i < num_languages; i++)
                        {
                          char *q1;
                          char *q2;

                          q1 = q;
                          if (i > 0)
                            *q++ = ':';
                          q2 = q;
                          for (; *p != (WCHAR)'\0'; p++)
                            {
                              if ((unsigned char) *p != *p || *p == ':')
                                {
                                  /* A non-ASCII character or a colon inside
                                     the Win32 locale name! Punt.  */
                                  q = q1;
                                  break;
                                }
                              *q++ = (unsigned char) *p;
                            }
                          if (q == q1)
                            /* An unexpected Win32 locale name occurred.  */
                            break;
                          *q = '\0';
                          _nl_locale_name_canonicalize (q2);
                          q = q2 + strlen (q2);
                          p++;
                        }
                      *q = '\0';
                      if (q > languages)
                        {
                          free (buffer);
                          return languages;
                        }
                      free (languages);
                    }
                }
              free (buffer);
            }
        }
    }
  return NULL;
}

/* Get a preference.  This works on Windows ME and newer.  */
static const char *
_nl_language_preferences_win32_ME (HMODULE kernel32)
{
  /* LANGID GetUserDefaultUILanguage (void);  */
  typedef LANGID (WINAPI *GetUserDefaultUILanguage_func) (void);
  GetUserDefaultUILanguage_func p_GetUserDefaultUILanguage;

  p_GetUserDefaultUILanguage =
   (GetUserDefaultUILanguage_func)
   GetProcAddress (kernel32, "GetUserDefaultUILanguage");
  if (p_GetUserDefaultUILanguage != NULL)
    return _nl_locale_name_from_win32_LANGID (p_GetUserDefaultUILanguage ());
  return NULL;
}

/* Get a preference.  This works on Windows 95 and newer.  */
static const char *
_nl_language_preferences_win32_95 (void)
{
  HKEY desktop_resource_locale_key;

  if (RegOpenKeyExA (HKEY_CURRENT_USER,
                     "Control Panel\\Desktop\\ResourceLocale",
                     0, KEY_QUERY_VALUE, &desktop_resource_locale_key)
      == NO_ERROR)
    {
      DWORD type;
      BYTE data[8 + 1];
      DWORD data_size = sizeof (data);
      DWORD ret;

      ret = RegQueryValueExA (desktop_resource_locale_key, NULL, NULL,
                              &type, data, &data_size);
      RegCloseKey (desktop_resource_locale_key);

      if (ret == NO_ERROR)
        {
          /* We expect a string, at most 8 bytes long, that parses as a
             hexadecimal number.  */
          if (type == REG_SZ
              && data_size <= sizeof (data)
              && (data_size < sizeof (data)
                  || data[sizeof (data) - 1] == '\0'))
            {
              LCID lcid;
              char *endp;
              /* Ensure it's NUL terminated.  */
              if (data_size < sizeof (data))
                data[data_size] = '\0';
              /* Parse it as a hexadecimal number.  */
              lcid = strtoul ((char *) data, &endp, 16);
              if (endp > (char *) data && *endp == '\0')
                return _nl_locale_name_from_win32_LCID (lcid);
            }
        }
    }
  return NULL;
}

/* Get the system's preference.  This can be used as a fallback.  */
static BOOL CALLBACK
ret_first_language (HMODULE h, LPCSTR type, LPCSTR name, WORD lang, LONG_PTR param)
{
  (void) h;
  (void) type;
  (void) name;
  *(const char **)param = _nl_locale_name_from_win32_LANGID (lang);
  return FALSE;
}
static const char *
_nl_language_preferences_win32_system (HMODULE kernel32)
{
  const char *languages = NULL;
  /* Ignore the warning on mingw here. mingw has a wrong definition of the last
     parameter type of ENUMRESLANGPROC.  */
  EnumResourceLanguagesA (kernel32, MAKEINTRESOURCEA(16) /* RT_VERSION */, MAKEINTRESOURCEA (1),
                         ret_first_language, (LONG_PTR)&languages);
  return languages;
}

#endif


#ifdef __MINT__
#include <mint/cookie.h>

static const char *_nl_language_preferences_mint(void)
{
	long akp = 0;
	
	if (Getcookie(C__AKP, &akp) == C_FOUND)
	{
		switch ((int)((akp >> 8) & 0xff))
		{
			/* these are documented in the compendium */
			case 0: return "en_US";
			case 1: return "de_DE";
			case 2: return "fr_FR";
			case 3: return "en_GB";
			case 4: return "es_ES";
			case 5: return "it_IT";
			case 6: return "sv_SE";
			case 7: return "fr_CH";
			case 8: return "de_CH";
			case 9: return "tr_TR";
			case 10: return "fi_FI";
			case 11: return "no_NO";
			case 12: return "da_DK";
			case 13: return "ar_SA";
			case 14: return "nl_NL";
			case 15: return "cs_CZ";
			case 16: return "hu_HU";
			/* http://toshyp.atari.org/en/003007.html#Cookie_2C_20_AKP */
			case 17: return "pl_PL";
			case 18: return "lt_LT";
			case 19: return "ru_RU";
			case 20: return "et_EE";
			case 21: return "be_BY";
			case 22: return "uk_UA";
			case 23: return "sk_SK";
			case 24: return "ro_RO";
			case 25: return "bg_BG";
			case 26: return "sl_SI";
			case 27: return "hr_HR";
			case 28: return "sr_RS";
			case 29: return "sr_ME";
			case 30: return "mk_MK";
			case 31: return "el_GR";
			case 32: return "lv_LV";
			case 33: return "he_IL";
			case 34: return "af_ZA"; /* ambigious */
			case 35: return "pt_PT";
			case 36: return "fr_BE";
			case 37: return "ja_JP";
			case 38: return "zh_CN";
			case 39: return "ko_KR";
			case 40: return "vi_VN";
			case 41: return "ar_IN"; /* ambigious */
			case 42: return "fa_IR";
			case 43: return "mn_MN";
			case 44: return "ne_NP";
			case 45: return "lo_LA";
			case 46: return "km_KH";
			case 47: return "id_ID";
			case 48: return "bn_BD";
		}
	}
	return NULL;
}
#endif


/* Determine the user's language preferences, as a colon separated list of
   locale names in XPG syntax
     language[_territory][.codeset][@modifier]
   The result must not be freed; it is statically allocated.
   The LANGUAGE environment variable does not need to be considered; it is
   already taken into account by the caller.  */

const char *
_nl_language_preferences_default (void)
{
#ifdef HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.2 or newer */
  {
    /* Cache the preferences list, since CoreFoundation calls are expensive.  */
    static const char *cached_languages;
    static int cache_initialized;

    if (!cache_initialized)
      {
        CFTypeRef preferences =
          CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"),
                                     kCFPreferencesCurrentApplication);
        if (preferences != NULL
            && CFGetTypeID (preferences) == CFArrayGetTypeID ())
          {
            CFArrayRef prefArray = (CFArrayRef)preferences;
            int n = CFArrayGetCount (prefArray);
            char buf[256];
            size_t size = 0;
            int i;

            for (i = 0; i < n; i++)
              {
                CFTypeRef element = CFArrayGetValueAtIndex (prefArray, i);
                if (element != NULL
                    && CFGetTypeID (element) == CFStringGetTypeID ()
                    && CFStringGetCString ((CFStringRef)element,
                                           buf, sizeof (buf),
                                           kCFStringEncodingASCII))
                  {
                    _nl_locale_name_canonicalize (buf);
                    size += strlen (buf) + 1;
                    /* Most GNU programs use msgids in English and don't ship
                       an en.mo message catalog.  Therefore when we see "en"
                       in the preferences list, arrange for gettext() to
                       return the msgid, and ignore all further elements of
                       the preferences list.  */
                    if (strcmp (buf, "en") == 0)
                      break;
                  }
                else
                  break;
              }
            if (size > 0)
              {
                char *languages = (char *) malloc (size);

                if (languages != NULL)
                  {
                    char *p = languages;

                    for (i = 0; i < n; i++)
                      {
                        CFTypeRef element =
                          CFArrayGetValueAtIndex (prefArray, i);
                        if (element != NULL
                            && CFGetTypeID (element) == CFStringGetTypeID ()
                            && CFStringGetCString ((CFStringRef)element,
                                                   buf, sizeof (buf),
                                                   kCFStringEncodingASCII))
                          {
                            _nl_locale_name_canonicalize (buf);
                            strcpy (p, buf);
                            p += strlen (buf);
                            *p++ = ':';
                            if (strcmp (buf, "en") == 0)
                              break;
                          }
                        else
                          break;
                      }
                    *--p = '\0';

                    cached_languages = languages;
                  }
              }
          }
        cache_initialized = 1;
      }
    if (cached_languages != NULL)
      return cached_languages;
  }
#endif

#ifdef WIN32_NATIVE
  {
    /* Cache the preferences list, since computing it is expensive.  */
    static const char *cached_languages;
    static int cache_initialized;

    /* Activate the new code only when the GETTEXT_MUI environment variable is
       set, for the time being, since the new code is not well tested.  */
    if (!cache_initialized && getenv ("GETTEXT_MUI") != NULL)
      {
        const char *languages = NULL;
        HMODULE kernel32 = GetModuleHandleA ("kernel32");

        if (kernel32 != NULL)
          languages = _nl_language_preferences_win32_mui (kernel32);

        if (languages == NULL && kernel32 != NULL)
          languages = _nl_language_preferences_win32_ME (kernel32);

        if (languages == NULL)
          languages = _nl_language_preferences_win32_95 ();

        if (languages == NULL && kernel32 != NULL)
          languages = _nl_language_preferences_win32_system (kernel32);

        cached_languages = languages;
        cache_initialized = 1;
      }
    if (cached_languages != NULL)
      return cached_languages;
  }
#endif

#ifdef __MINT__
   {
   	  const char *p = _nl_language_preferences_mint();
   	  if (p != NULL)
   	  	return p;
   }
#endif

  return NULL;
}
