#include "pch.hpp"
//===========================================================================//
// File:	 Mouse.cpp														 //
// Project:  GameOS					                                         //
// Contents: Mouse routines													 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 05/29/98  AG		Created													 //
// 06/25/98  JTR	Mouse keypressed not buffered, ack! Must fix!			 //
// 09/18/98  JTR	Added SetMousePosition									 //
//																			 //
//---------------------------------------------------------------------------// 
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "ControlManager.hpp"
#include "DirectX.hpp"
#include "Windows.hpp"
#include "ErrorHandler.hpp"
#include "Debugger.hpp"


extern bool gActive;
extern bool gGotFocus;


bool MouseWheel;			// TRUE if mouse wheel is present
bool SwapedButtons;
int XDelta,YDelta,WheelDelta;
int MouseGranularity;
float XPosition,YPosition, WheelPosition;
int XPositionInternal, YPositionInternal;
DWORD ButtonsPressed;
int MouseSpeed;
float OldXP=0,OldYP=0;
DWORD OldBP=0;


bool MovedMouse=0;
float MovedMouseX,MovedMouseY;





//
// Set the current windows mouse position
//
void __stdcall gos_SetMousePosition( float XPosition, float YPosition )
{
	SPEW(( GROUP_GAMEOS, "gos_SetMousePosition()" ));
	gosASSERT( XPosition>=0.0f && XPosition<=1.0f && YPosition>=0.0f && YPosition<=1.0f );
	MovedMouse=1;
	MovedMouseX=XPosition;
	MovedMouseY=YPosition;
}



//
// The mouse position and buttons are read once a frame.
//
// Position returns values converted into position on window. Range is 0.0 to 1.0
// When running in a window this may not match the sum of the deltas, as the windows mouse cursor can have acceleration
//
//
// Deltas returns values as a percentage of screen size. Range is -1.0 to 1.0
//
// ie: 0.5 means half a screen to the right or down
//
// The wheel position is only available as a delta value, Range is -1.0 to 1.0 (999.0 means no wheel)
//
//
// ButtonsPressed are bitflags, 0=Left Button, 1=Right, 2=Middle if present
//
void __stdcall gos_GetMouseInfo( float* pXPosition, float* pYPosition, int* pXDelta, int* pYDelta, int* pWheelDelta, DWORD* pButtonsPressed )
{
	if( 1==LogMode )
	{
//
// Log file playing back
//
		if( InDebugger )
		{
			if( pXPosition )
				*pXPosition=XPosition;
			if( pYPosition )
				*pYPosition=YPosition;
			if( pXDelta )
				*pXDelta=XDelta;
			if( pYDelta )
				*pYDelta=YDelta;
			if( pWheelDelta )
				*pWheelDelta=WheelDelta;
			if( pButtonsPressed )
				*pButtonsPressed=ButtonsPressed;
			return;
		}
		if( pXPosition )
			*pXPosition=LogInfo.Current->MouseX;
		if( pYPosition )
			*pYPosition=LogInfo.Current->MouseY;
		if( pXDelta )
			*pXDelta=LogInfo.Current->pXDelta;
		if( pYDelta )
			*pYDelta=LogInfo.Current->pYDelta;
		if( pWheelDelta )
			*pWheelDelta=LogInfo.Current->pWheelDelta;
		if( pButtonsPressed )
			*pButtonsPressed=LogInfo.Current->pButtonsPressed;
		return;
	}
//
// Log file recording
//
	else
	{
		if( DebuggerActive && !InDebugger && !gControlsActive )
		{
			if( pXPosition )
				*pXPosition=OldXP;
			if( pYPosition )
				*pYPosition=OldYP;
			if( pXDelta )					// Do not move mouse in debugger
				*pXDelta=0;
			if( pYDelta )
				*pYDelta=0;
			if( pWheelDelta )
				*pWheelDelta=0;
			if( pButtonsPressed )
				*pButtonsPressed=OldBP;
			return;
		}
		else
		{
			if( pXPosition )
				*pXPosition=XPosition;
			if( pYPosition )
				*pYPosition=YPosition;
			if( pXDelta )
				*pXDelta=XDelta;
			if( pYDelta )
				*pYDelta=YDelta;
			if( pWheelDelta )
				*pWheelDelta=WheelDelta;
			if( pButtonsPressed )
				*pButtonsPressed=ButtonsPressed;
			if( !InDebugger )
			{
				OldXP=XPosition;
				OldYP=YPosition;
				OldBP=ButtonsPressed;
			}
		}
	}
}









// Create and initialize the DI mouse and put all necessary information regarding it
// into the control manager data structure.
void CMCreateMouse()
{
	SPEW(( GROUP_GAMEOS, "CMCreateMouse()" ));
//
// Create the mouse device
//
    if (!GetSystemMetrics(SM_MOUSEPRESENT))
        return;
    if (wCreateDeviceEx( g_CMData.m_lpDirectInput7, GUID_SysMouse,IID_IDirectInputDevice7, (void **) &g_CMData.m_lpSysMouse7, NULL ) != DI_OK) {
        return;
    }
//
// Setup the cooperative level  (from the top level window)
//
	HWND tempHW=hWindow;
	while( GetParent( tempHW ) )
		tempHW=GetParent( tempHW );
	// [editor modern-Windows fix] In the hosted editor, GameOS is initialized before the
	// viewport window exists (CGameFrame::OnCreate hasn't run), so hWindow is NULL. Modern
	// DirectInput (Vista+) rejects SetCooperativeLevel with a NULL window (returns E_HANDLE
	// 0x80070006), which XP-era DirectInput tolerated -> the resulting PAUSE was fatal and
	// aborted editor startup. Fall back to a valid top-level window with background/non-
	// exclusive so init succeeds; the editor re-targets input when the real viewport shows.
	// Game path is unchanged: hWindow is valid there, so tempHW is non-NULL.
	if( tempHW==NULL )
	{
		tempHW = GetForegroundWindow();
		if( tempHW==NULL )
			tempHW = GetDesktopWindow();
		wSetCooperativeLevel(g_CMData.m_lpSysMouse7, tempHW, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
	}
	else
		wSetCooperativeLevel(g_CMData.m_lpSysMouse7, tempHW, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
//
// Get the device caps
//
	DIDEVCAPS dcaps;
	memset(&dcaps,0,sizeof(dcaps));
	dcaps.dwSize = sizeof(dcaps);
	wGetCapabilities(g_CMData.m_lpSysMouse7, &dcaps);

	g_CMData.m_numMouseAxes = (BYTE)dcaps.dwAxes;
	g_CMData.m_numMouseButtons = (BYTE) dcaps.dwButtons;
//
// Setup the data format
//
	wSetDataFormat(g_CMData.m_lpSysMouse7, &c_dfDIMouse);
//
// Find the number of buttons and if a wheel is present
//
	MouseWheel=(g_CMData.m_numMouseAxes > 2);

	if (MouseWheel)
	{
		DIPROPDWORD dipdw;
		dipdw.diph.dwSize = sizeof(dipdw);
		dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
		dipdw.diph.dwObj = DIMOFS_Z;
		dipdw.diph.dwHow = DIPH_BYOFFSET;
		g_CMData.m_lpSysMouse7->GetProperty(DIPROP_GRANULARITY,&dipdw.diph);
		MouseGranularity = dipdw.dwData;
		if( 0==MouseGranularity )
			MouseGranularity=1;
	}
//
// Get absolute mouse position using window calls
//
	POINT pt;
	GetCursorPos(&pt);
	XPositionInternal = pt.x;
	YPositionInternal = pt.y;
//
// Make coords an offset from the window top left
//
	if( Environment.fullScreen )
	{
		RECT WinRect;
		GetWindowRect( hWindow, &WinRect );
		pt.x-=WinRect.left;
		pt.y-=WinRect.top;
	}
	else
	{
		pt.x -= clientToScreen.x;
		pt.y -= clientToScreen.y;
	}
//
// Find out if mouse buttons have been swapped
//
	SwapedButtons=0!=GetSystemMetrics(SM_SWAPBUTTON);
//
// Clamp mouse position to window and make into floats 0.0 to 1.0
//
	XPosition=(pt.x<0) ? 0.0f : (pt.x>=Environment.screenWidth-1) ? 1.0f : (float)pt.x/(Environment.screenWidth-1);
	YPosition=(pt.y<0) ? 0.0f : (pt.y>=Environment.screenHeight-1) ? 1.0f : (float)pt.y/(Environment.screenHeight-1);
//
// Init other mouse variables
//
	MovedMouse=0;
	g_CMData.m_requiresAcquire=true;			// Make sure acquired before used
}











//
// Called once each loop, update all the mouse variables


//
//
//
void  CMUpdateMouse() 
{ 
	SPEW(( GROUP_GAMEOS, "CMUpdateMouse()" ));
//
// Do not update position if not active
//
	if( !gGotFocus || !gActive )
		return;
	if( !g_CMData.m_lpSysMouse7 )
	{
        MovedMouse=0;
        ButtonsPressed = 0;
		MouseInWindow=0;
	    XPosition=0.0;
	    YPosition=0.0;
		WheelDelta=0;
		XDelta=0;
		YDelta=0;
        return;
	}
//
// Has mouse been moved? Update it.
//
	if( MovedMouse )
	{
		MovedMouse=0;
		SetCursorPos( (int)(clientToScreen.x+(Environment.screenWidth-1)*MovedMouseX), (int)(clientToScreen.y+(Environment.screenHeight-1)*MovedMouseY ));
	}
//
// Get absolute mouse position using window calls
//
	POINT pt;
	GetCursorPos(&pt);
//
// Make coords an offset from the window top left
//
	if( Environment.fullScreen )
	{
		RECT WinRect;
		GetWindowRect( hWindow, &WinRect );
		pt.x-=WinRect.left;
		pt.y-=WinRect.top;
	}
	else
	{
		pt.x -= clientToScreen.x;
		pt.y -= clientToScreen.y;
	}
//
// Clamp mouse position to window and make into floats 0.0 to 1.0
//
	MouseInWindow=0;
	if( pt.x<0 )
	{
		MouseInWindow=0-pt.x;
		pt.x=0;
	}
	else if( pt.x>=Environment.screenWidth-1 )
	{
		if( pt.x-(Environment.screenWidth-1) > MouseInWindow )
			MouseInWindow=pt.x-(Environment.screenWidth-1);
		pt.x=Environment.screenWidth-1;
	}
	if( pt.y<0 )
	{
		if( 0-pt.y > MouseInWindow )
			MouseInWindow=0-pt.y;
		pt.y=0;
	}
	else if( pt.y>=Environment.screenHeight-1 )
	{
		if( pt.y-(Environment.screenHeight-1) > MouseInWindow )
			MouseInWindow=pt.y-(Environment.screenHeight-1);
		pt.y=Environment.screenHeight-1;
	}
	XPosition=(float)((pt.x+0.5)/(Environment.screenWidth-1));
	YPosition=(float)((pt.y+0.5)/(Environment.screenHeight-1));
	if( XPosition>=1.0f )
		XPosition=1.0f;
	if( YPosition>=1.0f )
		YPosition=1.0f;
//
// Get relative mouse movement information
//
	{
		DIMOUSESTATE dims;
		AllowFail=1;
		HRESULT result;
		result=wGetDeviceState( g_CMData.m_lpSysMouse7, sizeof(DIMOUSESTATE), &dims );
		AllowFail=0;
		if( FAILED(result) )
		{
			g_CMData.m_requiresAcquire=true;
			return;
		}
//
// Get mouse buttons
//
		if( SwapedButtons )
			ButtonsPressed=(GetStatus(KEY_RMOUSE) ? 1:0) | (GetStatus(KEY_LMOUSE) ? 2:0) | (GetStatus(KEY_MMOUSE) ? 4:0) | (GetStatus(KEY_MOUSEX1) ? 8:0) | (GetStatus(KEY_MOUSEX2) ? 16:0);
		else
			ButtonsPressed=(GetStatus(KEY_LMOUSE) ? 1:0) | (GetStatus(KEY_RMOUSE) ? 2:0) | (GetStatus(KEY_MMOUSE) ? 4:0) | (GetStatus(KEY_MOUSEX1) ? 8:0) | (GetStatus(KEY_MOUSEX2) ? 16:0);
//
// Get wheel delta information
//
		if( MouseWheel )
		{
			if( dims.lZ%120==0 )						// Divisible by 120?
			{
				WheelDelta=dims.lZ/120;					// Assume granularity is incorrect returned by DirectX and use 120
			}
			else
			{
				WheelDelta=dims.lZ/-MouseGranularity;	// Else trust DirectX
			}
		}
		else
		{
			WheelDelta=0;
		}
//
// Get X and Y mouse deltas
//
		XDelta=dims.lX;
		YDelta=dims.lY;
	}
} 
