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

#include <z_ctype.hpp>
#include <z_fmtstr.hpp>
#include <z_string.hpp>
#define ZAF_FORMATTED_STRING_INFO
#include "gbl_def.cpp"

// ----- ZafFormattedString -------------------------------------------------

ZafFormattedString::ZafFormattedString(int left, int top, int width,
	ZafStringData *zCompressedData, ZafStringData *zFormatData,
	ZafStringData *zDeleteData) :
	ZafString(left, top, width, new ZafStringData),
	compressedData(ZAF_NULLP(ZafStringData)),
	deleteData(ZAF_NULLP(ZafStringData))
{
	// Set the formatted string information (order is important).
	if (zFormatData)
		SetFormatData(zFormatData);
	else
		SetFormatData(new ZafStringData);

	if (zDeleteData)
		SetDeleteData(zDeleteData);
	else
		SetDeleteData(new ZafStringData);

	if (zCompressedData)
		SetCompressedData(zCompressedData);
	else
		SetCompressedData(new ZafStringData);
}

ZafFormattedString::ZafFormattedString(int left, int top, int width,
	const ZafIChar *compressedText, const ZafIChar *formatText,
	const ZafIChar *deleteText) :
	ZafString(left, top, width, new ZafStringData),
	compressedData(ZAF_NULLP(ZafStringData)),
	deleteData(ZAF_NULLP(ZafStringData))
{
	// Set the formatted string information (order is important).
	SetFormatData(new ZafStringData(formatText));
	SetDeleteData(new ZafStringData(deleteText));
	SetCompressedData(new ZafStringData(compressedText));
}

ZafFormattedString::ZafFormattedString(const ZafFormattedString &copy) :
	ZafString(copy),
	compressedData(ZAF_NULLP(ZafStringData)),
	deleteData(ZAF_NULLP(ZafStringData))
{
	// Set the formatted string information (order is important).
	if (copy.inputFormatData->Destroyable())
		SetFormatData(new ZafStringData(*copy.inputFormatData));
	else
		SetFormatData(copy.inputFormatData);
	if (copy.deleteData->Destroyable())
		SetDeleteData(new ZafStringData(*copy.deleteData));
	else
		SetDeleteData(copy.deleteData);
	if (copy.compressedData->Destroyable())
		SetCompressedData(new ZafStringData(*copy.compressedData));
	else
		SetCompressedData(copy.compressedData);
}

ZafFormattedString::~ZafFormattedString(void)
{
	// Remove the data notification.
	compressedData->SubtractNotification(this, (ZafUpdateFunction)Update);

	// Restore the formatted string information.
	if (compressedData && compressedData->Destroyable() && compressedData->NotifyCount() == 0)
		delete compressedData;
	if (deleteData && deleteData->Destroyable() && deleteData->NotifyCount() == 0)
		delete deleteData;
}

ZafWindowObject *ZafFormattedString::Duplicate(void)
{
	return (new ZafFormattedString(*this));
}

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

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

ZafIChar ZafFormattedString::IsLegalChar(ZafIChar textValue, int offset) const
{
	// Convert the text for comparison.
	ZafIChar format = inputFormatData->Char(offset);
	bool isAlpha = (isspace(textValue) || isalpha(textValue));
	int isDigit = isdigit(textValue);

	// Compare against native formats.
	if ((format == 'a' && isAlpha) ||
		(format == 'N' && isDigit) ||
		(format == 'c' && (isDigit || isAlpha)) ||
		(format == 'x' && textValue >= ' ' && textValue <= '~') ||
		(format == 'L' && (textValue == deleteData->Char(offset))))
		return (textValue);

	// Compare against upper-case formats.
	textValue = toupper(textValue);
	if ((format == 'A' && isAlpha) ||
		(format == 'C' && (isDigit || isAlpha)) ||
		(format == 'X' && textValue >= ' ' && textValue <= '~'))
		return (textValue);

	// The character is not legal.
	return (0);
}

const ZafPaletteStruct *ZafFormattedString::MapClassPalette(ZafPaletteType type, ZafPaletteState state)
{
	const ZafPaletteStruct *palette = MapPalette(defaultPaletteMap, type, state);
	return (palette ? palette : ZafString::MapClassPalette(type, state));
}

ZafError ZafFormattedString::SetCompressedData(ZafStringData *tCompressedData)
{
	// Remove the data notification.
	if (compressedData)
		compressedData->SubtractNotification(this, (ZafUpdateFunction)Update);

	// Reset the compressed data.
	if (compressedData && tCompressedData != compressedData &&
		compressedData->Destroyable() && compressedData->NotifyCount() == 0)
		delete compressedData;
	compressedData = tCompressedData ? tCompressedData : new ZafStringData;

	// Update the expanded data.
	UpdateExpandedData();

	// Add the data notification.
	compressedData->AddNotification(this, (ZafUpdateFunction)Update);

	// Set the os text.
	OSSetText();

	return (ZAF_ERROR_NONE);
}

ZafError ZafFormattedString::SetCompressedText(const ZafIChar *compressedText)
{
	// Reset the compressed data.
	return (compressedData->SetText(compressedText));
}

ZafError ZafFormattedString::SetDeleteData(ZafStringData *tDeleteData)
{
	// Reset the delete data.
	if (deleteData && tDeleteData != deleteData &&
		deleteData->Destroyable() && deleteData->NotifyCount() == 0)
		delete deleteData;
	deleteData = tDeleteData ? tDeleteData : new ZafStringData;

	// Reset the string identification.
	return (ZAF_ERROR_NONE);
}

ZafError ZafFormattedString::SetDeleteText(const ZafIChar *deleteText)
{
	// Reset the delete data.
	return (deleteData->SetText(deleteText));
}

ZafError ZafFormattedString::SetExpandedData(ZafStringData *expandedData)
{
	// Remove the data notification.
	if (stringData)
		stringData->SubtractNotification(this, (ZafUpdateFunction)UpdateExpanded);

	// Reset the string data.
	if (stringData && stringData != expandedData &&
		stringData->Destroyable() && stringData->NotifyCount() == 0)
		delete stringData;
	stringData = expandedData ? expandedData : new ZafStringData;

	// Update the compressed data.
	UpdateCompressedData();

	// Add the data notification.
	stringData->AddNotification(this, (ZafUpdateFunction)UpdateExpanded);

	// Set the os text.
	return (OSSetText());
}

ZafError ZafFormattedString::SetExpandedText(const ZafIChar *expandedText)
{
	// Disable notification.
	ZafUpdateType update = stringData->Update(this);
	stringData->SetUpdate(this, ZAF_UPDATE_NONE);

	ZafError retValue = stringData->SetText(expandedText);

	// Enable notification.
	stringData->SetUpdate(this, update);

	UpdateCompressedData();

	OSSetText();

	// Reset the delete data.
	return (retValue);
}

ZafError ZafFormattedString::SetFormatData(ZafStringData *tFormatData)
{
	// Reset the input data.
	return (ZafString::SetInputFormatData(tFormatData));
}

ZafError ZafFormattedString::SetFormatText(const ZafIChar *formatText)
{
	// Reset the output data.
	return (ZafString::SetInputFormat(formatText));
}

bool ZafFormattedString::SetLowerCase(bool )
{
	// lowerCase is set to false by this class.
	lowerCase = false;
	return (lowerCase);
}

bool ZafFormattedString::SetPassword(bool )
{
	// password is set to false by this class.
	password = false;
	return (password);
}

ZafError ZafFormattedString::SetRange(const ZafIChar *)
{
	// Range cannot be set for this class.
	return (ZAF_ERROR_INVALID);
}

ZafError ZafFormattedString::SetRangeData(ZafStringData *)
{
	// Range cannot be set for this class.
	return (ZAF_ERROR_INVALID);
}

ZafError ZafFormattedString::SetOutputFormat(const ZafIChar *)
{
	// Format cannot be set for this class.
	return (ZAF_ERROR_INVALID);
}

ZafError ZafFormattedString::SetOutputFormatData(ZafStringData *)
{
	// Format cannot be set for this class.
	return (ZAF_ERROR_INVALID);
}

ZafError ZafFormattedString::SetText(const ZafIChar *text)
{
	// Reset the compressed text.
	return (compressedData->SetText(text));
}

bool ZafFormattedString::SetUnanswered(bool setUnanswered)
{
	// Make sure the request is valid.
	if (unanswered != setUnanswered)
	{
		// Re-compute the text before resetting the flag.
		if (setUnanswered)
		{
			compressedData->SetText(ZafLanguageManager::blankString);
/* START BLOCK COMMENT
**				stringData->SetText(deleteData->Text());
**				UpdateCompressedData();
END BLOCK COMMENT */
		}

		unanswered = setUnanswered;
	}

	// Return the current attribute.
	return (unanswered);
}

bool ZafFormattedString::SetUpperCase(bool )
{
	// upperCase is set to false by this class.
	upperCase = false;
	return (upperCase);
}

bool ZafFormattedString::SetVariableName(bool )
{
	// variableName is false for this class.
	variableName = false;
	return (variableName);
}

const ZafIChar *ZafFormattedString::Text(void)
{
	// Get the os text.
	OSGetText();
	return (compressedData->Text());
}

void ZafFormattedString::UpdateCompressedData(void)
{
	// Disable notification.
	ZafUpdateType update = compressedData->Update(this);
	compressedData->SetUpdate(this, ZAF_UPDATE_NONE);

	// Reset the compressed text.
	int i, j;
	for (i = 0, j = 0; deleteData->Char(i); i++)
		if (!IsLiteralChar(inputFormatData->Char(i)))
		{
			ZafIChar value = IsLegalChar(stringData->Char(i), i);
			if (value)
				compressedData->SetChar(j++, value);
			else if (stringData->Char(i) == deleteData->Char(i))
				compressedData->SetChar(j++, stringData->Char(i));
		}
	compressedData->SetChar(j, '\0');
	compressedData->UpdateObjects();

	// Enable notification.
	compressedData->SetUpdate(this, update);
}

void ZafFormattedString::UpdateExpandedData(void)
{
	// Disable notification.
	ZafUpdateType update = stringData->Update(this);
	stringData->SetUpdate(this, ZAF_UPDATE_NONE);

	// Reset the expanded text.
	stringData->SetText(deleteData->Text());
	int i, j;
	for (i = 0, j = 0; deleteData->Char(i) && compressedData->Text() && compressedData->Char(j); i++)
		if (!IsLiteralChar(inputFormatData->Char(i)))
		{
			ZafIChar value = IsLegalChar(compressedData->Char(j), i);
			if (!value && compressedData->Char(j) == deleteData->Char(i))
				value = deleteData->Char(i);
			if (value)
			{
				stringData->SetChar(i, value);
				j++;
			}
		}
	stringData->UpdateObjects();

	// Enable notification.
	stringData->SetUpdate(this, update);
}

ZafError ZafFormattedString::UpdateExpanded(ZafFormattedString *fmtString, ZafUpdateType type)
{
	if (type & ZAF_UPDATE_OBJECT)
		fmtString->UpdateCompressedData();
	return (ZafString::Update(fmtString, type));
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafFormattedString::ZafFormattedString(const ZafIChar *name, ZafObjectPersistence &persist) :
	ZafString(name, persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY)),
	compressedData(ZAF_NULLP(ZafStringData)),
	deleteData(ZAF_NULLP(ZafStringData))
{
	// Read the data.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafFormattedString::ReadData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

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

void ZafFormattedString::ReadData(ZafObjectPersistence &persist)
{
	// Read the data.
	ZafIChar compressedName[ZAF_MAXNAMELEN], deleteName[ZAF_MAXNAMELEN];
	ZafFile *file = persist.CurrentFile();
	*file >> deleteName >> compressedName;

	// Read the data.  Must set deleteData before compressedData.
	if (*deleteName)
		SetDeleteData(new ZafStringData(deleteName, persist));
	if (*compressedName)
		SetCompressedData(new ZafStringData(compressedName, persist));
}

void ZafFormattedString::Write(ZafObjectPersistence &persist)
{
	// Write the object.
	ZafString::Write(persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY));
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafFormattedString::WriteData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

void ZafFormattedString::WriteData(ZafObjectPersistence &persist)
{
	// Write the data.
	const ZafIChar *deleteName = deleteData ? deleteData->StringID() : ZAF_NULLP(ZafIChar);
	const ZafIChar *compressedName = compressedData ? compressedData->StringID() : ZAF_NULLP(ZafIChar);
	ZafFile *file = persist.CurrentFile();
	*file << deleteName << compressedName;

	// Write the data.
	if (deleteData)
		deleteData->Write(persist);
	if (compressedData)
		compressedData->Write(persist);
}
#endif
