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

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

#include <stdlib.h>

int ZafCharLookup::ConvertToOSChar(ZafIChar zafChar, wchar_t *osChar)
{
	int offset = zafChar & 0xff;
	*osChar = lookup[offset] ? lookup[offset] : zafChar;
	return (1);
}

int ZafCharLookup::ConvertToZafChar(const wchar_t *osChar, ZafIChar &zafChar)
{
	int offset = *osChar & 0xff;
	zafChar = lookup[offset] ? lookup[offset] : *osChar;
	return (1);
}

// ----- ZafWcharLookup -----------------------------------------------------

#if defined(_SUNOS4)
#	undef mblen
#endif

class ZAF_EXPORT ZafWcharLookup
{
public:
	ZafWcharLookup(void);
	ZafWcharLookup(const ZafWcharLookup &copy);
	virtual ~ZafWcharLookup(void);
	int ConvertToOSChar(ZafIChar zafChar, char *osChar);
	int ConvertToOSChar(ZafIChar zafChar, wchar_t *osChar);
	int ConvertToZafChar(const char *osChar, ZafIChar &zafChar);
	int ConvertToZafChar(const wchar_t *osChar, ZafIChar &zafChar);

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

	int mblen(const char *local);

	// --- Persistent members ---
	ZafWcharLookup(const ZafIChar *mapName, const ZafClassName mapType, ZafDataPersistence &persist);
	void Write(ZafDataPersistence &persist);

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

	class ZAF_EXPORT ZafBlockEntry : public ZafCharLookup
	{
	public:
		ZafBlockEntry(ZafIChar defaultChar);
		ZafBlockEntry(const ZafBlockEntry &copy);
		virtual int ConvertToOSChar(ZafIChar zafChar, char *osChar);
		virtual int ConvertToOSChar(ZafIChar zafChar, wchar_t *osChar);
		virtual int ConvertToZafChar(const char *osChar, ZafIChar &zafChar);
		virtual int ConvertToZafChar(const wchar_t *osChar, ZafIChar &zafChar);


		// --- Persistent members ---
		ZafBlockEntry(const ZafIChar *mapName, const ZafClassName mapType,
			ZafDataPersistence &persist);
		void Write(ZafDataPersistence &persist);

		static ZafClassID classID;
		static ZafClassNameChar ZAF_FARDATA className[];

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

protected:
	friend class ZAF_EXPORT ZafCodeSetData;
	static ZafClassNameChar unicodeToLocalName[];
	static ZafClassNameChar localToUnicodeName[];

	struct ZafLeadinEntry
	{
		ZafUInt8 minValue, maxValue;
	};

	ZafIChar *mapName;
	ZafIChar *mapType;
	ZafIChar defaultChar;
	int noOfLeadins;
	ZafLeadinEntry leadin[20];
	ZafBlockEntry *blockEntry[256];

	void ReadData(ZafDataPersistence &persist);
	void WriteData(ZafDataPersistence &persist);
};

ZafClassID ZafWcharLookup::classID = ID_ZAF_WCHAR_LOOKUP;
ZafClassNameChar ZAF_FARDATA ZafWcharLookup::className[] = ZAF_ITEXT("ZafWcharLookup");
ZafClassID ZafWcharLookup::ZafBlockEntry::classID = ID_ZAF_WCHAR_BLOCK_ENTRY;
ZafClassNameChar ZAF_FARDATA ZafWcharLookup::ZafBlockEntry::className[] = ZAF_ITEXT("ZafBlockEntry");

ZafClassNameChar ZafWcharLookup::unicodeToLocalName[] = ZAF_ITEXT("unicodeToLocal");
ZafClassNameChar ZafWcharLookup::localToUnicodeName[] = ZAF_ITEXT("localToUnicode");

ZafWcharLookup::ZafWcharLookup(void) :
	mapName(0), mapType(0), defaultChar(' '), noOfLeadins(0)
{
	// Clear the span entries.
	for (int i = 0; i < 256; i++)
		blockEntry[i] = ZAF_NULLP(ZafBlockEntry);
}

ZafWcharLookup::ZafWcharLookup(const ZafWcharLookup &copy) :
	mapName(0), mapType(0), defaultChar(copy.defaultChar),
	noOfLeadins(copy.noOfLeadins)
{
	int i;
	for (i = 0; i < noOfLeadins; i++)
		leadin[i] = copy.leadin[i];

	for (i = 0; i < 256; i++)
		if (copy.blockEntry[i])
			blockEntry[i] = copy.blockEntry[i]->Duplicate();
}

ZafWcharLookup::~ZafWcharLookup(void)
{
	// Destroy the allocated span entries.
	for (int i = 0; i < 256; i++)
		if (blockEntry[i])
			delete blockEntry[i];

	if (mapName)
		delete [] mapName;
	if (mapType)
		delete [] mapType;
}

int ZafWcharLookup::ConvertToOSChar(ZafIChar zafChar, char *osChar)
{
	// Make sure the block is in memory.
	ZafUInt8 block = (ZafUInt8)(zafChar >> 8);
	if (!blockEntry[block])
		blockEntry[block] = new ZafBlockEntry(defaultChar);

	// Convert the value.
	return (blockEntry[block]->ConvertToOSChar(zafChar, osChar));
}

int ZafWcharLookup::ConvertToOSChar(ZafIChar zafChar, wchar_t *osChar)
{
	int blockNum = (zafChar >> 8) & 0xff;

	if (!blockEntry[blockNum])
		blockEntry[blockNum] = new ZafBlockEntry(defaultChar);

	// Convert the value.
	return (blockEntry[blockNum]->ConvertToOSChar(zafChar, osChar));
}

int ZafWcharLookup::ConvertToZafChar(const char *osChar, ZafIChar &zafChar)
{
	char tempString[2];
	int length = mblen(osChar);
//	int blockNum = *osChar & 0xff;
	int blockNum;

	if (length > 1)
	{
		tempString[0] = osChar[1];
		blockNum = *osChar & 0xff;
	}
	else
	{
		tempString[0] = osChar[0];
		blockNum = 0;
	}

	if (!blockEntry[blockNum])
		blockEntry[blockNum] = new ZafBlockEntry(defaultChar);

	tempString[1] = 0;
	blockEntry[blockNum]->ConvertToZafChar(tempString, zafChar);

	return (length);
}

int ZafWcharLookup::ConvertToZafChar(const wchar_t *osChar, ZafIChar &zafChar)
{
	int blockNum = (*osChar >> 8) & 0xff;

	if (!blockEntry[blockNum])
		blockEntry[blockNum] = new ZafBlockEntry(defaultChar);

	// Convert the value.
	return (blockEntry[blockNum]->ConvertToZafChar(osChar, zafChar));
}

ZafWcharLookup::ZafWcharLookup(const ZafIChar *tMapName, const ZafClassName tMapType, ZafDataPersistence &persist)
{
	for (int i = 0; i < 256; i++)
		blockEntry[i] = ZAF_NULLP(ZafBlockEntry);

	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)
		ZafWcharLookup::ReadData(persist);

	persist.PopLevel();
}

int ZafWcharLookup::mblen(const char *local)
{
	ZafUInt8 temp = *local;
	for (int i = 0; i < noOfLeadins; i++)
		if (leadin[i].minValue <= temp && temp <= leadin[i].maxValue)
			return (2);

	return (1);
}

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

	if (!file->Error())
	{
		*file >> noOfLeadins;
		int i;
		for (i = 0; i < noOfLeadins; i++)
		{
			*file >> leadin[i].minValue >> leadin[i].maxValue;
		}
		if (noOfLeadins < 0)
			*file >> leadin[0].minValue >> leadin[0].maxValue;

		ZafUInt16 value;
		*file >> value;
		defaultChar = (ZafIChar)value;

		ZafFileSystem *fileSystem = persist.CurrentFileSystem();
		ZafFileInfoStruct fileInfo;
		ZafIChar mapName[25];
		strcpy(mapName, mapType);
		strcat(mapName, ZAF_ITEXT(".*"));
		int finished = fileSystem->FindFirst(fileInfo, mapName);
		int blockNum;

		while (!finished)
		{
			ZafIChar *mapNum = strchr(fileInfo.name, '.');
			if (mapNum)
			{
				mapNum++;
				char temp[5];
				char *t;

				// Manually convert the string for use by sscanf.
				for (t = temp; *mapNum;)
				{
					*t = (char)*mapNum;
					t++;
					mapNum++;
				}
				*t = '\0';

				// Call built in sscanf because zafCodeSet doesn't exist yet.
				#undef sscanf
				sscanf(temp, "%x", &blockNum);
				persist.PushLevel(fileInfo.stringID, fileInfo.numberID, ZAF_PERSIST_FILE);
				persist.AllocateFile(fileInfo.name, ZAF_FILE_READ);
				if (!persist.Error())
					blockEntry[blockNum] = new ZafBlockEntry(0, 0, persist);
				persist.PopLevel();
			}
			finished = fileSystem->FindNext(fileInfo, mapName);
		}

	}
}

void ZafWcharLookup::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)
		ZafWcharLookup::WriteData(persist);

	persist.PopLevel();
}

void ZafWcharLookup::WriteData(ZafDataPersistence &persist)
{
	ZafFile *file = persist.CurrentFile();

	if (!file->Error())
	{
		*file << noOfLeadins;
		int i;
		for (i = 0; i < noOfLeadins; i++)
		{
			*file << leadin[i].minValue << leadin[i].maxValue;
		}
		if (noOfLeadins < 0)
			*file << leadin[0].minValue << leadin[0].maxValue;

		*file << (ZafUInt16)defaultChar;

		int blockNum;
		for (blockNum = 0; blockNum < 256; blockNum++)
			if (blockEntry[blockNum])
			{
//				*file << blockNum;
				blockEntry[blockNum]->Write(persist);
			}
		blockNum = -1;
		*file << blockNum;
	}

}

ZafWcharLookup::ZafBlockEntry::ZafBlockEntry(ZafIChar defaultChar) :
	ZafCharLookup(ZAF_NULLP(ZafIChar))
{
	for (int i = minValue; i <= maxValue; i++)
		lookup[i] = defaultChar;
}

ZafWcharLookup::ZafBlockEntry::ZafBlockEntry(const ZafIChar *, const ZafClassName, ZafDataPersistence &persist) :
	ZafCharLookup(ZAF_NULLP(ZafIChar))
{
	ZafFile *file = persist.CurrentFile();

	if (!file->Error())
	{
		int i;
		ZafUInt16 value;
		*file >> minValue >> maxValue;
		for (i = minValue; i <= maxValue; i++)
		{
			*file >> value;
			lookup[i] = (ZafIChar)value;
		}
	}
}

ZafWcharLookup::ZafBlockEntry::ZafBlockEntry(const ZafBlockEntry &copy):
	ZafCharLookup(copy)
{
}

void ZafWcharLookup::ZafBlockEntry::Write(ZafDataPersistence &persist)
{
	ZafFile *file = persist.CurrentFile();

	if (!file->Error())
	{
		int i;
		*file << minValue << maxValue;
		for (i = minValue; i <= maxValue; i++)
			*file << (ZafUInt16)lookup[i];
	}
}

int ZafWcharLookup::ZafBlockEntry::ConvertToOSChar(ZafIChar zafChar, char *osChar)
{
	ZafIChar tempChar;
	int offset;
	int returnValue = 1;

	if (zafChar > 256)
		offset = zafChar & 0xff;
	else
		offset = zafChar;

	tempChar = lookup[offset];
	if (tempChar > 256)
	{
		returnValue = 2;
		osChar[0] = (char)((tempChar >> 8) & 0xff);
		osChar[1] = (char)(tempChar & 0xff);
		osChar[2] = 0;
	}
	else
	{
		osChar[0] = (char)(tempChar & 0xff);
		osChar[1] = 0;
	}

	return returnValue;
}

int ZafWcharLookup::ZafBlockEntry::ConvertToOSChar(ZafIChar zafChar, wchar_t *osChar)
{
	int offset;

	offset = zafChar & 0xff;

	*osChar = lookup[offset];

	return 1;
}

int ZafWcharLookup::ZafBlockEntry::ConvertToZafChar(const char *osChar, ZafIChar &zafChar)
{
	zafChar = lookup[(*osChar & 0xff)];
	return 1;
}

int ZafWcharLookup::ZafBlockEntry::ConvertToZafChar(const wchar_t *osChar, ZafIChar &zafChar)
{

	zafChar = lookup[(*osChar & 0xff)];
	return 1;
}

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

int ZafCodeSetData::ConvertToOSWChar(ZafIChar zafChar, wchar_t *osChar)
{
	// Translate the value.
	int length = 0;
	if (unicodeToLocal)
		length = unicodeToLocal->ConvertToOSChar(zafChar, osChar);
	else if (isoToLocal)
		length = isoToLocal->ConvertToOSChar(zafChar, osChar);
	else
		osChar[length++] = zafChar;
	osChar[length] = 0;

	return (length);
}

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

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

	// Convert the string.
	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
		strcpy(osString, zafString);
	return (osString);
}

int ZafCodeSetData::ConvertToZafChar(const wchar_t *osChar, ZafIChar &zafChar)
{
	// Translate the value.
	int length = 1;
	if (localToUnicode)
		length = localToUnicode->ConvertToZafChar(osChar, zafChar);
	else if (localToIso)
		length = localToIso->ConvertToZafChar(osChar, zafChar);
	else
		zafChar = *osChar;
	return (length);
}

ZafIChar *ZafCodeSetData::ConvertToZafString(const wchar_t *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 = strlen(osString) + 1;
	if (!zafString && allocate)
		zafString = new ZafIChar[maxLength];
	else if (!zafString && !allocate)
	{
		zafString = mapTextBuffer;
		maxLength = sizeof(mapTextBuffer);
	}

	// Convert the string.
	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
		strcpy(zafString, osString);
	return (zafString);
}

ZafIChar *ZafCodeSetData::IsoToUnicode(const char *isoText, ZafIChar *unicodeText)
{
	if (!isoText)
		return (ZAF_NULLP(ZafIChar));

	int maxLength = strlen(isoText) + 1;
	if (!unicodeText)
		unicodeText = new ZafIChar[maxLength];

	ZafIChar *text = unicodeText;
	for (; *isoText; text++, isoText++)
		*text = (defaultLeadByte << 8) | *isoText;
	*text = '\0';

	return (unicodeText);
}

char *ZafCodeSetData::UnicodeToIso(const ZafIChar *unicodeText, char *isoText)
{
	if (!unicodeText)
		return (ZAF_NULLP(char));

	int maxLength = strlen(unicodeText) + 1;
	if (!isoText)
		isoText = new char[maxLength];

	char *text = isoText;
	for (; *unicodeText; text++, unicodeText++)
		*text = (char)*unicodeText;
	*text = '\0';
	return (isoText);
}

int ZafCodeSetData::mblen(const char *hardware)
{
	if (localToUnicode)
		return localToUnicode->mblen(hardware);
	return (1);
}

int ZafCodeSetData::wcstombs(char *s, const ZafIChar *pwcs, int n)
{
	if (n < 0)
		n = strlen(pwcs) * sizeof(ZafIChar) + 1;

	char temp[4];
	int length;
	int i;
	char *tmp;
	for (i = 0; *pwcs && i < n; pwcs++)
	{
		length = ConvertToOSChar(*pwcs, temp);

		if (i + length >= n)
			break;

		tmp = temp;
		for (;*tmp;tmp++)
			s[i++] = *tmp;

	}

	if (!*pwcs && i < n)
		s[i] = 0;

	return (i);
}

int ZafCodeSetData::mbstowcs(ZafIChar *pwcs, const char *s, int n)
{
	if (n < 0)
		n = strlen(s);

	int i;
	int length;
	for (i = 0; *s && i < n; i++)
	{
		length = ConvertToZafChar(s, pwcs[i]);
		s += length;
	}

	if (!*s && i < n)
		pwcs[i] = 0;

	return (i);
}

int ZafCodeSetData::OSConvertToOSChar(ZafIChar zafChar, char *osChar)
{
	return ::wctomb(osChar, zafChar);
}

char *ZafCodeSetData::OSConvertToOSString(const ZafIChar *zafString, char *osString, int maxLength)
{
	::wcstombs(osString, zafString, maxLength);
//	WideCharToMultiByte(CP_OEMCP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, zafString, -1, osString, maxLength, NULL, NULL);
	return osString;
}

int ZafCodeSetData::OSConvertToZafChar(const char *osChar, ZafIChar &zafChar)
{
	return ::mbtowc(&zafChar, osChar, MB_CUR_MAX);
}

ZafIChar *ZafCodeSetData::OSConvertToZafString(const char *osString, ZafIChar *zafString, int maxLength)
{
	::mbstowcs(zafString, osString, maxLength);
//	MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, osString, -1, zafString, maxLength);
	return zafString;
}
