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

#include <z_dsp.hpp>
#include <z_evtmgr.hpp>
#include <z_image.hpp>
#include <z_mouse2.hpp>
#include <z_string.hpp> // For strcmp().
#include <z_sysevt.hpp>
#include <z_utils.hpp>
#include <z_i18n.hpp>

#if defined(USE_ZAF_MOUSE)
#	define ZAF_MOUSE_INFO
#	include "d_hware.hpp"

#	if defined(USE_UGL_INPUT)
#		include <uglinput.h>
#		include <input/p8250ms.h>
//#		include <input/pps2ms.h>

UGL_INPUT_Q_ID InputQueueGet(void);
bool InputQueuePoll(ugl_input_event &event, ZafEventType type);
void InputQueueRelease();

#	endif
#endif

extern bool _devicePainting;
void (* mouseQueueEvent)(unsigned, int *, int *) = ZAF_NULLF(void (*)(unsigned, int *, int *));

#	if !defined(USE_UGL_INPUT)
static void mouseQueueEventFunc(unsigned state, int *horizontalMickeys, int *verticalMickeys);
int _horizontalRatio = 1;
int _verticalRatio = 1;

#define MAX_EVENTS	20
static unsigned sFifo[MAX_EVENTS];
static int xFifo[MAX_EVENTS];
static int yFifo[MAX_EVENTS];
static int zState;

extern int volatile _head, _tail;
#	endif

#if defined(DOS32PP) || (defined(_MSC_VER) && defined(DOS386))
extern void MouseRead();
#elif defined(DOSX286)
	bool _isOS2 = false;
#endif

static ZafMouse *_mouse;

// ----- ZafMouse ----------------------------------------------------------

#if defined(USE_ZAF_MOUSE)
void ZafMouse::CloseMouse(void)
{
#	if defined(USE_UGL_INPUT)
	uglInputDevDestroy(driver);
	InputQueueRelease();
#	else
	mouseQueueEvent = ZAF_NULLF(void (*)(unsigned, int *, int *));
	I_MouseClose();
#	endif
}

bool ZafMouse::DoubleClicked(int line, int column)
{
	bool doubleClicked = false;
	clock_t clickTime = clock();
	if ((clickTime - lastClickTime) * 1000 / CLOCKS_PER_SEC < DoubleClickRate() &&
		line < ZAF_INDEPENDENT_DOUBLE_CLICK_DISTANCE &&
		column < ZAF_INDEPENDENT_DOUBLE_CLICK_DISTANCE)
	{
		lastClickTime = 0;
		doubleClicked = true;
	}
	else
	{
		lastClickTime = clickTime;
	 	lastClickPosition = position;
	}

	return (doubleClicked);
}

void ZafMouse::MoveMouse(int deltaX, int deltaY)
{
	if (!_devicePainting)
	{
		_devicePainting = true;
		position.column += deltaX;
		position.line += deltaY;

		if (position.column >= display->columns)
			position.column = display->columns - 1;
		else if (position.column < 0)
			position.column = 0;
		if (position.line >= display->lines)
			position.line = display->lines - 1;
		else if (position.line < 0)
			position.line = 0;

		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;
#	if defined(ZAF_GRAPHICS_MODE)
		clip.coordinateType = ZAF_PIXEL;
#	elif defined(ZAF_TEXT_MODE)
		clip.coordinateType = ZAF_CELL;
#	endif // ZAF_GRAPHICS_MODE

		((ZafScreenDisplay *)display)->DeviceMove(ZAF_IMT_CURSOR_IMAGE,
				display->columns, display->lines, clip);
		((ZafScreenDisplay *)display)->DeviceMove(ZAF_IMT_MOUSE_IMAGE,
				position.column - offset.column, position.line - offset.line, clip);
		_devicePainting = false;
	}
}

void ZafMouse::OpenMouse(void)
{
#	if defined(USE_UGL_INPUT)
	UGL_INPUT_Q_ID inputQ = InputQueueGet();
	// Serial mouse
	driver = ugl8250MsMouseDevCreate(inputQ, 4, 0x3f8, display->lines,
		display->columns);
	// PS/2 mouse
//	driver = uglPs2MouseDevCreate(inputQ, 12, 0x60, display->lines,
//		display->columns);
	if (driver)
		installed = true;
#	else
	installed = I_MouseOpen();	// Try to initialize the mouse driver.
	_mouse = this;
	mouseQueueEvent = mouseQueueEventFunc;
#	endif
}

void ZafMouse::TranslateMouseEvent(ZafEventStruct &event)
{
#	if !defined(USE_UGL_INPUT)
	if (++_tail >= MAX_EVENTS)
		_tail = 0;
	event.rawCode = sFifo[_tail];
	position.column += xFifo[_tail];
	position.line   += yFifo[_tail];
#	endif

	if (event.rawCode & S_SHIFT)
		event.modifiers |= S_SHIFT;
	if (event.rawCode & S_CTRL)
		event.modifiers |= S_CTRL;
	if (event.rawCode & S_ALT)
		event.modifiers |= S_ALT;
	event.rawCode &= ~(S_ALT | S_CTRL | S_SHIFT);
}

static void mouseQueueEventFunc(unsigned state, int *horizontalMickeys, int *verticalMickeys)
{
#if	!defined(USE_UGL_INPUT)
	static int i, deltaX, deltaY;

#	if defined(DOS32PP) || (defined(_MSC_VER) && defined(DOS386))
	// Accumulate mickey count if less than ratios.
	static int xAmount, yAmount;
	if (state == zState &&
		ZafAbs(*horizontalMickeys + xAmount) < _horizontalRatio &&
		ZafAbs(*verticalMickeys + yAmount) < _verticalRatio)
	{
		xAmount += *horizontalMickeys;
		yAmount += *verticalMickeys;
		return;
	}
	else
	{
		*horizontalMickeys += xAmount;
		*verticalMickeys += yAmount;
		xAmount = *horizontalMickeys % _horizontalRatio;
		yAmount = *verticalMickeys % _verticalRatio;
	}
#	endif // DOS32PP, etc. 
	deltaX = *horizontalMickeys / _horizontalRatio;
	deltaY = *verticalMickeys / _verticalRatio;

	i = _head + 1;
	if (i >= MAX_EVENTS)
		i = 0;
	if (i != _tail && (state != zState || deltaX || deltaY))
	{
		// Update the mickey count.
		*horizontalMickeys %= _horizontalRatio;
		*verticalMickeys %= _verticalRatio;

		if (state == zState && _head != _tail)
		{
			// Do an interrupt move of the mouse.
#	if defined (DOSX286) && !defined(_MSC_VER)
			// Check for running under OS2.
			if (!_isOS2 && !state  && (deltaX || deltaY))
			{
				_mouse->MoveMouse(deltaX, deltaY);
				return;
			}
#	elif defined (ZAF_INDEPENDENT) && !defined(_MSC_VER) && !defined(__WATCOMC__) && !defined(DOS32PP)
			if (!state  && (deltaX || deltaY))
			{
# 		if defined(DOS16M)
#			if defined(ZAF_GRAPHICS_MODE)
				_mouse->MoveMouse(deltaX, deltaY);
#			endif // ZAF_GRAPHICS_MODE
#		else
				_mouse->MoveMouse(deltaX, deltaY);
#		endif // DOS16M
				return;
			}
#	endif // DOSX286
			// Update existing event in the queue, don't add a new one.
			xFifo[_head] += deltaX;
			yFifo[_head] += deltaY;
			return;
		}
		_head = i;
		sFifo[i] = zState = state;
		xFifo[i] = deltaX;
		yFifo[i] = deltaY;
	}
#	endif
}

#endif // USE_ZAF_MOUSE
