/*
 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
 */
#include "e.h"

static Ecore_Exe *_e_intl_input_method_exec = NULL;
static Ecore_Event_Handler *_e_intl_exit_handler = NULL;

static char *_e_intl_orig_lc_messages = NULL;
static char *_e_intl_orig_language = NULL;
static char *_e_intl_orig_lc_all = NULL;
static char *_e_intl_orig_lang = NULL;
static char *_e_intl_language = NULL;

static char *_e_intl_language_alias = NULL;

static char *_e_intl_orig_xmodifiers = NULL;
static char *_e_intl_orig_qt_im_module = NULL; 
static char *_e_intl_orig_gtk_im_module = NULL;

static const char *_e_intl_imc_personal_path = NULL;
static const char *_e_intl_imc_system_path = NULL;

static Eet_Data_Descriptor *_e_intl_input_method_config_edd = NULL;

#define E_EXE_STOP(EXE) if (EXE != NULL) { ecore_exe_terminate(EXE); ecore_exe_free(EXE); EXE = NULL; }
#define E_EXE_IS_VALID(EXE) (!((EXE == NULL) || (EXE[0] == 0)))

/* All locale parts */
#define E_INTL_LOC_ALL		E_INTL_LOC_LANG | \
				E_INTL_LOC_REGION | \
				E_INTL_LOC_CODESET | \
				E_INTL_LOC_MODIFIER

/* Locale parts wich are significant when Validating */
#define E_INTL_LOC_SIGNIFICANT	E_INTL_LOC_LANG | \
				E_INTL_LOC_REGION | \
				E_INTL_LOC_CODESET

/* Language Setting and Listing */
static char		*_e_intl_language_path_find(char *language);
static Evas_List 	*_e_intl_language_dir_scan(const char *dir);
static int 		 _e_intl_language_list_find(Evas_List *language_list, char *language);

/* Locale Validation and Discovery */
static Evas_Hash	*_e_intl_locale_alias_hash_get(void);
static char		*_e_intl_locale_alias_get(const char *language);
static Evas_List	*_e_intl_locale_system_locales_get(void);
static Evas_List	*_e_intl_locale_search_order_get(const char *locale);
static int		 _e_intl_locale_validate(const char *locale);
static void 		 _e_intl_locale_hash_free(Evas_Hash *language_hash);
static Evas_Bool 	 _e_intl_locale_hash_free_cb(Evas_Hash *hash, const char *key, void *data, void *fdata);

/* Input Method Configuration and Management */
static int 		 _e_intl_cb_exit(void *data, int type, void *event);
static Evas_List 	*_e_intl_imc_dir_scan(const char *dir);

EAPI int
e_intl_init(void)
{
   char *s;
   
   _e_intl_input_method_config_edd = E_CONFIG_DD_NEW("input_method_config", E_Input_Method_Config);
   E_CONFIG_VAL(_e_intl_input_method_config_edd, E_Input_Method_Config, version, INT);
   E_CONFIG_VAL(_e_intl_input_method_config_edd, E_Input_Method_Config, e_im_name, STR);
   E_CONFIG_VAL(_e_intl_input_method_config_edd, E_Input_Method_Config, gtk_im_module, STR);
   E_CONFIG_VAL(_e_intl_input_method_config_edd, E_Input_Method_Config, qt_im_module, STR);
   E_CONFIG_VAL(_e_intl_input_method_config_edd, E_Input_Method_Config, xmodifiers, STR);
   E_CONFIG_VAL(_e_intl_input_method_config_edd, E_Input_Method_Config, e_im_exec, STR);
   E_CONFIG_VAL(_e_intl_input_method_config_edd, E_Input_Method_Config, e_im_setup_exec, STR); 

   if ((s = getenv("LC_MESSAGES"))) _e_intl_orig_lc_messages = strdup(s);
   if ((s = getenv("LANGUAGE"))) _e_intl_orig_language = strdup(s);
   if ((s = getenv("LC_ALL"))) _e_intl_orig_lc_all = strdup(s);
   if ((s = getenv("LANG"))) _e_intl_orig_lang = strdup(s);

   if ((s = getenv("GTK_IM_MODULE"))) _e_intl_orig_gtk_im_module = strdup(s);
   if ((s = getenv("QT_IM_MODULE"))) _e_intl_orig_qt_im_module = strdup(s);
   if ((s = getenv("XMODIFIERS"))) _e_intl_orig_xmodifiers = strdup(s);
   
   return 1;
}

EAPI int
e_intl_shutdown(void)
{
   E_FREE(_e_intl_language);
   E_FREE(_e_intl_orig_lc_messages);
   E_FREE(_e_intl_orig_language);
   E_FREE(_e_intl_orig_lc_all);
   E_FREE(_e_intl_orig_lang);
   
   E_FREE(_e_intl_orig_gtk_im_module);
   E_FREE(_e_intl_orig_qt_im_module);
   E_FREE(_e_intl_orig_xmodifiers);

   if (_e_intl_imc_personal_path)
     evas_stringshare_del(_e_intl_imc_personal_path); 
   if (_e_intl_imc_system_path)
     evas_stringshare_del(_e_intl_imc_system_path);
   
   E_CONFIG_DD_FREE(_e_intl_input_method_config_edd);
  
   return 1;
}

/* Setup configuration settings and start services */
EAPI int
e_intl_post_init(void)
{
   if ((e_config->language) && (e_config->language[0] != 0))
     e_intl_language_set(e_config->language);
   else 
     e_intl_language_set(NULL);
   
   if ((e_config->input_method) && (e_config->input_method[0] != 0))
     e_intl_input_method_set(e_config->input_method); 

   _e_intl_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_intl_cb_exit, NULL);
   return 1;
}

EAPI int
e_intl_post_shutdown(void)
{
   if (_e_intl_exit_handler)
     {
	ecore_event_handler_del(_e_intl_exit_handler);
	_e_intl_exit_handler = NULL;			          
     }
   
   e_intl_input_method_set(NULL);
   
   e_intl_language_set(NULL);
   E_FREE(_e_intl_language_alias);
   
   E_EXE_STOP(_e_intl_input_method_exec);
   return 1;
}

/*
 * TODO 
 * - Add error dialogs explaining any errors while setting the locale
 *      * Locale aliases need to be configured
 *      * Locale is invalid
 *      * Message files are not found for this locale, then we have (en_US, POSIX, C)
 * - Add support of compound locales i.e. (en_US;zh_CN;C) ==Defer==
 * - Add Configuration for to-be-set environment variables
 */
EAPI void
e_intl_language_set(const char *lang)
{
   int set_envars;
    
   set_envars = 1;
   /* NULL lang means set everything back to the original environment 
    * defaults 
    */
   if (!lang)
     {
	e_util_env_set("LC_MESSAGES", _e_intl_orig_lc_messages);
	e_util_env_set("LANGUAGE", _e_intl_orig_language);
	e_util_env_set("LC_ALL", _e_intl_orig_lc_all);
	e_util_env_set("LANG", _e_intl_orig_lang);
	
	lang = getenv("LC_MESSAGES");
	if (!lang) lang = getenv("LANGUAGE");
	if (!lang) lang = getenv("LC_ALL");
	if (!lang) lang = getenv("LANG");
	
	set_envars = 0;
     }
   
   E_FREE(_e_intl_language_alias);
   _e_intl_language_alias = _e_intl_locale_alias_get(lang);  
   E_FREE(_e_intl_language);
   
   if (lang)
     _e_intl_language = strdup(lang);
   else
     _e_intl_language = NULL;
 
   if (!_e_intl_locale_validate(_e_intl_language_alias))
     {
	fprintf(stderr, "The locale '%s' cannot be found on your "
	       "system. Please install this locale or try "
               "something else.", _e_intl_language_alias);
     }
   else
     {
	/* Only set env vars is a non NULL locale was passed */
	if (set_envars)
	  {
	     e_util_env_set("LANGUAGE", _e_intl_language);
	     e_util_env_set("LANG", _e_intl_language);
	     e_util_env_set("LC_ALL", _e_intl_language);
	     e_util_env_set("LC_MESSAGES", _e_intl_language);
	  }
 
    	setlocale(LC_ALL, _e_intl_language);
        if (_e_intl_language)
	  {
             char *locale_path;
             
             locale_path = _e_intl_language_path_find(_e_intl_language_alias);
             if (locale_path == NULL)
	       {
		  E_Locale_Parts *locale_parts;

		  locale_parts = e_intl_locale_parts_get(_e_intl_language_alias);
   
		  /* If locale is C or some form of en don't report an error */
		  if ( locale_parts == NULL && strcmp (_e_intl_language_alias, "C") )
		    {
		       fprintf(stderr, 
			     "An error occurred setting your locale. \n\n"
			     
			     "The locale you have chosen '%s' appears to \n"
			     "be an alias, however, it can not be resloved.\n"
			     "Please make sure you have a 'locale.alias' \n"
			     "file in your 'messages' path which can resolve\n"
			     "this alias.\n\n"
			     
			     "Enlightenment will not be translated.\n", 
			     _e_intl_language_alias);
		    }
		  else if ( locale_parts && locale_parts->lang && strcmp(locale_parts->lang, "en") ) 
		    {
		       fprintf(stderr, 
			     "An error occurred setting your locale. \n\n"
				
			     "The translation files for the locale you \n"
			     "have chosen (%s) cannot be found in your \n"
			     "'messages' path. \n\n"
			     
			     "Enlightenment will not be translated.\n", 
			     _e_intl_language_alias);
		    }
		  e_intl_locale_parts_free(locale_parts);
	       }
	     else
	       {
		  bindtextdomain(PACKAGE, locale_path);
		  textdomain(PACKAGE);
		  bind_textdomain_codeset(PACKAGE, "UTF-8");
		  free(locale_path);
               } 
	  }
     }
}

EAPI const char *
e_intl_language_get(void)
{
   return _e_intl_language;
}

EAPI const char	*
e_intl_language_alias_get(void)
{
   return _e_intl_language_alias;
}

EAPI Evas_List *
e_intl_language_list(void)
{
   Evas_List *next;
   Evas_List *dir_list;
   Evas_List *all_languages;

   all_languages = NULL;
   dir_list = e_path_dir_list_get(path_messages);
   for (next = dir_list ; next ; next = next->next)
     {
	E_Path_Dir *epd;        
	Evas_List *dir_languages;
	        
	epd = next->data;		        
	dir_languages = _e_intl_language_dir_scan(epd->dir);
	while (dir_languages)
	  {
	     char *language;

	     language = dir_languages->data;
	     dir_languages = evas_list_remove_list(dir_languages, dir_languages);

	     if (  _e_intl_language_list_find(all_languages, language) || (strlen(language) > 2 && 
		      !_e_intl_locale_validate(language)))
	       {
		  free(language);
	       }
	     else
	       {
		  all_languages = evas_list_append(all_languages, language);
	       }
	  }
     }
   
   e_path_dir_list_free(dir_list);

   return all_languages;
}

static int
_e_intl_language_list_find(Evas_List *language_list, char *language)
{
   Evas_List *l;
   
   if (!language_list) return 0;
   if (!language) return 0;

   for (l = language_list; l; l = l->next)
     {
	char *lang;

	lang = l->data;
	if (!strcmp(lang, language)) return 1;
     }
   
   return 0;
}

EAPI void
e_intl_input_method_set(const char *imc_path)
{
   if (!imc_path)
     {
	E_EXE_STOP(_e_intl_input_method_exec); 
	e_util_env_set("GTK_IM_MODULE", _e_intl_orig_gtk_im_module);
        e_util_env_set("QT_IM_MODULE", _e_intl_orig_qt_im_module);
        e_util_env_set("XMODIFIERS", _e_intl_orig_xmodifiers);
     }	
   
   if (imc_path) 
     {   
	Eet_File *imc_ef;
	E_Input_Method_Config *imc;

	imc_ef = eet_open(imc_path, EET_FILE_MODE_READ);
	if (imc_ef)
	  {
	     imc = e_intl_input_method_config_read(imc_ef);
	     eet_close(imc_ef);
	     
	     if (imc) 	  
	       {	     
	          e_util_env_set("GTK_IM_MODULE", imc->gtk_im_module);
	          e_util_env_set("QT_IM_MODULE", imc->qt_im_module);
	          e_util_env_set("XMODIFIERS", imc->xmodifiers);
		 
		  E_EXE_STOP(_e_intl_input_method_exec); 
		  
		  if (E_EXE_IS_VALID(imc->e_im_exec)) 
		    {
		       e_util_library_path_strip();
		       _e_intl_input_method_exec = ecore_exe_run(imc->e_im_exec, NULL);
		       e_util_library_path_restore();
		       ecore_exe_tag_set(_e_intl_input_method_exec,"E/im_exec");
	       	       
		       if (  !_e_intl_input_method_exec || 
			     !ecore_exe_pid_get(_e_intl_input_method_exec))    
			 e_util_dialog_show(_("Input Method Error"),
					    _( "Error starting the input method executable<br><br>"
					       
					       "please make sure that your input<br>"
					       "method configuration is correct and<br>"
					       "that your configuration's<br>"
					       "executable is in your PATH<br>"));  
		    }
		  e_intl_input_method_config_free(imc);
	       }
	  }
     }   
}

EAPI Evas_List *
e_intl_input_method_list(void)
{
   Evas_List *input_methods;
   Evas_List *im_list;
   Evas_List *l;
   char *imc_path;

   im_list = NULL;
 
   /* Personal Path */  
   input_methods = _e_intl_imc_dir_scan(e_intl_imc_personal_path_get());
   for (l = input_methods; l; l = l->next)
     {
	imc_path = l->data;
	im_list = evas_list_append(im_list, imc_path);
     }

   while (input_methods)
     {
	input_methods = evas_list_remove_list(input_methods, input_methods);
     }

   /* System Path */
   input_methods = _e_intl_imc_dir_scan(e_intl_imc_system_path_get());
   for (l = input_methods; l; l = l->next)
     {
	imc_path = l->data;
	im_list = evas_list_append(im_list, imc_path);
     }

   while (input_methods)
     {
	input_methods = evas_list_remove_list(input_methods, input_methods);
     }

   return im_list;
}

const char *
e_intl_imc_personal_path_get(void)
{
   if (_e_intl_imc_personal_path == NULL)
     {
	char buf[4096];
	
	snprintf(buf, sizeof(buf), "%s/.e/e/input_methods", e_user_homedir_get());
	_e_intl_imc_personal_path = evas_stringshare_add(buf);
     }
   return _e_intl_imc_personal_path;
}

const char *
e_intl_imc_system_path_get(void)
{
   if (_e_intl_imc_system_path == NULL)
     {
	char buf[4096];
	
	snprintf(buf, sizeof(buf), "%s/data/input_methods", e_prefix_data_get());
	_e_intl_imc_system_path = evas_stringshare_add(buf);
     }
   return _e_intl_imc_system_path;

}

/* Get the input method configuration from the file */
EAPI E_Input_Method_Config *
e_intl_input_method_config_read(Eet_File *imc_file)
{
   E_Input_Method_Config *imc;
   
   imc = NULL;
   if (imc_file)	  
     {     
	imc = (E_Input_Method_Config *) eet_data_read(imc_file, _e_intl_input_method_config_edd, "imc");
     }
   return imc;
}

/* Write the input method configuration to the file */
EAPI int
e_intl_input_method_config_write(Eet_File *imc_file, E_Input_Method_Config *imc)
{
   int ok = 0;

   if (imc_file)
     {
	ok = eet_data_write(imc_file, _e_intl_input_method_config_edd, "imc", imc, 0);
     }
   return ok;
}

EAPI void
e_intl_input_method_config_free(E_Input_Method_Config *imc)
{
   if (imc != NULL)
     {
	if (imc->e_im_name) evas_stringshare_del(imc->e_im_name);
	if (imc->gtk_im_module) evas_stringshare_del(imc->gtk_im_module);
	if (imc->qt_im_module) evas_stringshare_del(imc->qt_im_module);
	if (imc->xmodifiers) evas_stringshare_del(imc->xmodifiers);
	if (imc->e_im_exec) evas_stringshare_del(imc->e_im_exec);
	if (imc->e_im_setup_exec) evas_stringshare_del(imc->e_im_setup_exec);
	E_FREE(imc);
     }
}

static int
_e_intl_cb_exit(void *data, int type, void *event)
{
   Ecore_Exe_Event_Del *ev;
   
   ev = event;
   if (!ev->exe) return 1;
   
   if (!(ecore_exe_tag_get(ev->exe) && 
	 (!strcmp(ecore_exe_tag_get(ev->exe), "E/im_exec")))) return 1;

   _e_intl_input_method_exec = NULL;
   return 1;
}

static void
_e_intl_locale_hash_free(Evas_Hash *locale_hash)
{
   if(!locale_hash) return;
   evas_hash_foreach(locale_hash, _e_intl_locale_hash_free_cb, NULL);
   evas_hash_free(locale_hash);
}

static Evas_Bool
_e_intl_locale_hash_free_cb(Evas_Hash *hash __UNUSED__, const char *key __UNUSED__, void *data, void *fdata __UNUSED__)
{
   free(data);
   return 1;
}


/* 
 * get the directory associated with the language. Language Must be valid alias
 * i.e. Already validated and already de-aliased.
 *
 * NULL means:
 *  1) The user does not have an enlightenment translation for this locale
 *  2) The user does not have their locale.aliases configured correctly
 * 
 * @return NULL if not found.    
 */
static char *
_e_intl_language_path_find(char *language)
{
   char		*directory;
   Evas_List	*dir_list;
   Evas_List	*search_list;
   Evas_List	*next_dir;
   Evas_List	*next_search;
   int		 found;

   search_list = _e_intl_locale_search_order_get(language); 

   if (search_list == NULL) return NULL;

   directory = NULL;
   found = 0;
   dir_list = e_path_dir_list_get(path_messages);
   
   /* For each directory in the path */
   for (next_dir = dir_list ; next_dir && !found ; next_dir = next_dir->next)
     {
	E_Path_Dir *epd;
	epd = next_dir->data;
	
	/* Match canonicalized locale against each possible search */
	for (next_search = search_list ; next_search && !found ; next_search = next_search->next)
	  {
	     char *search_locale;
	     char message_path[PATH_MAX];
	       
	     search_locale = next_search->data;
	     snprintf(message_path, sizeof(message_path), "%s/%s/LC_MESSAGES/%s.mo", epd->dir, search_locale, PACKAGE);
	     
	     if (ecore_file_exists(message_path) && !ecore_file_is_dir(message_path))
	       {
		  directory = strdup(epd->dir);
		  found = 1;
	       }
	  } 
     }
   
   e_path_dir_list_free(dir_list);
   
   while (search_list)
     {
	char *data;
	data = search_list->data;
	free(data);
	search_list = evas_list_remove_list(search_list, search_list);
     }

   return directory;
}

static Evas_List *
_e_intl_language_dir_scan(const char *dir)
{
   Evas_List *languages;
   Ecore_List *files;
   char *file;
   
   languages = NULL;
   
   files = ecore_file_ls(dir);
   if (!files) return NULL;
  
   ecore_list_goto_first(files);
   if (files)
     {
	while ((file = ecore_list_next(files)))
	  {
	     char file_path[PATH_MAX];
	     
	     snprintf(file_path, sizeof(file_path),"%s/%s/LC_MESSAGES/%s.mo", 
		   dir, file, PACKAGE);
	     if (ecore_file_exists(file_path) && !ecore_file_is_dir(file_path))
	       languages = evas_list_append(languages, strdup(file));

	  }
	ecore_list_destroy(files);
     }
   return languages;
}

/* get the alias for a locale 
 * 
 * return pointer to allocated alias string. never returns NULL
 * String will be the same if its a valid locale already or there 
 * is no alias. 
 */
static char *
_e_intl_locale_alias_get(const char *language)
{
   Evas_Hash *alias_hash;
   char *alias;
   
   if (language == NULL || !strncmp(language, "POSIX", strlen("POSIX")))
     return strdup("C");
   
   alias_hash = _e_intl_locale_alias_hash_get();
   if (alias_hash == NULL) /* No alias file available */
     {  
	return strdup(language);
     }

     {
	char *lower_language;
	int i;
	
	lower_language = malloc(strlen(language) + 1);
	for (i = 0; i < strlen(language); i++)
	     lower_language[i] = tolower(language[i]);
	lower_language[i] = 0;     
	
	alias = evas_hash_find(alias_hash, lower_language);
	free(lower_language);
     }
   
   if (alias) 
     {
	alias = strdup(alias);
     }
   else 
     {
	alias = strdup(language);
     }
  
   _e_intl_locale_hash_free(alias_hash);
   
   return alias;
}

static Evas_Hash *
_e_intl_locale_alias_hash_get(void)
{
   Evas_List *next;
   Evas_List *dir_list;
   Evas_Hash *alias_hash; 
     
   dir_list = e_path_dir_list_get(path_messages);
   alias_hash = NULL;
   
   for (next = dir_list ; next ; next = next->next)
     {
	char buf[4096];
	E_Path_Dir *epd;
	FILE *f;       	
   
	epd = next->data;
	
	snprintf(buf, sizeof(buf), "%s/locale.alias", epd->dir);
	f = fopen(buf, "r");
	if (f)
	  {
	     char alias[4096], locale[4096];
	     
	     /* read locale alias lines */
	     while (fscanf(f, "%4090s %[^\n]\n", alias, locale) == 2)
	       {
		  /* skip comments */
		  if ((alias[0] == '!') || (alias[0] == '#'))
		    continue;

		  /* skip dupes */
		  if (evas_hash_find(alias_hash, alias))
		    continue;
		  
		  alias_hash = evas_hash_add(alias_hash, alias, strdup(locale));
	       }
	     fclose (f);
	  }
     }
   e_path_dir_list_free(dir_list);
   
   return alias_hash;
}

/* return parts of the locale that are requested in the mask 
 * return null if the locale looks to be invalid (Does not have 
 * ll_DD)
 * 
 * the returned string needs to be freed
 */
/*
 * Not canonic, just gets the parts 
 */
EAPI E_Locale_Parts *
e_intl_locale_parts_get(const char *locale)
{
   /* Parse Results */
   E_Locale_Parts *locale_parts;
   char  language[4];
   char  territory[4];
   char  codeset[32];
   char  modifier[32];

   /* Parse State */
   int   state = 0; /* start out looking for the language */
   int   locale_idx;
   int   tmp_idx = 0;
   
   /* Parse Loop - Seperators are _ . @ */
   for ( locale_idx = 0; locale_idx < strlen(locale); locale_idx++ )
     {
	char locale_char;
	locale_char = locale[locale_idx];
	
	/* we have finished scanning the locale string */
	if(locale_char == 0)
	  break;
	
	/* scan this character based on the current start */
	switch(state)
	  {
	   case 0: /* Gathering Language */
	      if (tmp_idx == 2 && locale_char == '_')
		{
		   state++;
		   language[tmp_idx] = 0;
		   tmp_idx = 0;
		}
	      else if (tmp_idx < 2 && islower(locale_char))
		{
		   language[tmp_idx++] = locale_char;
		}
	      else
		{
		   return NULL;
		}
	      break;
	   case 1: /* Gathering Territory */
	      if (tmp_idx == 2 && locale_char == '.')
		{
		   state++;
		   territory[tmp_idx] = 0;
		   tmp_idx = 0;
		}
	      else if(tmp_idx == 2 && locale_char == '@')
		{
		   state += 2;
		   territory[tmp_idx] = 0;
		   codeset[0] = 0;
		   tmp_idx = 0;
		}
	      else if(tmp_idx < 2 && isupper(locale_char))
		{
		   territory[tmp_idx++] = locale_char;
		}
	      else 
		{
		   return NULL;
		}
	      break;
	   case 2: /* Gathering Codeset */
	      if (locale_char == '@')
		{
		   state++;
		   codeset[tmp_idx] = 0;
		   tmp_idx = 0;
		}
	      else if (tmp_idx < 32)
		{
		   codeset[tmp_idx++] = locale_char;
		}
	      else
		{
		   return NULL;
		}
	      break;
	   case 3: /* Gathering modifier */
	      if (tmp_idx < 32)
		{
		   modifier[tmp_idx++] = locale_char;
		}
	      else
		{
		   return NULL;
		}
	  }
     }

   /* set end-of-string \0 */
   switch (state)
     {
      case 0:
	 language[tmp_idx] = 0;
	 tmp_idx = 0;
      case 1:
	 territory[tmp_idx] = 0;
	 tmp_idx = 0;
      case 2:
	 codeset[tmp_idx] = 0;
	 tmp_idx = 0;
      case 3:
	 modifier[tmp_idx] = 0;
     }
   
   locale_parts = E_NEW(E_Locale_Parts, 1);
   
   /* Put the parts of the string together */
   if (language[0] != 0)
     {
	locale_parts->mask |= E_INTL_LOC_LANG;
	locale_parts->lang = evas_stringshare_add(language);
     }
   if (territory[0] != 0)
     {
	locale_parts->mask |= E_INTL_LOC_REGION;
	locale_parts->region = evas_stringshare_add(territory);
     }	
   if (codeset[0] != 0)	  
     {
	locale_parts->mask |= E_INTL_LOC_CODESET;
	locale_parts->codeset = evas_stringshare_add(codeset);
     }	
   if (modifier[0] != 0)     
     {	       
	locale_parts->mask |= E_INTL_LOC_MODIFIER;
	locale_parts->modifier = evas_stringshare_add(modifier);
     }
   
   return locale_parts;
}

EAPI void
e_intl_locale_parts_free(E_Locale_Parts *locale_parts)
{
   if (locale_parts != NULL)
     {
	if (locale_parts->lang) evas_stringshare_del(locale_parts->lang);
	if (locale_parts->region) evas_stringshare_del(locale_parts->region);
	if (locale_parts->codeset) evas_stringshare_del(locale_parts->codeset);
	if (locale_parts->modifier) evas_stringshare_del(locale_parts->modifier);
	E_FREE(locale_parts);
     }
}

EAPI char *
e_intl_locale_parts_combine(E_Locale_Parts *locale_parts, int mask)
{
   int locale_size;
   char *locale;

   if (!locale_parts) return NULL;
   
   if ((mask & locale_parts->mask) != mask) return NULL;
 
   /* Construct the clean locale string */
   locale_size = 0;
   
   /* determine the size */
   if (mask & E_INTL_LOC_LANG)
     locale_size =  strlen(locale_parts->lang) + 1;
   
   if (mask & E_INTL_LOC_REGION)
     locale_size += strlen(locale_parts->region) + 1;
   
   if (mask & E_INTL_LOC_CODESET)
     locale_size += strlen(locale_parts->codeset) + 1;
    
   if (mask & E_INTL_LOC_MODIFIER)
     locale_size += strlen(locale_parts->modifier) + 1;
   
   /* Allocate memory */
   locale = (char *) malloc(locale_size);
   locale[0] = 0;
   
   if (mask & E_INTL_LOC_LANG)
     {
	strcat(locale, locale_parts->lang);
     }
  
   if (mask & E_INTL_LOC_REGION)
     {
	if (locale[0] != 0) strcat(locale, "_");
	strcat(locale, locale_parts->region);
      }
   
   if (mask & E_INTL_LOC_CODESET)
      {
	 if (locale[0] != 0) strcat(locale, ".");
	 strcat(locale, locale_parts->codeset);
      }
   
   if (mask & E_INTL_LOC_MODIFIER)
     {
	if (locale[0] != 0) strcat(locale, "@");
	strcat(locale, locale_parts->modifier);	          
     }

   return locale;
}

EAPI char *
e_intl_locale_charset_canonic_get(const char *charset)
{
   char charset_canonic[32];
   char c;
   int i, i_tmp;
   
   i = 0;
   i_tmp = 0;
   while ((c = charset[i++]) != 0) 
     {
	if (isalnum(c))
	  {
	     charset_canonic[i_tmp++] = tolower(c);
	  }
     }
   charset_canonic[i_tmp] = 0;

   if (!strcmp(charset, charset_canonic))
     {
	return NULL;
     }
   
   return strdup(charset_canonic);
}

static Evas_List *
_e_intl_locale_system_locales_get(void)
{
   Evas_List	*locales;
   FILE		*output;

   locales = NULL;
   output = popen("locale -a", "r");
   if ( output ) 
     {
	char line[32];
	while ( fscanf(output, "%[^\n]\n", line) == 1)
	  {
	     locales = evas_list_append(locales, strdup(line));
	  }
	          
	pclose(output);
     }
   return locales;
}

/*
 * must be an un aliased locale;
 */
static int
_e_intl_locale_validate(const char *locale)
{
   Evas_List *all_locales;
   E_Locale_Parts *locale_parts; 
   char *locale_lr;
   char *locale_cs_canonic;
   int   found;
   
   found = 0;

   locale_parts = e_intl_locale_parts_get(locale);
   
   /* Gather the search information */
   locale_lr = e_intl_locale_parts_combine(locale_parts, E_INTL_LOC_LANG | E_INTL_LOC_REGION);
  
   if ( locale_lr == NULL )
     {
	/* Not valid locale, maybe its an alias */
	locale_lr = strdup(locale); 
	locale_cs_canonic = NULL;
     }
   else
     {
	if ( locale_parts && locale_parts->codeset ) 
	  locale_cs_canonic = e_intl_locale_charset_canonic_get(locale_parts->codeset);
	else
	  locale_cs_canonic = NULL;
     }
  
   /* Get list of all available locales and aliases */ 
   all_locales = _e_intl_locale_system_locales_get();

   /* Match locale with one from the list */
   while(all_locales)
     {
	char *locale_next;
	locale_next = all_locales->data;

	if (found == 0)
	  {
	     E_Locale_Parts *locale_parts_next;
	     char * locale_lr_next; 
	     
	     locale_parts_next = e_intl_locale_parts_get(locale_next);
	     locale_lr_next = e_intl_locale_parts_combine(locale_parts_next, 
		   E_INTL_LOC_LANG | E_INTL_LOC_REGION);
	     
	     if (locale_parts && locale_lr_next && !strcmp(locale_lr, locale_lr_next)) 
	       {
		  /* Matched lang/region part, now if CS matches */
		  if ((locale_parts->codeset == NULL) && (locale_parts_next->codeset == NULL))
		    {
		       /* Lang/Region parts match and no charsets, 
			* we have a match 
			*/	    
		       found = 1;
		    }
		  else if (locale_parts->codeset && locale_parts_next->codeset)
		    {
		       if (!strcmp(locale_parts->codeset, locale_parts_next->codeset))
			 {
			    /* Lang/Region and charsets match */
			    found = 1;
			 }
		       else if (locale_cs_canonic) 
			 {
			    char *locale_cs_canonic_next;
			    /* try to match charsets in canonic form */
			    
			    locale_cs_canonic_next = 
			       e_intl_locale_charset_canonic_get(locale_parts_next->codeset);
			    
			    if (locale_cs_canonic_next)
			      {
				 if (!strcmp(locale_cs_canonic, locale_cs_canonic_next))
				   {
				      /* Lang/Resion and charsets in canonic 
				       * form match 
				       */
				      found = 1;
				   }
				 free(locale_cs_canonic_next);
			      }
			    else
			      {
				 if (!strcmp(locale_cs_canonic, locale_parts_next->codeset))
				   {
				      /* Lang/Resion and charsets in canonic 
				       * form match 
				       */
				      found = 1;
				   }
			      }
			 }
		    }
	       }
	     else
	       {
		  /* Its an alias */
		  if (!strcmp(locale_lr, locale_next)) found = 1;
	       }
	     e_intl_locale_parts_free(locale_parts_next);
	     E_FREE(locale_lr_next);
	  }
	
	all_locales = evas_list_remove_list(all_locales, all_locales);
	free(locale_next);
     }
   e_intl_locale_parts_free(locale_parts);
   free(locale_lr);
   E_FREE(locale_cs_canonic);
   return found; 
}

/* 
 *  arg local must be an already validated and unaliased locale
 *  returns the locale search order e.g.
 *  en_US.UTF-8 ->
 *   Mask (b) Locale (en_US.UTF-8)
 *   Mask (a) Locale (en_US)
 *   Mask (9) Locale (en.UTF-8)
 *   Mask (8) Locale (en)
 */
static Evas_List *
_e_intl_locale_search_order_get(const char *locale)
{
   Evas_List *search_list;
   E_Locale_Parts *locale_parts;
   char *masked_locale;
   int mask;
   
   locale_parts = e_intl_locale_parts_get(locale);
   if (locale_parts == NULL) return NULL;
   
   search_list = NULL;
   for ( mask = E_INTL_LOC_ALL; mask >= E_INTL_LOC_LANG; mask-- )
     {
	if ((mask & locale_parts->mask) == mask)
	  {
	     /* Only append if the mask we need is available */
	     masked_locale = e_intl_locale_parts_combine(locale_parts, mask);
	     search_list = evas_list_append(search_list, masked_locale);
	  }
     } 
   e_intl_locale_parts_free(locale_parts);
   return search_list;
}
   
static Evas_List *
_e_intl_imc_dir_scan(const char *dir)
{
   Evas_List *imcs;
   Ecore_List *files;
   char *file;
   
   imcs = NULL;
   
   files = ecore_file_ls(dir);
   if (!files) return NULL;
  
   ecore_list_goto_first(files);
   if (files)
     {
	while ((file = ecore_list_next(files)))
	  {
	     if (strstr(file, ".imc") != NULL)
	       {
		  char buf[PATH_MAX]; 
	     
		  snprintf(buf, sizeof(buf), "%s/%s", dir, file);	
		  imcs = evas_list_append(imcs, strdup(buf));
	       }
	  }
	ecore_list_destroy(files);
     }
   return imcs;
}

