/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework 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 as in version 2 of the License.

 * 
 * The iWear Framework 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
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWEAR_I18N_H
#define __IWEAR_I18N_H 

#ifndef __IWEAR_UTILITY_H
#include <iwear/utility.h>
#endif

#ifndef __IWEAR_IWMUTEX_H
#include <iwear/iwmutex.h>
#endif

#ifndef __IWEAR_EXCEPTIONS_H
#include <iwear/exceptions.h>
#endif

#include <map>
#include <iosfwd>

namespace iwear
{

enum iwear_language
{
    iwear_locale, 	///< This is not a real language, but if passed, the
    			///< language is tried to be gathered from the environment
    iwear_german,
    iwear_english,
    iwear_chinese,
    iwear_russian,
    iwear_french,
    num_iwear_language ///< In certain environments means, take language from locale
};

/** 
 * This macro actually does nothing but emitting the string literal, code wise.
 * But the parsers that extract translation strings recognize it, so that in
 * cases where you cannot, or dont want to use i18n::trans, you can
 * nevertheless let this string be embeddded in the translation files. 
 */ 
#define I18NTRANS(x) x

class i18nLoader;
/**
 * @todo Think about if we maybe want to make this a namespace instead of a
 * class with all static functions.
 */
class i18n    
{
private:
    /**
     * Static instance to initialize whats needed... This instance does nothing
     * and so do others, so we simply forbid instances.
     */
    static i18n initializer;
    struct lang_code_pair { iwear_language lang; const char * code; }; 
    static lang_code_pair locale_code_table[];

    static const char * setlocale_ret;

    i18n( );
    ~i18n( );
protected:

//    static debugstream* lstr;
    static Mutex mutex;
    static std::map<iwear_language, const char *>* locale_code;
    static std::map<const char *, iwear_language,cstring_less>* locale_strings;
    static std::multimap<const char *,string, cstring_less > langmap;

    /**
     * This std::mapping holds all the loaders we know. If a loader is for all
     * languages, it is kept here as loader of the language num_iwear_language
     */
    static std::multimap<iwear_language,i18nLoader*> loaders;
    /**
     * The default language, is the compiled in language is english. Never ever
     * need to change this !
     */
    static const iwear_language default_language;
    static iwear_language actual_language;

    static std::ofstream* unf;
public:

    /**
     * Sometimes it happens that messages are passed around and only be
     * translated after they have been passed. To catch those cases, it is
     * possible to save those translations in a file. Use with care, there is
     * no management of duplicates etc. just the strings will be put into the
     * file.
     */
    static void set_unknown_file( const std::string& uf );

    static inline const char * get_locale_code( iwear_language ln )
    {
	std::map<iwear_language, const char *>::iterator lit = locale_code->find(ln);
	if ( lit != locale_code->end() )
	    return lit->second;
	else 
	    THROW( iwruntime_error,("Could not find requested language code"));
    }

    /**
     * Gets the iwear_language type for a specified language code.
     * i.e. If you passe "de_DE" to the method it will return
     * iwear_german.
     * @param language_code The language code of which you search the
     * iwear_language.
     * @return The iwear language that matches the language code.
     * @throws iwruntime_error If the language is not found.
     */
    static inline iwear_language get_iwear_language( const string& language_code )
    {
	if( language_code == "locale" ) return iwear_locale;

	std::map<iwear_language, const char *>::iterator lit = locale_code->begin();
	while ( lit != locale_code->end() ){
	    if (lit->second == language_code ){
		return lit->first;
	    }
	    ++lit;
	}
	THROW( iwruntime_error,("Could not find requested iwear language for specified code."));
    }
    /**
     * This accesses the the real std::map only if the pointer wasnt found in the
     * cache. If it then was found in the real std::mapping, then it will be put in
     * the cache, assuming that the pointer is a compile-time constant pointer.
     * If it wasnt, then if it is looked up again, its contents will not be
     * checked again.
     * @param the string to be translated
     * @param card the number that should be used with a sentence. If 0, the
     * first match should be used. If > 0 then the entries are searched
     * sequentially and the one that best matches will be choosen.
     */
    static const char * trans( const char *, uint32_t card = 0);

    /**
     * This always bypasses the cache, since it assumes a runtime created
     * string. If the translation was not found, then this returns a pointer to
     * the .c_str() of the passed string !!
     */
    static inline const char * trans( const string& txt, uint32_t card = 0 ) { return trans(txt.c_str(),card); }

    /**
     * This adds a loader for a different part of the application, e.g. some plugin etc.
     * @note We dont support the GNU gettext style concept of domains for
     * different applications that must be set before getting some text. We
     * think that every sentence must have the same translation, thuss setting
     * the domain does not make difference to adding all translations to a
     * single container.
     */
    static void add_loader( i18nLoader& , iwear_language lng = num_iwear_language);

    static void remove_loader( i18nLoader&, iwear_language lng = num_iwear_language );

    /**
     * Sets the actual language. This will trigger a load of the corresponding
     * data for the seperate loaders. If the passed language is
     * num_iwear_language (i.e. default value) then it re-loads the current
     * language data from all the loaders it knows.
     */
    static void set_language( iwear_language lng = num_iwear_language );
    static iwear_language get_language( void ) { return actual_language; }
};

class i18nLoader
{
private:
protected:
    iwear_language active_language;
public:
    i18nLoader( ) : active_language( i18n::get_language() ) { }
    virtual ~i18nLoader() { }
    /**
     * Shall return a reference to some language std::mapping....
     */
    virtual std::multimap<const char *, string, cstring_less >& get_langmap( iwear_language ) = 0;

    /**
     * Shall release information occupied by previously get_langmap calls. If
     * called, no reference previously returned by it can be used any more.
     */
    virtual void release( void ) = 0;
};

}

#endif
