//	Zinc Application Framework - Z_LOC.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_loc.hpp>
#include <z_lang.hpp>
#include <z_string.hpp>
#include <time.h>
#define ZAF_LOCALE_DATA_INFO
#include "loc_def.cpp"
#include "gbl_def.cpp"

// Locale definitions for the USA (US).
// Changes to these tables must be reflected in intl/loc_us.cpp.
static ZafIChar defaultCanonicalLocaleName[] = ZAF_ITEXT("canonical");

static ZafEraStruct ZAF_FARDATA defaultCanonicalEraTable[] =
{
	{ 1, 1, 1721424L, 0xffffffffL, ZAF_ITEXT("AD"), ZAF_ITEXT("%EC %Ey") },
	{ 0, 4713, 0L, 1721423L, ZAF_ITEXT("BC"), ZAF_ITEXT("%Ey %EC") }
};

static ZafLocaleStruct ZAF_FARDATA defaultCanonicalLocale =
{
	ZAF_ITEXT("."),		// decimalSeparator
	ZAF_ITEXT("."),		// monDecimalSeparator
	ZAF_ITEXT(","),		// thousandsSeparator
	ZAF_ITEXT(","),		// monThousandsSeparator
	ZAF_ITEXT("$"),		// currencySymbol
	{ 3, 0 },			// grouping
	{ 3, 0 },			// monGrouping
	ZAF_ITEXT("USD "),	// intCurrencySymbol
	1,					// posCurrencyPrecedes
	1,					// negCurrencyPrecedes
	2,					// fractionDigits
	2,					// intFractionDigits
	ZAF_ITEXT("+"),		// positiveSign
	1,					// posSignPrecedes
	0,					// posSpaceSeparation
	ZAF_ITEXT("-"),		// negativeSign
	1,					// negSignPrecedes
	0,					// negSpaceSeparation
	ZAF_ITEXT("("),
	ZAF_ITEXT(")"),
	ZAF_ITEXT("%d"),
	ZAF_ITEXT("%d"),
	ZAF_ITEXT("%g"),
	ZAF_ITEXT("%g"),
	(ZAF_ITEXT("%I:%M:%S%p\n")
		ZAF_ITEXT("%I:%M:%S\n")
		ZAF_ITEXT("%H:%M:%S\n")
		ZAF_ITEXT("%I%M%S%p\n")
		ZAF_ITEXT("%I%M%S\n")
		ZAF_ITEXT("%H%M%S\n")
		ZAF_ITEXT("%I:%M%p\n")
		ZAF_ITEXT("%I:%M\n")
		ZAF_ITEXT("%H:%M\n")
		ZAF_ITEXT("%I%M%p\n")
		ZAF_ITEXT("%I%M\n")
		ZAF_ITEXT("%H%M\n")
		ZAF_ITEXT("%I%p\n")
		ZAF_ITEXT("%I%\n")
		ZAF_ITEXT("%H%")),
	ZAF_ITEXT("%H:%M:%S"),
	ZAF_ITEXT("%I:%M:%S %p"),
	(ZAF_ITEXT("%m/%d/%y\n")
		ZAF_ITEXT("%d/%m/%y\n")
		ZAF_ITEXT("%m-%d-%y\n")
		ZAF_ITEXT("%d-%m-%y\n")
		ZAF_ITEXT("%m.%d.%y\n")
		ZAF_ITEXT("%d.%m.%y\n")
		ZAF_ITEXT("%m%d%y\n")
		ZAF_ITEXT("%d%m%y\n")
		ZAF_ITEXT("%h%d,%y\n")
		ZAF_ITEXT("%h%d%y\n")
		ZAF_ITEXT("%d%h,%y\n")
		ZAF_ITEXT("%d%h%y\n")
		ZAF_ITEXT("%m/%d/%Y\n")
		ZAF_ITEXT("%d/%m/%Y\n")
		ZAF_ITEXT("%m-%d-%Y\n")
		ZAF_ITEXT("%d-%m-%Y\n")
		ZAF_ITEXT("%m.%d.%Y\n")
		ZAF_ITEXT("%d.%m.%Y\n")
		ZAF_ITEXT("%m%d%Y\n")
		ZAF_ITEXT("%d%m%Y\n")
		ZAF_ITEXT("%h%d,%Y\n")
		ZAF_ITEXT("%h%d%Y\n")
		ZAF_ITEXT("%d%h,%Y\n")
		ZAF_ITEXT("%d%h%Y\n")
		ZAF_ITEXT("%m%d\n")
		ZAF_ITEXT("%d%m\n")
		ZAF_ITEXT("%h%d\n")
		ZAF_ITEXT("%d%h")),
	ZAF_ITEXT("%m/%d/%Y"),
	ZAF_ITEXT("%D%T"),
	ZAF_ITEXT("%m/%d/%Y %I:%M:%S %p"),
	ZAF_ITEXT("0123456789"),
	ZAF_ITEXT("0123456789"),
	ZAF_ITEXT(":"),
	ZAF_ITEXT("-"),
	2299160L,			// Oct 4, 1582
	11,					// to 2299171L	Oct 15, 1582
	LENGTHOF(defaultCanonicalEraTable),
	defaultCanonicalEraTable
};

// ----- ZafLocaleData ------------------------------------------------------

ZafLocaleData::ZafLocaleData(void) :
	ZafI18nData()
{
	// Initialize pointers.
	creditLeftParen = creditRightParen =
		integerStringInputFormat = integerStringOutputFormat =
		realStringInputFormat =	realStringOutputFormat =
		timeStringInputFormat =	timeStringOutputFormat =
		time12StringOutputFormat =
		dateStringInputFormat =	dateStringOutputFormat =
		dateTimeStringInputFormat =	dateTimeStringOutputFormat =
		defDigits = altDigits = ZAF_NULLP(ZafIChar);
	eraTableLength = 0;
	eraTable = ZAF_NULLP(ZafEraStruct);

	// Clear the class members.
	Clear();
}

ZafLocaleData::ZafLocaleData(const ZafLocaleStruct &tData) :
	ZafI18nData()
{
	// Initialize pointers.
	creditLeftParen = creditRightParen =
		integerStringInputFormat = integerStringOutputFormat =
		realStringInputFormat =	realStringOutputFormat =
		timeStringInputFormat =	timeStringOutputFormat =
		time12StringOutputFormat =
		dateStringInputFormat =	dateStringOutputFormat =
		dateTimeStringInputFormat =	dateTimeStringOutputFormat =
		defDigits = altDigits = ZAF_NULLP(ZafIChar);
	eraTableLength = 0;
	eraTable = ZAF_NULLP(ZafEraStruct);

	// Set the class members.
	SetLocale(tData);
}

ZafLocaleData::ZafLocaleData(const ZafLocaleData &copy) :
	ZafI18nData(copy)
{
	// Initialize pointers.
	creditLeftParen = creditRightParen =
		integerStringInputFormat = integerStringOutputFormat =
		realStringInputFormat =	realStringOutputFormat =
		timeStringInputFormat =	timeStringOutputFormat =
		time12StringOutputFormat =
		dateStringInputFormat =	dateStringOutputFormat =
		dateTimeStringInputFormat =	dateTimeStringOutputFormat =
		defDigits = altDigits = ZAF_NULLP(ZafIChar);
	eraTableLength = 0;
	eraTable = ZAF_NULLP(ZafEraStruct);

	// Copy the data contents.
	SetLocale(copy);
}

ZafLocaleData::~ZafLocaleData(void)
{
	// Clean up old entries.
	if (creditLeftParen)
		delete []creditLeftParen;
	if (creditRightParen)
		delete []creditRightParen;
	if (integerStringInputFormat)
		delete []integerStringInputFormat;
	if (integerStringOutputFormat)
		delete []integerStringOutputFormat;
	if (realStringInputFormat)
		delete []realStringInputFormat;
	if (realStringOutputFormat)
		delete []realStringOutputFormat;
	if (timeStringInputFormat)
		delete []timeStringInputFormat;
	if (timeStringOutputFormat)
		delete []timeStringOutputFormat;
	if (dateStringInputFormat)
		delete []dateStringInputFormat;
	if (dateStringOutputFormat)
		delete []dateStringOutputFormat;
	if (dateTimeStringInputFormat)
		delete []dateTimeStringInputFormat;
	if (dateTimeStringOutputFormat)
		delete []dateTimeStringOutputFormat;
	if (time12StringOutputFormat)
		delete []time12StringOutputFormat;
	if (defDigits)
		delete []defDigits;
	if (altDigits)
		delete []altDigits;
	if (eraTable)
		delete []eraTable;
}

ZafData *ZafLocaleData::Duplicate(void)
{
	return (new ZafLocaleData(*this));
}

bool ZafLocaleData::IsA(ZafClassID compareID) const
{
	return ((compareID == classID) ? true : ZafI18nData::IsA(compareID));
}

bool ZafLocaleData::IsA(ZafClassName compareName) const
{
	return (!strcmp(compareName, className) ? true : ZafI18nData::IsA(compareName));
}

void ZafLocaleData::LocaleAllocate(const ZafIChar *name)
{
	// Check for initialization.
	if (zafLocale)
		return;
	ZafI18nData::AddStaticModule(LocaleFree);

	// Try the zafDataManager.
	ZafError localError = ZAF_ERROR_NONE;
	if (!zafLocale && zafDataManager)
	{
		// Read the locale from the data manager.
		if (name)
			zafLocale = DynamicPtrCast(zafDataManager->AllocateData(name, ZafLocaleData::className, ZafLocaleData::classID), ZafLocaleData);

		// Make sure zafLocale's destructor isn't called multiple times.
		if (zafLocale)
		{
			// Register the canonical locale to prevent memory leaks.
			if (!canonicalLocale)
			{
				canonicalLocale = new ZafLocaleData(defaultCanonicalLocale);
				canonicalLocale->SetStringID(defaultCanonicalLocaleName);
				canonicalLocale->AddNotification(zafDataManager);
				zafDataManager->Add(canonicalLocale);
			}
		}
#if defined(USE_ZAF_PERSISTENCE)
		else if (zafDataPersistence)
		{
			localError = zafDataPersistence->Error();
			zafDataPersistence->SetError(ZAF_ERROR_NONE);
		}
#endif
	}

	// Construct from the system values.
	if (!zafLocale && name && *name)
	{
//		if (strcmp(name, defaultLocaleName))
		{
			zafLocale = new ZafLocaleData(defaultLocale);
			if (OSLocaleInformation(*zafLocale))
				zafLocale->SetStringID(defaultLocaleName);
			else
			{
				delete zafLocale;
				zafLocale = 0;
			}
		}
	}

	// Default to code initialization.
	if (!zafLocale)
	{
		zafLocale = new ZafLocaleData(defaultLocale);
		zafLocale->SetStringID(defaultLocaleName);
	}
	if (!canonicalLocale)
	{
		canonicalLocale = new ZafLocaleData(defaultCanonicalLocale);
		canonicalLocale->SetStringID(defaultCanonicalLocaleName);
	}

	// Set/clear the zafLocale error.
	zafLocale->SetError(localError);
}

void ZafLocaleData::LocaleFree(bool globalRequest)
{
	// Destroy the static locale.
	if (globalRequest && zafLocale && zafLocale->Destroyable() &&
		zafLocale->NotifyCount() == 0)
	{
		delete zafLocale;
		zafLocale = ZAF_NULLP(ZafLocaleData);
	}

	if (globalRequest && canonicalLocale && canonicalLocale->Destroyable() &&
		canonicalLocale->NotifyCount() == 0)
	{
		delete canonicalLocale;
		canonicalLocale = ZAF_NULLP(ZafLocaleData);
	}
}

ZafUInt32 ZafLocaleData::TimeStamp(void)
{
	ZafUInt32 value;
	(void) time((time_t *)&value);
#if defined(ZAF_MACINTOSH)
	// Fix for time value since Apple uses 1904.
	value -= 31554100L * 66L + 249000L;
#elif defined(__SC__)
	// Fix for time value since Symantec deemed 1968 better than 1970
	value -= 63108000L;
#endif
	return (value);
}

// ----- Attributes ---------------------------------------------------------

void ZafLocaleData::Clear(void)
{
	// Only allow clearing to the canonical locale.
	if (canonicalLocale)
		SetLocale(*canonicalLocale);
	SetError(ZAF_ERROR_NONE);
}

ZafError ZafLocaleData::SetLocale(const ZafLocaleStruct &copy)
{
	int i;

	// Clean up old entries.
	PushLevel();
	if (creditLeftParen)
		delete []creditLeftParen;
	if (creditRightParen)
		delete []creditRightParen;
	if (integerStringInputFormat)
		delete []integerStringInputFormat;
	if (integerStringOutputFormat)
		delete []integerStringOutputFormat;
	if (realStringInputFormat)
		delete []realStringInputFormat;
	if (realStringOutputFormat)
		delete []realStringOutputFormat;
	if (timeStringInputFormat)
		delete []timeStringInputFormat;
	if (timeStringOutputFormat)
		delete []timeStringOutputFormat;
	if (dateStringInputFormat)
		delete []dateStringInputFormat;
	if (dateStringOutputFormat)
		delete []dateStringOutputFormat;
	if (dateTimeStringInputFormat)
		delete []dateTimeStringInputFormat;
	if (dateTimeStringOutputFormat)
		delete []dateTimeStringOutputFormat;
	if (time12StringOutputFormat)
		delete []time12StringOutputFormat;
	if (defDigits)
		delete []defDigits;
	if (altDigits)
		delete []altDigits;
	if (eraTable)
		delete []eraTable;

	// Next, copy the strings.
	strcpy(decimalSeparator, copy.decimalSeparator);
	strcpy(monDecimalSeparator, copy.monDecimalSeparator);
	strcpy(thousandsSeparator, copy.thousandsSeparator);
	strcpy(monThousandsSeparator, copy.monThousandsSeparator);
	strcpy(currencySymbol, copy.currencySymbol);
	memcpy(grouping, copy.grouping, 10);
	memcpy(monGrouping, copy.monGrouping, 10);
	strcpy(intCurrencySymbol, copy.intCurrencySymbol);
	posCurrencyPrecedes = copy.posCurrencyPrecedes;
	negCurrencyPrecedes = copy.negCurrencyPrecedes;
	fractionDigits = copy.fractionDigits;
	intFractionDigits = copy.intFractionDigits;
	strcpy(positiveSign, copy.positiveSign);
	posSignPrecedes = copy.posSignPrecedes;
	posSpaceSeparation = copy.posSpaceSeparation;
	strcpy(negativeSign, copy.negativeSign);
	negSignPrecedes = copy.negSignPrecedes;
	negSpaceSeparation = copy.negSpaceSeparation;

	creditLeftParen = strdup(copy.creditLeftParen);
	creditRightParen = strdup(copy.creditRightParen);

	integerStringInputFormat = strdup(copy.integerStringInputFormat);
	integerStringOutputFormat = strdup(copy.integerStringOutputFormat);
	realStringInputFormat = strdup(copy.realStringInputFormat);
	realStringOutputFormat = strdup(copy.realStringOutputFormat);
	timeStringInputFormat = strdup(copy.timeStringInputFormat);
	timeStringOutputFormat = strdup(copy.timeStringOutputFormat);
	dateStringInputFormat = strdup(copy.dateStringInputFormat);
	dateStringOutputFormat = strdup(copy.dateStringOutputFormat);
	dateTimeStringInputFormat = strdup(copy.dateTimeStringInputFormat);
	dateTimeStringOutputFormat = strdup(copy.dateTimeStringOutputFormat);
	time12StringOutputFormat = strdup(copy.time12StringOutputFormat);
	defDigits = strdup(copy.defDigits);
	altDigits = strdup(copy.altDigits);

	strcpy(timeSeparator, copy.timeSeparator);
	strcpy(dateSeparator, copy.dateSeparator);
	beginGregorian = copy.beginGregorian;
	skipGregorian = copy.skipGregorian;
	eraTableLength = copy.eraTableLength;

	// Last, copy the era table.
	eraTable = new ZafEraStruct[eraTableLength];
	for (i = 0; i < eraTableLength; i++)
		eraTable[i] = copy.eraTable[i];
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafLocaleData::SetLocaleName(const ZafIChar *newLocaleName)
{
	// Make sure locale has been initialized once.
	LocaleAllocate(newLocaleName);

	// If the locale is correct don't initialize it again.
	if (zafLocale && (!newLocaleName || !strcmp(newLocaleName, zafLocale->StringID())))
		return (ZAF_ERROR_NONE);

	// Read a new locale from the data manager.
	ZafLocaleData *newLocale = ZAF_NULLP(ZafLocaleData);
	if (zafDataManager)
	{
#if defined(USE_ZAF_PERSISTENCE)
		// Register the read procedure.
		if (zafDataPersistence)
			zafDataPersistence->AddDataConstructor(ZafLocaleData::className,
				ZafLocaleData::classID, ZafLocaleData::Read);
#endif
		newLocale = DynamicPtrCast(zafDataManager->AllocateData(newLocaleName, ZafLocaleData::className, ZafLocaleData::classID), ZafLocaleData);
		if (newLocale && zafLocale)
		{
			if (zafLocale && zafLocale->Destroyable() && zafLocale->NotifyCount() == 0)
				delete zafLocale;
			zafLocale = newLocale;
		}
#if defined(USE_ZAF_PERSISTENCE)
		else if (zafDataPersistence)
			zafDataPersistence->SetError(ZAF_ERROR_NONE);
#endif
	}

	// Return the error status.
	return (newLocale ? ZAF_ERROR_NONE : ZAF_ERROR_INVALID);
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafLocaleData::ZafLocaleData(const ZafIChar *name, ZafDataPersistence &persist) :
	ZafI18nData(name, persist.PushLevel(className, classID, ZAF_PERSIST_ROOT_DIRECTORY))
{
	// Initialize pointers.
	creditLeftParen = creditRightParen =
		integerStringInputFormat = integerStringOutputFormat =
		realStringInputFormat =	realStringOutputFormat =
		timeStringInputFormat =	timeStringOutputFormat =
		time12StringOutputFormat =
		dateStringInputFormat =	dateStringOutputFormat =
		dateTimeStringInputFormat =	dateTimeStringOutputFormat =
		defDigits = altDigits = ZAF_NULLP(ZafIChar);
	eraTableLength = 0;
	eraTable = ZAF_NULLP(ZafEraStruct);

	// Read the object.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafLocaleData::ReadData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

ZafElement *ZafLocaleData::Read(const ZafIChar *name, ZafDataPersistence &persist)
{
	return (new ZafLocaleData(name, persist));
}

void ZafLocaleData::ReadData(ZafDataPersistence &persist)
{
	int i;

	// Read the locale data.
	ZafFile *file = persist.CurrentFile();
	*file
		>> decimalSeparator
		>> monDecimalSeparator
		>> thousandsSeparator
		>> monThousandsSeparator
		>> currencySymbol;
	for (i = 0; i < 10; i++)
	{
		ZafUInt8 value;
		*file >> value;
		grouping[i] = (char)value;
	}
	for (i = 0; i < 10; i++)
	{
		ZafUInt8 value;
		*file >> value;
		monGrouping[i] = (char)value;
	}
	*file
		 >> intCurrencySymbol
		 >> posCurrencyPrecedes
		 >> negCurrencyPrecedes
		 >> fractionDigits
		 >> intFractionDigits
		 >> positiveSign
		 >> posSignPrecedes
		 >> posSpaceSeparation
		 >> negativeSign
		 >> negSignPrecedes
		 >> negSpaceSeparation;
	*file
		 >> &creditLeftParen
		 >> &creditRightParen
		 >> &integerStringInputFormat
		 >> &integerStringOutputFormat
		 >> &realStringInputFormat
		 >> &realStringOutputFormat
		 >> &timeStringInputFormat
		 >> &timeStringOutputFormat
		 >> &dateStringInputFormat
		 >> &dateStringOutputFormat
		 >> &dateTimeStringInputFormat
		 >> &dateTimeStringOutputFormat
		 >> &time12StringOutputFormat
		 >> &defDigits
		 >> &altDigits;
	*file
		 >> timeSeparator
		 >> dateSeparator
		 >> beginGregorian
		 >> skipGregorian
		 >> eraTableLength;
	if (eraTableLength)
		eraTable = new ZafEraStruct[eraTableLength];
	for (i = 0; i < eraTableLength; i++)
		*file
			>> eraTable[i].direction
			>> eraTable[i].offset
			>> eraTable[i].startDate
			>> eraTable[i].endDate
			>> eraTable[i].eraName
			>> eraTable[i].eraFormat;
}

void ZafLocaleData::Write(ZafDataPersistence &persist)
{
	// Write the object.
	ZafData::Write(persist.PushLevel(className, classID, ZAF_PERSIST_ROOT_DIRECTORY));
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafLocaleData::WriteData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

void ZafLocaleData::WriteData(ZafDataPersistence &persist)
{
	int i;

	// Write the locale data.
	ZafFile *file = persist.CurrentFile();
	*file
		<< decimalSeparator
		<< monDecimalSeparator
		<< thousandsSeparator
		<< monThousandsSeparator
		<< currencySymbol;
	for (i = 0; i < 10; i++)
		*file << grouping[i];
	for (i = 0; i < 10; i++)
		*file << monGrouping[i];
	*file
		<< intCurrencySymbol
		<< posCurrencyPrecedes
		<< negCurrencyPrecedes
		<< fractionDigits
		<< intFractionDigits
		<< positiveSign
		<< posSignPrecedes
		<< posSpaceSeparation
		<< negativeSign
		<< negSignPrecedes
		<< negSpaceSeparation;
	*file
		<< creditLeftParen
		<< creditRightParen
		<< integerStringInputFormat
		<< integerStringOutputFormat
		<< realStringInputFormat
		<< realStringOutputFormat
		<< timeStringInputFormat
		<< timeStringOutputFormat
		<< dateStringInputFormat
		<< dateStringOutputFormat
		<< dateTimeStringInputFormat
		<< dateTimeStringOutputFormat
		<< time12StringOutputFormat
		<< defDigits
		<< altDigits;
	*file
		<< timeSeparator
		<< dateSeparator
		<< beginGregorian
		<< skipGregorian
		<< eraTableLength;
	for (i = 0; i < eraTableLength; i++)
		*file
			<< eraTable[i].direction
			<< eraTable[i].offset
			<< eraTable[i].startDate
			<< eraTable[i].endDate
			<< eraTable[i].eraName
			<< eraTable[i].eraFormat;
}
#endif
