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

#include <z_cset.hpp>
#include <z_store.hpp>
#include <z_string.hpp>
#define ZAF_CODE_SET_INFO
#include "gbl_def.cpp"
#if !defined(ZAF_UNICODE) && !defined(ZAF_ISO8859_1)
#include "map_def.cpp"
#endif

#if defined(ZAF_MSWINDOWS)
#	include <w_app.hpp>
#endif

// ----- ZafCharLookup ------------------------------------------------------

class ZAF_EXPORT ZafCharLookup
{
public:
	ZafCharLookup(const ZafIChar *lookup, ZafUInt8 minValue = 0, ZafUInt8 maxValue = 255);
	ZafCharLookup(const ZafCharLookup &copy);
	virtual ~ZafCharLookup(void);
	virtual int ConvertToOSChar(ZafIChar zafChar, char *osChar);
	virtual int ConvertToZafChar(const char *osChar, ZafIChar &zafChar);

	// --- Special UNICODE functions ---
#	if defined(ZAF_UNICODE)
	int ConvertToOSChar(ZafIChar zafChar, wchar_t *osChar);
	int ConvertToZafChar(const wchar_t *osChar, ZafIChar &zafChar);
#	endif

#if defined(USE_ZAF_PERSISTENCE)
	// --- Persistent members ---
	ZafCharLookup(const ZafIChar *mapName, const ZafClassName mapType, ZafDataPersistence &persist);
	virtual void Write(ZafDataPersistence &persist);
#endif

	ZafCharLookup *Duplicate() { return new ZafCharLookup(*this);}

	// --- Class identification ---
	static ZafClassID classID;
	static ZafClassNameChar ZAF_FARDATA className[];

protected:
	friend class ZAF_EXPORT ZafCodeSetData;
	static ZafClassNameChar ZAF_FARDATA isoToLocalName[];
	static ZafClassNameChar ZAF_FARDATA localToIsoName[];

	ZafIChar *mapName;
	ZafIChar *mapType;
	ZafUInt8 minValue, maxValue;
	ZafIChar lookup[256];

#if defined(USE_ZAF_PERSISTENCE)
	void ReadData(ZafDataPersistence &persist);
	void WriteData(ZafDataPersistence &persist);
#endif
};

#if defined(ZAF_UNICODE)
#	include "u_cset.cpp"
#endif

ZafClassID ZafCharLookup::classID = ID_ZAF_CHAR_LOOKUP;
ZafClassNameChar ZAF_FARDATA ZafCharLookup::className[] = ZAF_ITEXT("ZafCharLookup");

ZafClassNameChar ZAF_FARDATA ZafCharLookup::isoToLocalName[] = ZAF_ITEXT("isoToLocal");
ZafClassNameChar ZAF_FARDATA ZafCharLookup::localToIsoName[] = ZAF_ITEXT("localToIso");

ZafCharLookup::ZafCharLookup(const ZafIChar *tLookup, ZafUInt8 tMinValue, ZafUInt8 tMaxValue) :
	mapName(ZAF_NULLP(ZafIChar)), mapType(ZAF_NULLP(ZafIChar)),
	minValue(tMinValue), maxValue(tMaxValue)
{
	// Copy the array.
	memset(lookup, 0, 256 * sizeof(ZafIChar));
	if (tLookup)
		memcpy(&lookup[minValue], tLookup, (maxValue-minValue+1)*sizeof(ZafIChar));
}

ZafCharLookup::ZafCharLookup(const ZafCharLookup &copy) :
	mapName(ZAF_NULLP(ZafIChar)), mapType(ZAF_NULLP(ZafIChar)),
	minValue(copy.minValue), maxValue(copy.maxValue)
{
	if (copy.mapName)
		mapName = strdup(copy.mapName);

	if (copy.mapType)
		mapType = strdup(copy.mapType);

	// Copy the array.
	memset(lookup, 0, 256);
	memcpy(&lookup[minValue], copy.lookup, (maxValue-minValue+1) * sizeof(ZafIChar));
}

#if defined(USE_ZAF_PERSISTENCE)
ZafCharLookup::ZafCharLookup(const ZafIChar *tMapName,
	const ZafClassName tMapType, ZafDataPersistence &persist) :
	minValue(0), maxValue(255)
{
	mapName = tMapName ? strdup(tMapName) : ZAF_NULLP(ZafIChar);
	mapType = tMapType ? strdup(tMapType) : ZAF_NULLP(ZafIChar);

	persist.PushLevel(className, classID, ZAF_PERSIST_FILE);
	persist.AllocateFile(mapType, ZAF_FILE_READ);

	if (persist.Error() == ZAF_ERROR_NONE)
		ZafCharLookup::ReadData(persist);
 
	persist.PopLevel();
}

void ZafCharLookup::ReadData(ZafDataPersistence &persist)
{
	ZafFile *file = persist.CurrentFile();

	// Read the object information.
	memset(lookup, 0, 256);
	if (file && !file->Error())
	{
		int i;
		*file >> minValue >> maxValue;
		for (i = minValue; i <= maxValue; i++)
		{
			ZafUInt8 value;
			*file >> value;
			lookup[i] = (ZafIChar)value;
		}
	}
}
#endif

ZafCharLookup::~ZafCharLookup(void)
{
	if (mapName)
		delete []mapName;
	if (mapType)
		delete []mapType;
}

int ZafCharLookup::ConvertToOSChar(ZafIChar zafChar, char *osChar)
{
	// Convert the value.
#	if defined(ZAF_UNICODE)
	*osChar = (char)(zafChar & 0xff);
#	else
	*osChar = (minValue <= (ZafUInt8)zafChar &&
		(ZafUInt8)zafChar <= maxValue && lookup[(ZafUInt8)zafChar]) ?
		lookup[(ZafUInt8)zafChar] : zafChar;
#	endif
	return (1);
}

int ZafCharLookup::ConvertToZafChar(const char *osChar, ZafIChar &zafChar)
{
	// Convert the value.
#	if defined(ZAF_UNICODE)
	zafChar = (ZafUInt8)*osChar;
#	else
	zafChar = (minValue <= (ZafUInt8)*osChar &&
		(ZafUInt8)*osChar <= maxValue && lookup[(ZafUInt8)*osChar]) ?
		lookup[(ZafUInt8)*osChar] : *osChar;
#	endif
	return (1);
}

#if defined(USE_ZAF_PERSISTENCE)
void ZafCharLookup::Write(ZafDataPersistence &persist)
{
	persist.PushLevel(className, classID, ZAF_PERSIST_FILE);
	persist.AllocateFile(mapType, ZAF_FILE_CREATE | ZAF_FILE_READWRITE);

	if (persist.Error() == ZAF_ERROR_NONE)
		ZafCharLookup::WriteData(persist);

	persist.PopLevel();
}

void ZafCharLookup::WriteData(ZafDataPersistence &persist)
{
	// Write the object information.
	ZafFile *file = persist.CurrentFile();
	if (!file->Error())
	{
		int i;
		ZafUInt8 value;

		*file << minValue << maxValue;
		for (i = minValue; i <= maxValue; i++)
		{
			value = (ZafUInt8)lookup[i];
			*file<<value;
		}
	}
}
#endif

// ----- ZafCodeSetData -----------------------------------------------------

ZafCodeSetData::ZafCodeSetData(ZafIChar *tIsoToLocal, ZafIChar *tLocalToIso) :
	ZafCodeSetDataStub(),
	defaultLeadByte(0)
{
	// Determine the directory separator.
	InitializeDirSepStr();

	// Set the map tables.
#	if defined(ZAF_UNICODE)
	nativeMapping = false;
	unicodeToLocal = localToUnicode = ZAF_NULLP(ZafWcharLookup);
	isoToLocal = new ZafCharLookup(tIsoToLocal);
	isoToLocal->mapType = strdup(ZafCharLookup::isoToLocalName);
	localToIso = new ZafCharLookup(tLocalToIso);
	localToIso->mapType = strdup(ZafCharLookup::localToIsoName);
#	elif defined(ZAF_ISO8859_1)
	isoToLocal = new ZafCharLookup(tIsoToLocal);
	isoToLocal->mapType = strdup(ZafCharLookup::isoToLocalName);
	localToIso = new ZafCharLookup(tLocalToIso);
	localToIso->mapType = strdup(ZafCharLookup::localToIsoName);
#	else
	isoToLocal = localToIso = ZAF_NULLP(ZafCharLookup);
#	endif
}

ZafCodeSetData::ZafCodeSetData(const ZafCodeSetData &copy) :
	ZafCodeSetDataStub(copy),
	defaultLeadByte(copy.defaultLeadByte),
	isoToLocal(0), localToIso(0)
{
	strcpy(dirSepStr, copy.dirSepStr);
	if (copy.isoToLocal)
		isoToLocal = copy.isoToLocal->Duplicate();
	if (copy.localToIso)
		localToIso = copy.localToIso->Duplicate();
#	if defined(ZAF_UNICODE)
	nativeMapping = false;
	if (copy.unicodeToLocal)
		unicodeToLocal = copy.unicodeToLocal->Duplicate();
	if (copy.localToUnicode)
		localToUnicode = copy.localToUnicode->Duplicate();
#	endif
}

ZafCodeSetData::~ZafCodeSetData(void)
{
	// Delete the data.
#	if defined(ZAF_UNICODE)
	if (unicodeToLocal)
		delete unicodeToLocal;
	if (localToUnicode)
		delete localToUnicode;
#	endif
	if (isoToLocal)
		delete isoToLocal;
	if (localToIso)
		delete localToIso;
}

void ZafCodeSetData::Clear(void)
{
	// No clear operation is defined for this class, but a method is defined
	// for the pure virtual ZafData::Clear().
}

void ZafCodeSetData::CodeSetAllocate(const ZafIChar *name)
{
	// Check for initialization.
	if (zafCodeSet)
		return;
	ZafI18nData::AddStaticModule(CodeSetFree);

	// Check for special codes, since ZAF runs natively in unicode or ISO modes.
#	if defined(ZAF_UNICODE)
	static ZafIChar *unicodeName = ZAF_ITEXT("UNICODE");
	if (name && !stricmp(name, unicodeName))
	{
		zafCodeSet = new ZafCodeSetData(ZAF_NULLP(ZafIChar), ZAF_NULLP(ZafIChar));
		zafCodeSet->SetStringID(unicodeName);
		return;
	}
#	elif defined(ZAF_ISO8859_1)
	static ZafIChar *isoName = ZAF_ITEXT("ISO8859-1");
	if (name && !stricmp(name, isoName))
	{
		zafCodeSet = new ZafCodeSetData(ZAF_NULLP(ZafIChar), ZAF_NULLP(ZafIChar));
		zafCodeSet->SetStringID(isoName);
		return;
	}
#	endif

	// Try the zafDataManager.
	ZafError localError = ZAF_ERROR_NONE;
	if (zafDataManager && !zafCodeSet)
	{
		if (name)
			zafCodeSet = DynamicPtrCast(zafDataManager->AllocateData(name, ZafCodeSetData::className, ZafCodeSetData::classID), ZafCodeSetData);

		// Make sure zafCodeSet's destructor isn't called multiple times.
		if (zafCodeSet)
			ZafI18nData::SubtractStaticModule(CodeSetFree);
#if defined(USE_ZAF_PERSISTENCE)
		else if (zafDataPersistence)
		{
			localError = zafDataPersistence->Error();
			zafDataPersistence->SetError(ZAF_ERROR_NONE);
		}
#endif
	}

	// Default to code initialization.
	if (!zafCodeSet)
	{
#	if defined(ZAF_UNICODE)
		zafCodeSet = new ZafCodeSetData(ZAF_NULLP(ZafIChar), ZAF_NULLP(ZafIChar));
		zafCodeSet->SetStringID(unicodeName);
#	elif defined(ZAF_ISO8859_1)
		zafCodeSet = new ZafCodeSetData(ZAF_NULLP(ZafIChar), ZAF_NULLP(ZafIChar));
		zafCodeSet->SetStringID(isoName);
#	else
		zafCodeSet = new ZafCodeSetData(i18nToLocal, localToI18n);
		zafCodeSet->SetStringID(defaultCodeSetName);
#	endif
	}

	// Remap the directory separator based on the current language.
	zafCodeSet->InitializeDirSepStr();

#	if defined(ZAF_UNICODE)
	// Set native unicode support flag.
	zafCodeSet->SetNativeMapping(ZafCodeSetDataStub::OSNativeMapping());
	if (!zafCodeSet->NativeMapping())
	{
		for (ZafIChar *temp = zafCodeSet->dirSepStr; *temp; temp++)
			zafCodeSet->ConvertToZafChar(temp, *temp);
	}
#	else
	for (ZafIChar *temp = zafCodeSet->dirSepStr; *temp; temp++)
		zafCodeSet->ConvertToZafChar(temp, *temp);
#	endif

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

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

int ZafCodeSetData::ConvertToOSChar(ZafIChar zafChar, char *osChar)
{
	// Translate the value.
	int length = 0;
#	if defined(ZAF_UNICODE)
	//Use OS mapping.
	if (nativeMapping)
		length = OSConvertToOSChar(zafChar, osChar);
	else if (unicodeToLocal)
		length = unicodeToLocal->ConvertToOSChar(zafChar, osChar);
	else if (isoToLocal)
		length = isoToLocal->ConvertToOSChar(zafChar, osChar);
	else
		osChar[length++] = (char)zafChar;
#	else
	if (isoToLocal)
		length = isoToLocal->ConvertToOSChar(zafChar, osChar);
	else
		osChar[length++] = zafChar;
#	endif
	osChar[length] = 0;

	return (length);
}

char *ZafCodeSetData::ConvertToOSString(const ZafIChar *zafString, char *osString, bool allocate)
{
	// Seed the osString string.
	static char ZAF_FARDATA mapTextBuffer[256];
	if (!zafString)
		return (ZAF_NULLP(char));

	// Make sure there is a valid buffer.
	int maxLength = (int)(strlen(zafString) + 1);
	if (allocate)
		osString = new char[maxLength * sizeof(ZafIChar)];
	else if (!osString)
	{
		osString = mapTextBuffer;
		maxLength = sizeof(mapTextBuffer);
	}

	// Convert the string.
#	if defined(ZAF_UNICODE)
	// Use OS Mpping.
	if (nativeMapping)
		return (OSConvertToOSString(zafString, osString, maxLength));
	else if (unicodeToLocal)
	{
		int j = 0;
		for (int i = 0; i < maxLength && zafString[i]; i++)
			j += unicodeToLocal->ConvertToOSChar(zafString[i], &osString[j]);
		osString[j] = '\0';
	}
	else if (isoToLocal)
	{
		int j = 0;
		for (int i = 0; i < maxLength && zafString[i]; i++)
			j += isoToLocal->ConvertToOSChar(zafString[i], &osString[j]);
		osString[j] = '\0';
	}
	else
	{
		char *text = osString;
		while (*zafString)
			*text++ = (char)*zafString++;
		*text = 0;
	}
#	else
	if (isoToLocal)
	{
		int j = 0;
		for (int i = 0; i < maxLength && zafString[i]; i++)
			j += isoToLocal->ConvertToOSChar(zafString[i], &osString[j]);
		osString[j] = '\0';
	}
	else
		strcpy(osString, zafString);
#	endif
	return (osString);
}

int ZafCodeSetData::ConvertToZafChar(const char *osChar, ZafIChar &zafChar)
{
	// Translate the value.
	int length = 1;
#	if defined(ZAF_UNICODE)
	// Use OS mapping.
	if (nativeMapping)
		length = OSConvertToZafChar(osChar, zafChar);
	else if (localToUnicode)
		length = localToUnicode->ConvertToZafChar(osChar, zafChar);
	else if (localToIso)
		length = localToIso->ConvertToZafChar(osChar, zafChar);
	else
		zafChar = *osChar;
#	else
	if (localToIso)
		length = localToIso->ConvertToZafChar(osChar, zafChar);
	else
		zafChar = *osChar;
#	endif
	return (length);
}

ZafIChar *ZafCodeSetData::ConvertToZafString(const char *osString, ZafIChar *zafString, bool allocate)
{
	// Seed the os string.
	static ZafIChar ZAF_FARDATA mapTextBuffer[256];
	if (!osString)
		return (ZAF_NULLP(ZafIChar));

	// Make sure there is a valid buffer.
	int maxLength = (int)(strlen(osString) + 1);
	if (allocate)
		zafString = new ZafIChar[maxLength];
	else if (!zafString)
	{
		zafString = mapTextBuffer;
		maxLength = sizeof(mapTextBuffer);
	}

	// Convert the string.
#	if defined(ZAF_UNICODE)
	// Use OS Mpping.
	if (nativeMapping)
		return (OSConvertToZafString(osString, zafString, maxLength));
	else if (localToUnicode)
	{
		int i = 0;
		for (int j = 0; i < maxLength && osString[j]; i++)
			j += localToUnicode->ConvertToZafChar(&osString[j], zafString[i]);
		zafString[i] = '\0';
	}
	else if (localToIso)
	{
		int i = 0;
		for (int j = 0; i < maxLength && osString[j]; i++)
			j += localToIso->ConvertToZafChar(&osString[j], zafString[i]);
		zafString[i] = '\0';
	}
	else
	{
		ZafIChar *text = zafString;
		while (*osString)
			*text++ = *osString++;
		*text = 0;
	}
#	else
	if (localToIso)
	{
		int i = 0;
		for (int j = 0; i < maxLength && osString[j]; i++)
			j += localToIso->ConvertToZafChar(&osString[j], zafString[i]);
		zafString[i] = '\0';
	}
	else
		strcpy(zafString, osString);
#	endif
	return (zafString);
}

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

void ZafCodeSetData::InitializeDirSepStr(void)
{
	dirSepStr[0] = ZAF_DIR_SEP_CHAR;
	dirSepStr[1] = '\0';
}

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

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

ZafError ZafCodeSetData::SetCodeSetName(const ZafIChar *newCodeSetName)
{
	// Make sure code set has been initialized once.
	CodeSetAllocate(newCodeSetName);

	// If the code set is correct don't initialize it again.
	if (zafCodeSet && newCodeSetName && *newCodeSetName &&
			!strcmp(newCodeSetName, zafCodeSet->StringID()))
		return (ZAF_ERROR_NONE);

	// Read a new code set from the data manager.
	ZafCodeSetData *newCodeSet = ZAF_NULLP(ZafCodeSetData);
	if (zafDataManager)
	{
#if defined(USE_ZAF_PERSISTENCE)
		// Register the read procedure.
		if (zafDataPersistence)
			zafDataPersistence->AddDataConstructor(ZafCodeSetData::className,
				ZafCodeSetData::classID, ZafCodeSetData::Read);
#endif
		newCodeSet = DynamicPtrCast(zafDataManager->AllocateData(newCodeSetName, ZafCodeSetData::className, ZafCodeSetData::classID), ZafCodeSetData);
		if (newCodeSet && zafCodeSet)
		{
			if (zafCodeSet && zafCodeSet->Destroyable() && zafCodeSet->NotifyCount() == 0)
				delete zafCodeSet;
			zafCodeSet = newCodeSet;
			// CodeSetAllocate sets ZafApplication to delete zafCodeSet.
			ZafI18nData::SubtractStaticModule(CodeSetFree);
		}
#if defined(USE_ZAF_PERSISTENCE)
		else if (zafDataPersistence)
			zafDataPersistence->SetError(ZAF_ERROR_NONE);
#endif
	}

	// Set the error status.
	if (newCodeSetName && *newCodeSetName && !newCodeSet)
		zafCodeSet->SetError(ZAF_ERROR_INVALID);

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

ZafUInt8 ZafCodeSetData::SetDefaultLeadByte(ZafUInt8 tDefaultLeadByte)
{
	// Reset the default lead byte.
	if (defaultLeadByte != tDefaultLeadByte)
		defaultLeadByte = tDefaultLeadByte;
	return defaultLeadByte;
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafCodeSetData::ZafCodeSetData(const ZafIChar *name, ZafDataPersistence &persist) :
	ZafCodeSetDataStub(name, persist.PushLevel(className, classID, ZAF_PERSIST_ROOT_DIRECTORY)),
		defaultLeadByte(0)
{
	isoToLocal = localToIso = ZAF_NULLP(ZafCharLookup);
#	if defined(ZAF_UNICODE)
	unicodeToLocal = localToUnicode = ZAF_NULLP(ZafWcharLookup);
#	endif

	if (persist.Error() == ZAF_ERROR_NONE)
	{
		// Determine the directory separator.
		InitializeDirSepStr();

		// Set the map tables.
#		if defined(ZAF_UNICODE)
		// Read the tables.
		unicodeToLocal = new ZafWcharLookup(name, ZafWcharLookup::unicodeToLocalName, persist);
		localToUnicode = new ZafWcharLookup(name, ZafWcharLookup::localToUnicodeName, persist);
		ZafError unicodeError = persist.Error();
		isoToLocal = new ZafCharLookup(name, ZafCharLookup::isoToLocalName, persist);
		localToIso = new ZafCharLookup(name, ZafCharLookup::localToIsoName, persist);
		persist.SetError(unicodeError);
#		elif defined(ZAF_ISO8859_1)
		isoToLocal = new ZafCharLookup(name, ZafCharLookup::isoToLocalName, persist);
		localToIso = new ZafCharLookup(name, ZafCharLookup::localToIsoName, persist);
#		else
		isoToLocal = localToIso = ZAF_NULLP(ZafCharLookup);
#		endif
	}

	persist.PopLevel();

	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

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

void ZafCodeSetData::Write(ZafDataPersistence &persist)
{
	// Write the map tables.
	ZafData::Write(persist.PushLevel(className, classID, ZAF_PERSIST_ROOT_DIRECTORY));

#	if defined(ZAF_UNICODE)
	if (unicodeToLocal)
		unicodeToLocal->Write(persist);
	if (localToUnicode)
		localToUnicode->Write(persist);
#	endif

	if (isoToLocal)
		isoToLocal->Write(persist);
	if (localToIso)
		localToIso->Write(persist);

	persist.PopLevel();

	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}
#endif

ZafClassID ZafCodeSetData::ClassID(void) const
{
	return (classID);
}

ZafClassName ZafCodeSetData::ClassName(void) const
{
	return (className);
}

ZafUInt8 ZafCodeSetData::DefaultLeadByte(void) const
{
	return (defaultLeadByte);
}

const ZafIChar *ZafCodeSetData::CodeSetName(void)
{
	return (zafCodeSet ? zafCodeSet->StringID() : ZAF_NULLP(ZafIChar));
}


