//	Zinc Application Framework - D_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_stdio.hpp>
#include <z_string.hpp>

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

void ZafFormattedString::DeleteText(ZafEventType ccode)
{
	// Get the current cursor position.
	int startOffset = cursor, endOffset = cursor;

	// Determine the delete range.
	switch (ccode)
	{
	case L_LEFT:
		if (endOffset > 0)
			startOffset = endOffset - 1;
		while (startOffset && IsLiteralChar(inputFormatData->Char((int)startOffset)))
			startOffset--;
		endOffset = startOffset + 1;
		break;

	case L_WORD_LEFT:
		startOffset = endOffset - 1;
		break;

	case L_BOL:
		startOffset = 0;
		while (IsLiteralChar(inputFormatData->Char((int)startOffset)))
			startOffset++;
		break;

	case L_RIGHT:
		while (IsLiteralChar(inputFormatData->Char((int)startOffset)))
			startOffset++;
		endOffset = startOffset + 1;
		break;

	case L_WORD_RIGHT:
		endOffset = startOffset + 1;
		break;

	case L_EOL:
		endOffset = strlen(deleteData->Text());
		break;

	default:
		return; // unknown command.
	}
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafFormattedString::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	return (ZafString::Draw(event, ccode));
}
#endif

ZafEventType ZafFormattedString::Event(const ZafEventStruct &event)
{
	bool needsUpdate = false;

	ZafEventType ccode = LogicalEvent(event);

	// Check for zinc events.
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		// Check for an unanswered string.
		if (Unanswered())
			compressedData->SetText(ZafLanguageManager::blankString);
		UpdateExpandedData();
		OSSetText();
		ccode = ZafString::Event(event);
		break;

	// ----- Action messages -----
	case S_CURRENT:
		while (inputFormatData->Char((int)cursor) == 'L')
			cursor++;
		SetCursorOffset(cursor);
//		SetInsert(false);
		ccode = ZafString::Event(event);
		break;

	case E_KEY:
		{
			if (ViewOnly())
				break;
						   
			// Check for marked text.
			if (beginMark != -1)
			{
				ZafUpdateType updateType = stringData->Update();
				stringData->SetUpdate(ZAF_UPDATE_NONE);
				const ZafIChar *deleteText = deleteData->Text();
				for (int begin = beginMark; begin < endMark; begin++)
					stringData->Replace(begin, 1, &deleteText[begin], 1);
				stringData->SetUpdate(updateType);
				cursor = beginMark;
				beginMark = endMark = -1;
				needsUpdate = true;
				SetChanged(true);
				UpdateCompressedData();
			}

			// Reposition cursor if on literal char.
  			if (inputFormatData->Char(cursor) == 'L')
				MoveCursor(L_RIGHT);
			if (cursor == (int)strlen(inputFormatData->Text()))
				break;
		
			// See if char is legal.
			ZafIChar tChar = event.key.value;
			if (IsLegalChar(tChar, cursor))
			{
				ZafEventStruct tEvent = event;
				tEvent.key.value = tChar;

				ZafUpdateType updateType = stringData->Update();
				stringData->SetUpdate(ZAF_UPDATE_NONE);
				stringData->Replace(cursor, 1, &tChar, 1);
				stringData->SetUpdate(updateType);
				MoveCursor(L_RIGHT);

				if (inputFormatData->Char(cursor) == 'L')
					MoveCursor(L_RIGHT);
				needsUpdate = true;
				SetChanged(true);
				UpdateCompressedData();
				UpdateExpandedData();
			}
		}
		break;

	case L_BACKSPACE:
		if (!ViewOnly())
		{
			if (beginMark != endMark)
			{
				Event(S_CUT);
				break;
			}
			MoveCursor(L_LEFT);
			ZafUpdateType updateType = stringData->Update();
			stringData->SetUpdate(ZAF_UPDATE_NONE);
			stringData->Replace(cursor, 1, &deleteData->Text()[cursor], 1);
			stringData->SetUpdate(updateType);
			needsUpdate = true;
			SetChanged(true);
		}
		break;

	case L_CUT:
	case S_CUT:
		if (beginMark != -1 && !ViewOnly())
		{
			int length = endMark - beginMark;

			if (pasteBuffer)
				delete []pasteBuffer;
			pasteLength = length;
			if (pasteLength)
			{
				pasteBuffer = new ZafIChar[pasteLength];
				memcpy(pasteBuffer, &stringData->Text()[beginMark], pasteLength * sizeof(ZafIChar));
			}
			else
				pasteBuffer = ZAF_NULLP(ZafIChar);

			ZafUpdateType updateType = stringData->Update();
			stringData->SetUpdate(ZAF_UPDATE_NONE);
			const ZafIChar *deleteText = deleteData->Text();
			for (int begin = beginMark; begin < endMark; begin++)
				stringData->Replace(begin, 1, &deleteText[begin], 1);
			stringData->SetUpdate(updateType);

			cursor = beginMark;
			while (inputFormatData->Char(cursor) && IsLiteralChar(inputFormatData->Char(cursor)))
				cursor++;
			SetCursorOffset(cursor);
			beginMark = endMark = -1;
			needsUpdate = true;
			SetChanged(true);
		}
		break;

	case L_PASTE:
	case S_PASTE:
		if (!ViewOnly() && pasteBuffer)
		{
			int pos = cursor;
			for(ZafIChar *nPos = pasteBuffer; *nPos; nPos++)
			{
				if (*nPos == deleteData->Char(pos))
					pos++;
				else
				{
					while (inputFormatData->Char(pos) == 'L')
						pos++;
					if (IsLegalChar(*nPos, pos))
					{
						ZafUpdateType updateType = stringData->Update();
						stringData->SetUpdate(ZAF_UPDATE_NONE);
						stringData->Replace(pos++, 1, nPos, 1);
						stringData->SetUpdate(updateType);
					}
				}
			}
			ZafString::Event(S_REDISPLAY);
		}
		break;

	case L_DELETE:
		if (ViewOnly())
			break;

		if (beginMark != endMark)
			Event(ZafEventStruct(S_CUT));
		else
		{
			const ZafIChar *text = stringData->Text();
			int length = strlen(inputFormatData->Text());
			if (cursor == length)
				break;
			if (inputFormatData->Char(cursor) == 'L')
				MoveCursor(L_RIGHT);
			ZafUpdateType updateType = stringData->Update();
			stringData->SetUpdate(ZAF_UPDATE_NONE);
			const ZafIChar *deleteText = deleteData->Text();
			for (int i = cursor; i < length; i++)
				if (text[i] && text[i] != deleteText[i])
				{
					stringData->Replace(i, 1, &deleteText[i], 1);
					needsUpdate = true;
					SetChanged(true);
					break;
				}
			stringData->SetUpdate(updateType);
		}
		if (HzJustify() != ZAF_HZ_LEFT && Focus())
			Event(S_REDISPLAY_CURSOR);
		break;

	case L_LEFT:
	case L_RIGHT:
		if (beginMark != -1)
			needsUpdate = true;
		beginMark = endMark = -1;
		MoveCursor(ccode);
		break;

/* START BLOCK COMMENT
**		case L_SELECT:
**		case S_NON_CURRENT:
**			OSGetText();
**			ccode = ZafString::Event(event);
**			break;
END BLOCK COMMENT */

	case L_BOL:
	case L_WORD_LEFT:
		ZafString::Event(event);
		if (inputFormatData->Char(cursor) == 'L')
			MoveCursor(L_RIGHT);
		break;

	case L_EOL:
	case L_WORD_RIGHT:
		ZafString::Event(event);
		if (cursor < (int)strlen(inputFormatData->Text()) && inputFormatData->Char(cursor) == 'L')
			MoveCursor(L_RIGHT);
		break;

	case L_END_SELECT:
		ccode = ZafString::Event(event);
		if (inputFormatData->Char(cursor) == 'L' && beginMark == endMark)
			MoveCursor(L_RIGHT);
		break;

	// ----- Default or unknown messages -----
	default:
		ccode = ZafString::Event(event);
		break;
	}

	if (needsUpdate)
	{
		ZafRegionStruct drawRegion;
		drawRegion.left = drawRegion.top = 0;
		drawRegion.right = zafRegion.right - zafRegion.left;
		drawRegion.bottom = zafRegion.bottom - zafRegion.top;
		drawRegion.coordinateType = zafRegion.coordinateType;
		if (bordered)
			drawRegion -= ZAF_BORDER_SIZE;
		Event(ZafEventStruct(S_REDISPLAY_REGION, 0, drawRegion));
	}

	// Return the control code.
	return (ccode);
}

void ZafFormattedString::InsertChar(ZafIChar key)
{
	ZafEventStruct event;
	event.type = E_KEY;
	event.key.value = key;
	Event(event);
}

void ZafFormattedString::InsertText(ZafIChar *value)
{
	// Insert the characters into the text.
	while (*value)
	{
		InsertChar(*value);
		value++;
	}
}

void ZafFormattedString::MoveCursor(ZafEventType ccode)
{
	// Get the current cursor position.
	int oldOffset = cursor, newOffset = cursor;

	// Determine the new cursor position.
	switch (ccode)
	{
	case L_BOL:
		// Go to the beginning of line.
		newOffset = 0;
		while (inputFormatData->Char(newOffset) && IsLiteralChar(inputFormatData->Char(newOffset)))
			newOffset++;
		break;

	case L_WORD_LEFT:
		// Move off the beginning of the word.
		if (newOffset)
			newOffset--;
		while (newOffset && IsLiteralChar(inputFormatData->Char(newOffset)))
			newOffset--;
		// Move to the start of the current word.
		while (newOffset && !IsLiteralChar(inputFormatData->Char(newOffset-1)))
			newOffset--;
		break;

	case L_LEFT:
		// Move left one character.
		if (newOffset)
			newOffset--;
		while (newOffset && IsLiteralChar(inputFormatData->Char(newOffset)))
			newOffset--;
		break;

	case L_EOL:
		// Go to the end of line.
		newOffset = strlen(inputFormatData->Text()) - 1;
		while (newOffset && IsLiteralChar(inputFormatData->Char(newOffset)))
			newOffset--;
		break;

	case L_WORD_RIGHT:
		// Move to the start of the next word.
		while (inputFormatData->Char(newOffset) && !IsLiteralChar(inputFormatData->Char(newOffset)))
			newOffset++;
		while (inputFormatData->Char(newOffset) && IsLiteralChar(inputFormatData->Char(newOffset)))
			newOffset++;
		break;

	case L_RIGHT:
		if (inputFormatData->Char(newOffset))
			newOffset++;
		while (inputFormatData->Char(newOffset) && IsLiteralChar(inputFormatData->Char(newOffset)))
			newOffset++;
		break;

	default:
		return; // unknown command.
	}

	// Move to the specified position.
	if (oldOffset != newOffset && !IsLiteralChar(inputFormatData->Char(newOffset)))
	{
		cursor = newOffset;
		SetCursorOffset(cursor);
	}
}

// ----- OS Specific Functions ----------------------------------------------

ZafEventType ZafFormattedString::OSEvent(const ZafEventStruct &)
{
	return (S_ERROR);
}

ZafError ZafFormattedString::OSGetText(void)
{
	UpdateCompressedData();
	return (ZAF_ERROR_NONE);
}

void ZafFormattedString::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	ZafWindowObject::OSMapPalette(palette, type, state);
}

void ZafFormattedString::OSRegisterObject(void)
{
	ZafWindowObject::OSRegisterObject();
}

ZafError ZafFormattedString::OSSetText(void)
{
	UpdateExpandedData();

	const ZafIChar *text = stringData->Text();
	cursor = length = strlen(text);
	beginMark = endMark = -1;

	if (screenID)
	{
		// Update cursor in case the text shifted or the new text is shorter.
		firstDisplayedChar = 0;
  		SetCursorOffset(cursor);
		ZafRegionStruct drawRegion;
		drawRegion.left = drawRegion.top = 0;
		drawRegion.right = zafRegion.Width() - 1;
		drawRegion.bottom = zafRegion.Height() - 1;
		drawRegion.coordinateType = zafRegion.coordinateType;
		if (bordered)
			drawRegion -= ZAF_BORDER_SIZE;
		Event(ZafEventStruct(S_REDISPLAY_REGION, 0, drawRegion));
	}
	return (ZAF_ERROR_NONE);
}

