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

#include <w_app.hpp>
#include <z_ctype.hpp>
#include <z_keymap.hpp>
#include <z_text.hpp>

// ----- ZafText ---------------------------------------------------------
void ZafUpdateOSMaxLength(OSWindowID screenID, int maxLength, bool isPaste);

ZafWindowObject *ZafText::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Defer to the base class.
	return (ZafWindow::Add(object, position));
}

int ZafText::CursorOffset(void) const
{
	// Get cursor offset from windows.
#if defined(ZAF_WIN32)
	int startPos, endPos;
	SendMessage(screenID, EM_GETSEL, (WPARAM)&startPos, (LPARAM)&endPos);
	return startPos;
#else
	return (HIWORD(SendMessage(screenID, EM_GETSEL, 0, 0L)));
#endif
}

ZafPositionStruct ZafText::CursorPosition(void) const
{
#if defined(ZAF_WIN32)
	int startPos, endPos;
	SendMessage(screenID, EM_GETSEL, (WPARAM)&startPos, (LPARAM)&endPos);
#else
	int startPos = HIWORD(SendMessage(screenID, EM_GETSEL, 0, 0));
#endif

	ZafPositionStruct position;
	position.coordinateType = ZAF_CELL;
	position.line = LOWORD(SendMessage(screenID, EM_LINEFROMCHAR, (WPARAM)startPos, 0));
	position.column = startPos - LOWORD(SendMessage(screenID, EM_LINEINDEX, (WPARAM)position.line, 0));
	return (position);
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafText::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Begin drawing operation.
	ZafRegionStruct drawRegion = BeginDraw();

	// Draw border.
	if (bordered)
		DrawBorder(drawRegion, ccode);

	// Fill the background.
	DrawBackground(drawRegion, ccode == S_REDISPLAY_DATA ? S_REDISPLAY : ccode);

	if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION || ccode == S_REDISPLAY_DATA)
	{
		// Set the text palette.
		display->SetPalette(LogicalPalette(ZAF_PM_TEXT, PaletteState()));

		// Draw the text.
		RECT rect;
		drawRegion.ExportPixel(rect);
		rect.left++;
		rect.top++;
		SetBkMode(display->DisplayContext(), TRANSPARENT);
		if (ZafMSWindowsApp::convertText)
		{
			char *osText = stringData->DynamicOSText();
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
			DrawTextA(display->DisplayContext(), osText, -1, &rect, DT_WORDBREAK);
#else
			DrawText(display->DisplayContext(), osText, -1, &rect, DT_WORDBREAK);
#endif
			delete []osText;
		}
//??? Should use a definition something like ZAF_TEXT_CONVERSION_CODE_NEEDED?
#if defined(ZAF_WIN32) || !defined(ZAF_UNICODE)
		else
			DrawText(display->DisplayContext(), stringData->Text(), -1, &rect, DT_WORDBREAK);
#endif

	}

	// End the drawing operation.
	EndDraw();

	return (ccode);
}
#endif

ZafEventType ZafText::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = event.type;

	if (ccode == E_OSEVENT)
		return ZafText::OSEvent(event);

	// Process Zinc events.
	switch (ccode)
	{
	case S_CURRENT:
		if (autoClear)
#if defined(ZAF_WIN32)
		{
			SendMessage(screenID, EM_SETSEL, (WPARAM)0, (LPARAM)0xFFFFFFFF);
//??? Do we want this?
	 		SendMessage(screenID, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
		}
#else
	  		SendMessage(screenID, EM_SETSEL, (WPARAM)0, (LPARAM)0x7FFF0000L);
#endif
		ccode = ZafWindowObject::Event(event);
		break;

#if	!defined(ZAF_WIN32)
	case S_INITIALIZE:
		hEditDS = 0;
		ccode = ZafWindow::Event(event);
		break;
#endif

	case S_NON_CURRENT:
		OSGetText();
		ccode = ZafWindowObject::Event(event);
		break;

	case N_NON_CURRENT:
		{
		// Syncronize text buffers.
		ZafIChar *oldText = strdup(stringData->Text());
		OSGetText();

		// Set changed only if the text is really different.
		if (strcmp(oldText, stringData->Text()) != 0)
			SetChanged(true);
		delete []oldText;

		// Validate the field.
		ccode = ZafWindowObject::Event(event);

		// Check for errors.
		if (ccode == ZAF_ERROR_NONE)
			unanswered = false;
		else if (ccode == ZAF_ERROR_LEAVE_INVALID)
		{
			invalid = true;
			unanswered = false;
		}
		}
		break;

	case S_ADD_OBJECT:
	case S_SUBTRACT_OBJECT:
		ccode = ZafWindow::Event(event);
		break;

	case S_CUT:
		SendMessage(screenID, WM_CUT, 0, 0);
		break;

	case S_COPY:
		SendMessage(screenID, WM_COPY, 0, 0);
		break;

	case S_PASTE:
		SendMessage(screenID, WM_PASTE, 0, 0);
		break;

	case S_UPDATE_OBJECT:
		OSSetText();
		break;

	case S_UPDATE_DATA:
		OSGetText();
		break;

	default:
		ccode = ZafWindowObject::Event(event);
		break;
	}

	return (ccode);
}


bool ZafText::SetAutoClear(bool tAutoClear)
{
	autoClear = tAutoClear;
	return autoClear;
}

ZafError ZafText::SetCursorOffset(int offset)
{
	if (screenID)
	{
		// Set the cursor position.
#if defined(ZAF_WIN32)
			SendMessage(screenID, EM_SETSEL, (WPARAM)offset, (LPARAM)offset);
	 		SendMessage(screenID, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
#else
	  		SendMessage(screenID, EM_SETSEL, (WPARAM)0, MAKELPARAM(offset, offset));
#endif
		return (ZAF_ERROR_NONE);
	}

	return (ZAF_ERROR_INVALID_ID);
}

ZafError ZafText::SetCursorPosition(ZafPositionStruct position)
{
	// Make sure position.line is within the text buffer.
	int lines = (int)SendMessage(screenID, EM_GETLINECOUNT, 0, 0);
	if (position.line >= lines)
		position.line = lines - 1;

	// Get the offset of the first character in the specified line.
	int offset = (int)SendMessage(screenID, EM_LINEINDEX, (WPARAM)position.line, 0);

	// Make sure position.column is within the text line.
	int columns = (int)SendMessage(screenID, EM_LINELENGTH, (WPARAM)offset, 0);
	if (position.column >= columns)
		position.column = columns;

	// Adjust the offset for the specified coloumn.
	offset += position.column;

	// Set the cursor position.  
	return (SetCursorOffset(offset));
}

bool ZafText::SetVisible(bool visible)
{
	return ZafWindowObject::SetVisible(visible);
}

bool ZafText::SetWordWrap(bool tWordWrap)
{
	if (wordWrap != tWordWrap)
	{
		wordWrap = tWordWrap;
	
		if (screenID)
		{
			UpdateWindow(parent->screenID);
			SendMessage(parent->screenID, WM_SETREDRAW, (WPARAM)FALSE, 0);
			Event(S_DESTROY);
			Event(S_CREATE);
			if (parent->AutomaticUpdate(false))
			  	SendMessage(parent->screenID, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM)0);
			Redisplay();
		}
	}

	return wordWrap;
}

ZafWindowObject *ZafText::Subtract(ZafWindowObject *object)
{
	// Defer to the base class.
	return (ZafWindow::Subtract(object));
}

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

ZafEventType ZafText::OSEvent(const ZafEventStruct &event)
{
	if (editMode && event.InputType() == E_MOUSE)
		return (ZafWindowObject::Event(event));

	ZafEventType ccode = event.type;

	switch (event.osEvent.message)
	{
#if defined(ZAF_WIN32)
	case WM_CTLCOLOREDIT:
#else
	case WM_CTLCOLOR:
#endif
		{
//??? Not necessary if not OSDraw().
		// Get the palettes.
		ZafPaletteStruct textPalette = LogicalPalette(ZAF_PM_TEXT, PaletteState());
		ZafPaletteStruct backgroundPalette = LogicalPalette(ZAF_PM_BACKGROUND, PaletteState());

		// Get the OS colors from the text palette.
		COLORREF colorForeground = (textPalette.colorForeground == ZAF_CLR_DEFAULT) ?
			textPalette.osPalette.colorForeground :
			((ZafScreenDisplay *)display)->colorTable[textPalette.colorForeground];
		COLORREF colorBackground = (backgroundPalette.colorBackground == ZAF_CLR_DEFAULT) ?
			backgroundPalette.osPalette.colorBackground :
			((ZafScreenDisplay *)display)->colorTable[backgroundPalette.colorBackground];

		// Set the OS colors.
		SelectPalette((HDC)event.osEvent.wParam, ((ZafScreenDisplay *)display)->logicalPalette, FALSE);
		::SetTextColor((HDC)event.osEvent.wParam, colorForeground);
		SetBkColor((HDC)event.osEvent.wParam, colorBackground);

		// Get a background brush.
		HBRUSH backgroundBrush = ((ZafScreenDisplay *)display)->GetGdiCacheBrush(backgroundPalette);
		((ZafScreenDisplay *)display)->ReleaseGdiCacheObject(backgroundBrush);
		ccode = (ZafEventType)backgroundBrush;
		}
		break;

	case WM_CHAR:
		ZafUpdateOSMaxLength(screenID, stringData->MaxLength(), false);
		// Make return key get processed as new line.
		if ((int)event.osEvent.wParam == VK_RETURN)
			ccode = DefaultCallback(event);
		else
			ccode = ZafWindowObject::Event(event);
		break;

	case WM_PASTE:
		ZafUpdateOSMaxLength(screenID, stringData->MaxLength(), true);
		ccode = ZafWindowObject::Event(event);
		break;

	case WM_KEYDOWN:
	case WM_KEYUP:
		{
		int keyValue = (int)event.osEvent.wParam;

		if (keyValue == VK_TAB)
			ccode = S_UNKNOWN;
		else
		{
			ccode = ZafWindowObject::OSEvent(event);
			if (ccode == S_UNKNOWN)
			{
				// Translate the virtual key code to an ASCII value.
				UINT charValue = MapVirtualKey(keyValue, 2);
				if ((charValue >= '0' && charValue <= '9') ||
				 (charValue >= 'A' && charValue <= 'Z') ||
				 (keyValue >= VK_NUMPAD0 && keyValue <= VK_DIVIDE) ||
				 (keyValue >= VK_SPACE && keyValue <= VK_DOWN) ||
				 keyValue == VK_BACK)
//				if (charValue && IsPrint((ZafIChar)charValue))
					ccode = 0;
			}
		}
		}
		break;

	case WM_PAINT:
	case WM_ERASEBKGND:
		if (OSDraw())
			ccode = DefaultCallback(event);
		else
			ccode = ZafWindowObject::Event(event);
		break;

#if	!defined(ZAF_WIN32)
	case WM_NCDESTROY:
		ccode = ZafWindow::OSEvent(event);
		if (hEditDS)
			GlobalFree(hEditDS);
		break;
#endif

	case WM_WINDOWPOSCHANGED:
		ccode = ZafWindow::OSEvent(event);
		break;

	default:
		ccode = ZafWindowObject::OSEvent(event);
//		ccode = ZafWindow::OSEvent(event);
	}

	return (ccode);
}

ZafError ZafText::OSGetText(void)
{
	// Update the internal text.
	if (screenID && SystemObject() && OSDraw())
	{
		stringData->SetUpdate(this, ZAF_UPDATE_NONE);
		int textLength = (WORD)SendMessage(screenID, WM_GETTEXTLENGTH, (WPARAM)0, (LPARAM)0);
#if defined(ZAF_UNICODE)
		if (ZafMSWindowsApp::convertText)
		{
			char *osText = new char[textLength + 1];
#	if defined(ZAF_WIN32)
			SendMessageA(screenID, WM_GETTEXT, textLength + 1, (LONG)osText);
#	else
			SendMessage(screenID, WM_GETTEXT, textLength + 1, (LONG)osText);
#	endif
			stringData->SetOSText(osText);
			delete []osText;
		}
		else
		{
			ZafIChar *text = new ZafIChar[textLength + 1];
			SendMessage(screenID, WM_GETTEXT, textLength + 1, (LONG)text);
			stringData->SetText(text);
			delete []text;
		}
#else
		char *text = new char[textLength + 1];
		SendMessage(screenID, WM_GETTEXT, textLength + 1, (LONG)text);
		if (ZafMSWindowsApp::convertText)
			stringData->SetOSText(text);
		else
			stringData->SetText(text);
		delete []text;
#endif
		stringData->SetUpdate(this, ZAF_UPDATE_ALL);
	}
	return (ZAF_ERROR_NONE);
}

void ZafText::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	if (type == ZAF_PM_BACKGROUND)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
			palette.osPalette.colorForeground = GetSysColor(COLOR_WINDOW);
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
			palette.osPalette.colorBackground = GetSysColor(COLOR_WINDOW);
	}
	else
		ZafWindowObject::OSMapPalette(palette, type, state);
}

void ZafText::OSRegisterObject(void)
{
	DWORD dwStyle = ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_CHILD | WS_CLIPSIBLINGS;
	DWORD dwExStyle = 0;

	if (Disabled() && !editMode)
		dwStyle |= WS_DISABLED;
	if (visible)
		dwStyle |= WS_VISIBLE;

	if (HorizontalScrollBar())
		dwStyle |= WS_HSCROLL;

	if (VerticalScrollBar())
		dwStyle |= WS_VSCROLL;

	if (Bordered() && OSDraw())
	{
#if defined(ZAF_MSWINDOWS_3D)
		if (ZafMSWindowsApp::native3D)
			dwExStyle |= WS_EX_CLIENTEDGE;
		else
			dwStyle |= WS_BORDER;
#else
		dwStyle |= WS_BORDER;
#endif
	}

	if (osDraw)
	{
		if (!WordWrap())
			dwStyle |= ES_AUTOHSCROLL;
		if (HzJustify() == ZAF_HZ_RIGHT)
			dwStyle |= ES_RIGHT;
		if (HzJustify() == ZAF_HZ_CENTER)
			dwStyle |= ES_CENTER;
		if (viewOnly)
			dwStyle |= ES_READONLY;

#if defined(ZAF_WIN32)
		screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, ZAF_ITEXT("EDIT"), dwStyle, stringData->Text(), 0, dwExStyle);
#else
		// Give the text object its own data segment. (64K limit)
		hEditDS = (HINSTANCE)GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE, 256L);
		screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, ZAF_ITEXT("EDIT"), dwStyle, stringData->Text(), 0, dwExStyle, hEditDS);
#endif

		// Set the font.
		ZafPaletteStruct fontPalette = LogicalPalette(ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED);
		SendMessage(screenID, WM_SETFONT, (WPARAM)((ZafScreenDisplay *)display)->fontTable[fontPalette.font], (LPARAM)FALSE);

		// Limit the size of the edit buffer;
		SendMessage(screenID, EM_LIMITTEXT,
			(WPARAM)(stringData->MaxLength() > -1) ? stringData->MaxLength() : 0,
			(LPARAM)FALSE);

		// Set the text.
		bool saveChanged = changed;
		OSSetText();
		changed = saveChanged;
	}
	else
		screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, NULL,
			dwStyle, NULL, 0, dwExStyle);

}

OSWindowID ZafText::OSScreenID(ZafScreenIDType) const
{
	return (screenID);
}

ZafError ZafText::OSSetText(void)
{
//??? This code should be in a "z_" file?
	// Handle unanswered case.
	if (Unanswered())
		stringData->SetText(ZafLanguageManager::blankString);

	// Set the os text.
	if (screenID && SystemObject() && OSDraw())
	{
		if (ZafMSWindowsApp::convertText)
		{
			char *osText = stringData->DynamicOSText();
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
			SendMessageA(screenID, WM_SETTEXT, (WPARAM)0, (LPARAM)osText);
#else
			SendMessage(screenID, WM_SETTEXT, (WPARAM)0, (LPARAM)osText);
#endif
			delete []osText;
		}
		else
			SendMessage(screenID, WM_SETTEXT, (WPARAM)0, (LPARAM)stringData->Text());
	}
	else
		RedisplayData();

	return (ZAF_ERROR_NONE);
}


void ZafText::OSSize(void)
{
	// Default to the base class.
	ZafWindowObject::OSSize();
}

ZafError ZafText::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	// Default to the base class.
	return (ZafWindow::OSUpdatePalettes(zafTypes, osTypes));
}
