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

#include <time.h>
#include <z_win.hpp>
#include <z_cursor.hpp>
#include <z_sysevt.hpp>
#include <z_dsp.hpp>
#include <z_string.hpp> // For strcmp().
#include <z_app.hpp>
#include <z_utils.hpp>
#define ZAF_CURSOR_INFO
#include "img_def.cpp"
#include "gbl_def.cpp"

#if defined(ZAF_TEXT_MODE)
#	include "d_hware.hpp"
#endif

// ----- ZafCursor ---------------------------------------------------------
extern bool _devicePainting;

#define activeObject zafCursor_activeObject
extern ZafWindowObject *activeObject;

#if !defined(ZAF_TEXT_MODE)
#	define _upToDate zafCursor_upToDate
extern bool _upToDate;

// Define cursor system resources.
static struct SYS_CURSOR
{
	ZafDeviceImage imageID;
	ZafImageStruct *image;
} ZAF_FARDATA _cursorImage[] =
{
	{ DC_INSERT, &_insert_ZafCursor },
	{ DC_OVERSTRIKE, &_overstrike_ZafCursor },
	{ 0, 0 }
};
#endif

ZafCursor::ZafCursor(ZafDeviceState _state, ZafDeviceImage _imageType) :
	ZafDevice(E_CURSOR, _state), imageType(_imageType)
{
	// Check the image information.
	ImageAllocate();
	SetStringID(className);
	SetNumberID((ZafNumberID)classID);

	// Initialize the cursor information.
	position.column = display->columns;
	position.line = display->lines;
	offset.column = offset.line = 0;
#if defined(ZAF_TEXT_MODE)
	position.coordinateType = offset.coordinateType = ZAF_CELL;
#else
	position.coordinateType = offset.coordinateType = ZAF_PIXEL;
#endif
	blinkTimer = new ZafTimer(D_OFF, blinkRate);
	blinkTimer->SetQueueEvents(true);
	blinkTimer->SetNotifyMessage(N_TIMER_PRIVATE);
	eventManager->Add(blinkTimer);
	installed = true;
}

ZafCursor::~ZafCursor(void)
{
	// Check the image information.
	ImageFree();
}

ZafEventType ZafCursor::Event(const ZafEventStruct &event)
{
	// Switch on the raw code.
	ZafDeviceState oldState = DeviceState();
	ZafDeviceImage oldImage = ImageType();
	ZafPositionStruct oldPosition = position;
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	case D_INITIALIZE:
		// Create the timer.
		blinkTimer->Event(D_ON);

		oldState = D_OFF;
		oldImage = 0;
		Event(imageType);
		break;

	case D_DEINITIALIZE:
		// Kill the timer.
		blinkTimer->Event(D_OFF);
		eventManager->Subtract(blinkTimer);
		delete blinkTimer;
		blinkTimer = ZAF_NULLP(ZafTimer);
		SetDeviceState(D_HIDE);
		break;

	case D_ON:
	case D_OFF:
		deviceState = ccode;
		if (blinkTimer)
			blinkTimer->Event(deviceState);
		break;

	case D_ACTIVATE:
		activeObject = event.windowObject;
		break;

	case D_DEACTIVATE:
		activeObject = ZAF_NULLP(ZafWindowObject);
		break;

	case D_STATE:
		return ((deviceState == D_OFF) ? D_OFF : ImageType());

	case D_POSITION:
		position = event.position;
		if (deviceState == D_HIDE)
			SetDeviceState(D_ON);
		break;

	case DC_INSERT:
	case DC_OVERSTRIKE:
		imageType = ccode;
		SetDeviceState(D_ON);
#if !defined(ZAF_TEXT_MODE)
		if (ccode == DC_INSERT)
			offset.column = 0;
		else
			offset.column = 1;
#endif
		break;
	}

	// Reset the cursor information.
	if (oldImage == ImageType() && oldState == deviceState &&
		oldPosition == position)
		return (ccode);

	_devicePainting = true;
#if defined(ZAF_TEXT_MODE)
	if (deviceState == D_ON && position.column >= 0 &&
		position.column < display->columns && position.line >= 0 &&
		position.line < display->lines)
		I_CursorPosition(position.line, position.column, ImageType());
	else
		I_CursorRemove();
#else
	if (deviceState == D_ON)
	{
/* START BLOCK COMMENT
**			if (ImageType() != oldImage || !_image)
**			{
**				if (_image)
**					delete _image;
**				_image = new ZafBitmapData((ImageType() == DC_INSERT) ?
**					insertInfo : overstrikeInfo);
**			}
END BLOCK COMMENT */
		int tableOffset = 0;
		if (ImageType() == DC_INSERT)
			tableOffset = 0;
		else if (ImageType() == DC_OVERSTRIKE)
			tableOffset = 1;
//		offset.column = offset.line = 0;

		ZafRegionStruct clip, viewPort;

		// Start with screen region and pare it down.
		clip.left = clip.top = 0;
		clip.right = display->columns - 1;
		clip.bottom = display->lines - 1;
		clip.coordinateType = ZAF_PIXEL;

		if (activeObject)
		{
			// Start with the object's region, based at (0, 0), and translate it
			// based on its parents' regions.
			viewPort.left = viewPort.top = 0;
			viewPort.right = activeObject->zafRegion.Width() - 1;
			viewPort.bottom = activeObject->zafRegion.Height() - 1;
			viewPort.coordinateType = activeObject->zafRegion.coordinateType;

			// Get the clip and virtual regions.
			activeObject->GetClip(activeObject, clip, viewPort);
		}

		_upToDate = ((ZafScreenDisplay *)display)->DeviceSet(ZAF_IMT_CURSOR_IMAGE, position.column + offset.column,
			position.line + offset.line, imageTable[tableOffset]->width, ZafMin(15, display->TextSize(ZAF_ITEXT("Wq")).Height() + 1),
			imageTable[tableOffset]->array, clip);
	}
	else
	{
		ZafRegionStruct clip;

		// Start with screen region and pare it down.
		clip.left = clip.top = 0;
		clip.right = display->columns - 1;
		clip.bottom = display->lines - 1;
		clip.coordinateType = ZAF_PIXEL;

		_upToDate = ((ZafScreenDisplay *)display)->DeviceMove(ZAF_IMT_CURSOR_IMAGE, display->columns,
			display->lines, clip);
	}
#endif
	_devicePainting = false;

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

#if defined(ZAF_TEXT_MODE)
void ZafCursor::ImageAllocate(void) {}
void ZafCursor::ImageFree(bool) {}
#else
void ZafCursor::ImageAllocate(void)
{
	// Check for an initialized cursor system.
#define initialized zafCursor_initialized
	extern bool initialized;
	if (initialized)
		return;

	// Initialize the cursor images.
	ZafI18nData::AddStaticModule(ImageFree);
	int i;
	for (i = 0; i < ZAF_MAXCURSORIMAGES; i++)
		imageTable[i] = ZAF_NULLP(ZafBitmapData);
	for (i = 0; _cursorImage[i].imageID; i++)
	{
		ZafNumberID numberID = ZafAbs((int)_cursorImage[i].imageID);
		if (!imageTable[i])
		{
			imageTable[i] = new ZafBitmapData(*_cursorImage[i].image);
			imageTable[i]->SetNumberID(numberID);
		}
	}

	initialized = true;
}

void ZafCursor::ImageFree(bool globalRequest)
{
	// Destroy the static images.
	if (globalRequest && imageTable[0])
	{
		for (int i = 0; imageTable[i]; i++)
		{
			imageTable[i]->staticHandle = false;
			delete imageTable[i];
			imageTable[i] = ZAF_NULLP(ZafBitmapData);
		}
	}
}
#endif

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

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

void ZafCursor::Poll(void)
{
#if !defined(ZAF_TEXT_MODE)
	if (!installed || deviceState == D_OFF)
		return;

	// Get the next event (without destroying it) to see if we need
	// to do anything.
	ZafEventStruct event;
	eventManager->Get(event, Q_NO_BLOCK | Q_BEGIN | Q_NO_DESTROY | Q_NO_POLL);

	if (event.type == N_TIMER_PRIVATE && event.device == blinkTimer)
		eventManager->Get(event, Q_NO_BLOCK | Q_BEGIN | Q_NO_POLL);

	// Cursor could not be updated during a previous paint operation.
	else if (!_upToDate)
	{
		ZafDeviceImage newImage = ImageType();
		imageType = 0;
		Event(ZafEventStruct(newImage));
		_upToDate = true;
		return;
	}
	else
		return;


	_devicePainting = true;
	if (deviceState == D_ON)
	{
		ZafRegionStruct clip;

		// Start with screen region and pare it down.
		clip.left = clip.top = 0;
		clip.right = display->columns - 1;
		clip.bottom = display->lines - 1;
		clip.coordinateType = ZAF_PIXEL;

		_upToDate = ((ZafScreenDisplay *)display)->DeviceMove(ZAF_IMT_CURSOR_IMAGE, display->columns,
			display->lines, clip);
		SetDeviceState(D_HIDE);
	}
	else
	{
		ZafRegionStruct clip, viewPort;

		// Start with screen region and pare it down.
		clip.left = clip.top = 0;
		clip.right = display->columns - 1;
		clip.bottom = display->lines - 1;
		clip.coordinateType = ZAF_PIXEL;

		if (activeObject)
		{
			// Start with the object's region, based at (0, 0), and translate it
			// based on its parents' regions.
			viewPort.left = viewPort.top = 0;
			viewPort.right = activeObject->zafRegion.Width() - 1;
			viewPort.bottom = activeObject->zafRegion.Height() - 1;
			viewPort.coordinateType = activeObject->zafRegion.coordinateType;

			// Get the clip and virtual regions.
			activeObject->GetClip(activeObject, clip, viewPort);

			if (!clip.Overlap(viewPort))
				return;
		}

		_upToDate = ((ZafScreenDisplay *)display)->DeviceMove(ZAF_IMT_CURSOR_IMAGE,
			position.column + offset.column, position.line + offset.line, clip);
		SetDeviceState(D_ON);
	}
	_devicePainting = false;

#endif
}

int ZafCursor::SetBlinkRate(int tBlinkRate)
{
	blinkRate = tBlinkRate;
	return (blinkRate);
}

ZafDeviceImage ZafCursor::SetImageType(ZafDeviceImage tImageType)
{
	imageType = tImageType;
	return (imageType);
}

