/*
**	termLocale.c
**
**	Localization support routines
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* The catalog data is stored in the following format. */

struct CatCompArrayType
{
	LONG	cca_ID;
	STRPTR	cca_Str;
};

extern struct CatCompArrayType	*AppStrings;
extern LONG			 NumAppStrings;

	/* LocaleOpen(STRPTR CatalogName,STRPTR BuiltIn):
	 *
	 *	Open string translation tables.
	 */

VOID __regargs
LocaleOpen(STRPTR CatalogName,STRPTR BuiltIn,LONG Version)
{
	if(LocaleBase = (struct LocaleBase *)OpenLibrary("locale.library",38))
	{
		if(LocaleBase -> lb_SysPatches)
		{
			strcpy(ConvNumber,"%lD");
			strcpy(ConvNumber10,"%10lD");

			if(Catalog = OpenCatalog(NULL,CatalogName,
				OC_BuiltInLanguage,	BuiltIn,
				OC_BuiltInCodeSet,	0,
			TAG_DONE))
			{
				BOOL TooOld = FALSE;

					// Don't load an outdated catalog file

				if(Catalog -> cat_Version < Version)
					TooOld = TRUE;
				else
				{
					if(strcmp(GetCatalogStr(Catalog,MSG_OFFSET_TEST1_TXT,""),"v4.0"))
						TooOld = TRUE;
				}

				if(TooOld)
				{
					if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0))
					{
						MyEasyRequest(NULL,"The catalog file is too old to be used\nwith this `term' revision.","Continue");

						CloseLibrary(IntuitionBase);

						IntuitionBase = NULL;
					}

					CloseCatalog(Catalog);

					Catalog = NULL;
				}
			}

			Locale = OpenLocale(NULL);
		}
		else
		{
			if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0))
			{
				MyEasyRequest(NULL,"Your \"locale.library\" is not installed correctly.","Continue");

				CloseLibrary(IntuitionBase);

				IntuitionBase = NULL;
			}

			strcpy(ConvNumber,"%ld");
			strcpy(ConvNumber10,"%10ld");

			CloseLibrary(LocaleBase);

			LocaleBase = NULL;
		}
	}
	else
	{
		strcpy(ConvNumber,"%ld");
		strcpy(ConvNumber10,"%10ld");
	}
}

	/* LocaleClose():
	 *
	 *	Close the translation tables.
	 */

VOID
LocaleClose()
{
	if(Locale)
	{
		CloseLocale(Locale);

		Locale = NULL;
	}

	if(Catalog)
	{
		CloseCatalog(Catalog);

		Catalog = NULL;
	}

	if(LocaleBase)
	{
		CloseLibrary(LocaleBase);

		LocaleBase = NULL;
	}
}

	/* LanguageCheck():
	 *
	 *	Checks to see if the currently selected language
	 *	is english.
	 */

VOID
LanguageCheck()
{
	if(Locale && Catalog)
	{
		if(Locale -> loc_LanguageName)
		{
			if(!Stricmp(Locale -> loc_LanguageName,"english.language"))
				English = TRUE;
			else
				English = FALSE;
		}
		else
			English = FALSE;
	}
	else
		English = TRUE;
}

	/* GadToolsStrlen(STRPTR *String):
	 *
	 *	Custom version of strlen, useful for gadtools object titles
	 *	with embedded underscore characters.
	 */

WORD __regargs
GadToolsStrlen(STRPTR String)
{
	WORD Len = 0;

	while(*String)
	{
		if(*String++ != '_')
			Len++;
	}

	return(Len);
}

	/* GadToolsLongestString(WORD From,WORD To):
	 *
	 *	Determines the longest string used by a gadtools
	 *	control panel.
	 */

WORD __regargs
GadToolsLongestString(WORD From,WORD To)
{
	STRPTR	Text;
	WORD	MaxLen = 0,
		Len,i;

	for(i = From ; i <= To ; i++)
	{
		if(Text = LocaleString(i))
		{
			if((Len = GadToolsStrlen(Text)) > MaxLen)
				MaxLen = Len;
		}
	}

	return(MaxLen);
}

	/* LocaleSeconds(WORD Seconds):
	 *
	 *	Return seconds in proper format.
	 */

STRPTR __regargs
LocaleSeconds(WORD Seconds)
{
	STATIC UBYTE Time[10];

	if(Locale)
		SPrintf(Time,"%2lD%s%02lD",Seconds / 100,Locale -> loc_DecimalPoint,Seconds % 100);
	else
		SPrintf(Time,"%2ld.%02ld",Seconds / 100,Seconds % 100);

	return(Time);
}

	/* SmallCurrency():
	 *
	 *	Support function for the rates control panel, returns a formatted
	 *	string to contain a string like "cents/unit".
	 */

STRPTR
SmallCurrency()
{
	STATIC UBYTE Buffer[30];

	if(Locale)
		SPrintf(Buffer,LocaleString(MSG_RATEPANEL_PAY_PER_UNIT_GAD),Locale -> loc_MonSmallCS);
	else
		SPrintf(Buffer,LocaleString(MSG_RATEPANEL_PAY_PER_UNIT_GAD),"Pay");

	return(Buffer);
}

	/* InsertGrouping(STRPTR Buffer,STRPTR GroupData,STRPTR GroupSeparator):
	 *
	 *	Tricky stuff, folks! This beauty will insert grouping characters
	 *	into a readily-prepared string buffer filled with numeric
	 *	contents. It takes the group separator tokens and group separator
	 *	strings into account.
	 */

VOID __regargs
InsertGrouping(STRPTR Buffer,STRPTR GroupData,STRPTR GroupSeparator)
{
	UBYTE	LocalBuffer[80];	// Sufficient, but too large
	STRPTR	Index;
	LONG	i,j,SeparatorSize;
	WORD	Count;			// How many characters per group
	BOOL	RepeatGroupCount;	// Keep repeating group size until end

		// Set up for the first group

	switch(*GroupData)
	{
		case 0:			// Repeat current grouping scheme until end
		case 255:		// No further grouping is to be performed

			Count = 0;

			break;

		default:		// Initial group size

			RepeatGroupCount = FALSE;

			Count = *GroupData++;

			break;
	}

		// Check the size of the group separator string

	if((SeparatorSize = strlen(GroupSeparator) - 1) < 1)
		Count = 0;

		// That where we'll start

	Index = LocalBuffer;

		// Build the string back to front, we will reverse it later

	for(i = strlen(Buffer) - 1, j = 1 ; i >= 0 ; i--, j++)
	{
			// Pick up the next number character

		*Index++ = Buffer[i];

			// Are we to insert the grouping characters here?

		if(Count && j == Count)
		{
			LONG k;

				// Insert the grouping characters

			for(k = SeparatorSize ; k >= 0 ; k--)
				*Index++ = GroupSeparator[k];

				// Reset the group size counter

			j = 0;

				// Pick up the next grouping token?

			if(!RepeatGroupCount)
			{
					// Ok, what kind of token is it?

				switch(*GroupData)
				{
					case 0:		// Repeat current grouping scheme

						RepeatGroupCount = TRUE;
						break;

					case 255:	// Perform no further grouping

						Count = 0;
						break;

					default:	// New group size

						Count = *GroupData++;
						break;
				}
			}
		}
	}

		// Look how long the resulting string is

	j = (LONG)((ULONG)Index - (ULONG)LocalBuffer) - 1;

		// Copy it back

	Index = Buffer;

		// Reverse the order of characters while copying

	for(i = j ; i >= 0 ; i--)
		*Index++ = LocalBuffer[i];

		// Provide null-termination

	*Index = 0;
}

VOID __regargs
ConvertMonetaryQuantity(LONG Units,STRPTR Destination,BOOL UseCurrency)
{
	UBYTE	IntegerBuffer[80];	// Sufficient, but too large

	STRPTR	SignText,		// Signed/unsigned quantity text
		SpaceText,		// Currency/number separation
		Currency;		// The name of the currency
	UBYTE	SpaceSep,		// A space separates currency and quantity?
		SignPos,		// Where to place the sign text
		CSPos;			// Where to place the currency text
	BYTE	Sign;			// Negative or positive quantity?

		// Negative quantity?

	if(Units < 0)
	{
		Sign = -1;

		Units = -Units;
	}
	else
		Sign = 1;

		// Does this currency sport a fractional smaller currency?

	if(Locale -> loc_MonFracDigits)
	{
		UBYTE	NumberBuffer[5],
			FractionBuffer[40];
		LONG	Integer,
			Fraction,
			Scale;
		WORD	i;

			// Prepare the formatting string

		SPrintf(NumberBuffer,"%%0%ldld",Locale -> loc_MonFracDigits);

			// Turn the number of fractional digits into a power of ten

		for(i = 0, Scale = 1 ; i < Locale -> loc_MonFracDigits ; i++)
			Scale *= 10;

			// Split the quantity in integer and fractional part

		Integer		= Units / Scale;
		Fraction	= Units % Scale;

			// Build the integer text

		SPrintf(IntegerBuffer,"%ld",Integer);

		InsertGrouping(IntegerBuffer,Locale -> loc_MonGrouping,Locale -> loc_MonGroupSeparator);

			// Build the fractional text

		SPrintf(FractionBuffer,NumberBuffer,Fraction);

		InsertGrouping(FractionBuffer,Locale -> loc_MonFracGrouping,Locale -> loc_MonFracGroupSeparator);

			// Add the monetary decimal point

		strcat(IntegerBuffer,Locale -> loc_MonDecimalPoint);

			// Add the fractional part

		strcat(IntegerBuffer,FractionBuffer);
	}
	else
	{
			// Build the integer text

		SPrintf(IntegerBuffer,"%ld",Units);

		InsertGrouping(IntegerBuffer,Locale -> loc_MonGrouping,Locale -> loc_MonGroupSeparator);
	}

		// Pick up the appropriate formatting parameters

	if(Sign < 0)
	{
		SignText	= Locale -> loc_MonNegativeSign;
		SpaceSep	= Locale -> loc_MonNegativeSpaceSep;
		SignPos		= Locale -> loc_MonNegativeSignPos;
		CSPos		= Locale -> loc_MonNegativeCSPos;
	}
	else
	{
		SignText	= Locale -> loc_MonPositiveSign;
		SpaceSep	= Locale -> loc_MonPositiveSpaceSep;
		SignPos		= Locale -> loc_MonPositiveSignPos;
		CSPos		= Locale -> loc_MonPositiveCSPos;
	}

		// Are we to use the currency symbol?

	if(UseCurrency)
	{
			// Pick up the currency text

		Currency = Locale -> loc_MonCS;

			// Take care of the separation information

		if(SpaceSep == SS_NOSPACE)
			SpaceText = "";
		else
			SpaceText = " ";
	}
	else
		Currency = SpaceText = "";

		// Now merge all the information into one single string

	if(CSPos == CSP_PRECEDES)
	{
		switch(SignPos)
		{
			case SP_PARENS:

				// Currency (Space) Sign Value
				SPrintf(Destination,"(%s%s%s%s)",Currency,SpaceText,SignText,IntegerBuffer);
				break;

			case SP_PREC_ALL:

				// Sign Currency (Space) Value
				SPrintf(Destination,"%s%s%s%s",SignText,Currency,SpaceText,IntegerBuffer);
				break;

			case SP_SUCC_ALL:

				// Currency (Space) Value Sign
				SPrintf(Destination,"%s%s%s%s",Currency,SpaceText,IntegerBuffer,SignText);
				break;

			case SP_PREC_CURR:

				// Sign Currency (Space) Value
				SPrintf(Destination,"%s%s%s%s",SignText,Currency,SpaceText,IntegerBuffer);
				break;

			case SP_SUCC_CURR:

				// Currency Sign (Space) Value
				SPrintf(Destination,"%s%s%s%s",Currency,SignText,SpaceText,IntegerBuffer);
				break;
		}
	}
	else
	{
		switch(SignPos)
		{
			case SP_PARENS:

				// Sign Value (Space) Currency
				SPrintf(Destination,"(%s%s%s%s)",SignText,IntegerBuffer,SpaceText,Currency);
				break;

			case SP_PREC_ALL:

				// Sign Value (Space) Currency
				SPrintf(Destination,"%s%s%s%s",SignText,IntegerBuffer,SpaceText,Currency);
				break;

			case SP_SUCC_ALL:

				// Value (Space) Currency Sign
				SPrintf(Destination,"%s%s%s%s",IntegerBuffer,SpaceText,Currency,SignText);
				break;

			case SP_PREC_CURR:

				// Value (Space) Sign Currency
				SPrintf(Destination,"%s%s%s%s",IntegerBuffer,SpaceText,SignText,Currency);
				break;

			case SP_SUCC_CURR:

				// Value (Space) Currency Sign
				SPrintf(Destination,"%s%s%s%s",IntegerBuffer,SpaceText,Currency,SignText);
				break;
		}
	}
}

	/* CreateSum(LONG Quantity):
	 *
	 *	Create a string containing a monetary quantity formatted
	 *	according to the current locale rules.
	 */

STRPTR __regargs
CreateSum(LONG Quantity,BYTE UseCurrency)
{
	STATIC UBYTE Buffer[100];

	if(Locale)
		ConvertMonetaryQuantity(Quantity,Buffer,UseCurrency);
	else
		SPrintf(Buffer,"%ld.%02ld",Quantity / 100,Quantity % 100);

	return(Buffer);
}

	/* LocalizeString(STRPTR *Strings,WORD From,WORD To):
	 *
	 *	Localize an array of strings.
	 */

VOID __regargs
LocalizeString(STRPTR *Strings,WORD From,WORD To)
{
	WORD i,j;

	for(i = From, j = 0 ; i <= To ; i++)
	{
		if(!Strings[j])
			Strings[j++] = LocaleString(i);
	}
}

	/* LocalizeMenu(struct NewMenu *Menu,WORD From):
	 *
	 *	Localize a NewMenu definition.
	 */

VOID __regargs
LocalizeMenu(struct NewMenu *Menu,WORD From)
{
	STRPTR Label,Shortcut;

        while(Menu -> nm_Type != NM_END)
	{
		Shortcut = LocaleString(From);

		if(Shortcut[0] && !Shortcut[1])
			Label = Shortcut + 2;
		else
		{
			Label		= Shortcut;
			Shortcut	= NULL;
		}

		switch(Menu -> nm_Type)
		{
			case NM_TITLE:

				Menu -> nm_Label = Label;

				From++;

				break;

			case NM_ITEM:
			case NM_SUB:

				if(Menu -> nm_Label != NM_BARLABEL)
				{
					Menu -> nm_Label	= Label;
					Menu -> nm_CommKey	= Shortcut;

					From++;
				}

				break;
		}

		Menu++;
	}
}

	/* LocaleString(LONG ID):
	 *
	 *	Obtain a string from the translation pool.
	 */

STRPTR __regargs
LocaleString(LONG ID)
{
	STRPTR Builtin;

	if(ID < NumAppStrings && AppStrings[ID] . cca_ID == ID)
		Builtin = AppStrings[ID] . cca_Str;
	else
	{
		LONG i;

		Builtin = "";

		for(i = 0 ; i < NumAppStrings ; i++)
		{
			if(AppStrings[i] . cca_ID == ID)
			{
				Builtin = AppStrings[i] . cca_Str;

				break;
			}
		}
	}

	if(Catalog)
	{
		STRPTR String = GetCatalogStr(Catalog,ID,Builtin);

		if(String[0])
			return(String);
		else
			return(Builtin);
	}
	else
		return(Builtin);
}

STRPTR __saveds __stdargs
LocaleHookFunc(struct Hook *Hook,struct LayoutHandle *Handle,LONG ID)
{
	return(LocaleString(ID));
}

STATIC LONG __saveds __asm
FormatDateHookFunc(register __a0 struct Hook *Hook,register __a1 UBYTE Char)
{
	STRPTR String = Hook -> h_Data;

	*String++ = Char;

	Hook -> h_Data = String;

	return(TRUE);
}

	/* FormatStamp():
	 *
	 *	Convert a date stamp into human readable
	 *	form by taking the current locale parameters
	 *	into account.
	 */

BOOLEAN __regargs
FormatStamp(struct DateStamp *Stamp,STRPTR DateBuffer,STRPTR TimeBuffer,STRPTR BothBuffer,BOOLEAN SubstituteDay)
{
	struct DateStamp Now;

		// If no time stamp given, do with current time

	if(!Stamp)
		DateStamp(Stamp = &Now);

		// Is the current locale available?

	if(Locale)
	{
		struct Hook LocalHook = {{NULL}, (HOOKFUNC)FormatDateHookFunc};

			// Combine date and time text?

		if(BothBuffer && !SubstituteDay)
		{
			LocalHook . h_Data = BothBuffer;

			FormatDate(Locale,Locale -> loc_DateTimeFormat,Stamp,&LocalHook);

			StripSpaces(BothBuffer);
		}
		else
		{
			UBYTE	LocalDateBuffer[40],
				LocalTimeBuffer[40];

				// Provide storage space

			if(BothBuffer)
			{
				DateBuffer = LocalDateBuffer;
				TimeBuffer = LocalTimeBuffer;
			}

				// Do we have a date buffer to fill?

			if(DateBuffer)
			{
					// Are we to substitute the current day with
					// text such as today, yesterday, etc.?

				if(SubstituteDay)
				{
					struct DateStamp Today;

						// Get the current time

					DateStamp(&Today);

						// Does the date refer to yesterday?

					if(Stamp -> ds_Days == Today . ds_Days - 1)
						strcpy(DateBuffer,GetLocaleStr(Locale,YESTERDAYSTR));
					else
					{
							// Does the date refer to today?

						if(Stamp -> ds_Days == Today . ds_Days)
							strcpy(DateBuffer,GetLocaleStr(Locale,TODAYSTR));
						else
						{
								// Does the date refer to tomorrow?

							if(Stamp -> ds_Days == Today . ds_Days + 1)
								strcpy(DateBuffer,GetLocaleStr(Locale,TOMORROWSTR));
							else
								SubstituteDay = NULL;
						}
					}
				}

				if(!SubstituteDay)
				{
					LocalHook . h_Data = DateBuffer;

					FormatDate(Locale,Locale -> loc_DateFormat,Stamp,&LocalHook);
				}

				StripSpaces(DateBuffer);
			}

			if(TimeBuffer)
			{
				LocalHook . h_Data = TimeBuffer;

				FormatDate(Locale,Locale -> loc_TimeFormat,Stamp,&LocalHook);

				StripSpaces(TimeBuffer);
			}

				// Combine date and time

			if(BothBuffer)
			{
				strcpy(BothBuffer,DateBuffer);
				strcat(BothBuffer," ");
				strcat(BothBuffer,TimeBuffer);
			}
		}
	}
	else
	{
		struct DateTime	DateTime;
		UBYTE		LocalDateBuffer[40],
				LocalTimeBuffer[40];

			// Provide storage space

		if(BothBuffer)
		{
			DateBuffer = LocalDateBuffer;
			TimeBuffer = LocalTimeBuffer;
		}

			// No locale, so we will use dos.library instead.

		CopyMem(Stamp,&DateTime . dat_Stamp,sizeof(struct DateStamp));

		DateTime . dat_Format	= FORMAT_DOS;
		DateTime . dat_Flags	= SubstituteDay ? DTF_SUBST : NULL;
		DateTime . dat_StrDay	= NULL;
		DateTime . dat_StrDate	= DateBuffer;
		DateTime . dat_StrTime	= TimeBuffer;

		if(!DateToStr(&DateTime))
			return(FALSE);

		if(DateBuffer)
			StripSpaces(DateBuffer);

		if(TimeBuffer)
			StripSpaces(TimeBuffer);

			// Combine date and time

		if(BothBuffer)
		{
			strcpy(BothBuffer,DateBuffer);
			strcat(BothBuffer," ");
			strcat(BothBuffer,TimeBuffer);
		}
	}

	return(TRUE);
}

	/* FormatTime(STRPTR Buffer,LONG Hours,LONG Minutes,LONG Seconds):
	 *
	 *	Given hours, minutes and seconds, format this data into
	 *	a human-readable string.
	 */

VOID __regargs
FormatTime(STRPTR Buffer,LONG Hours,LONG Minutes,LONG Seconds)
{
	if(Locale)
	{
		struct Hook LocalHook = {{NULL}, (HOOKFUNC)FormatDateHookFunc};

		struct DateStamp Stamp;

		Stamp . ds_Days		= 0;
		Stamp . ds_Minute	= Hours * 60 + Minutes;
		Stamp . ds_Tick		= MAX(0,Seconds) * TICKS_PER_SECOND;

		LocalHook . h_Data = Buffer;

		if(Seconds < 0)
			FormatDate(Locale,Locale -> loc_ShortTimeFormat,&Stamp,&LocalHook);
		else
			FormatDate(Locale,Locale -> loc_TimeFormat,&Stamp,&LocalHook);
	}
	else
	{
		if(Seconds < 0)
			SPrintf(Buffer,"%02ld:%02ld",Hours,Minutes);
		else
			SPrintf(Buffer,"%02ld:%02ld:%02ld",Hours,Minutes,Seconds);
	}
}
