#ifndef MOUSE_CPP
#define MOUSE_CPP

#include <core/inc.hpp>

static BYTE NtUserInjectMouseInput_Bytes [ 30 ];
inline const BYTE XOR_KEY = 0xACACACADDEDFFFA;

auto mouse::c_mouse::encrypt_function_bytes( BYTE* bytes, size_t size ) -> void 
{
	for ( size_t i = 0; i < size; ++i ) {
		bytes [ i ] ^= XOR_KEY;
	}
}

auto mouse::c_mouse::decrypt_function_bytes( BYTE* bytes, size_t size ) -> void 
{
	for ( size_t i = 0; i < size; ++i ) {
		bytes [ i ] ^= XOR_KEY;
	}
}

auto mouse::c_mouse::initialize_handler( ) -> bool
{
	LPVOID NtUserInjectMouseInput_Addr = GetProcAddress( GetModuleHandleA( hash_str( "win32u" ) ), hash_str( "NtUserInjectMouseInput" ) );
	if ( !NtUserInjectMouseInput_Addr )
	{
		NtUserInjectMouseInput_Addr = GetProcAddress( GetModuleHandleA( hash_str( "user32" ) ), hash_str( "NtUserInjectMouseInput" ) );
		if ( !NtUserInjectMouseInput_Addr )
		{
			NtUserInjectMouseInput_Addr = GetProcAddress( GetModuleHandleA( hash_str( "user32" ) ), hash_str( "InjectMouseInput" ) );
			if ( !NtUserInjectMouseInput_Addr )
				return false;
		}
	}

	memcpy( NtUserInjectMouseInput_Bytes, NtUserInjectMouseInput_Addr, 30 );
	this->encrypt_function_bytes( NtUserInjectMouseInput_Bytes, 30 );
	return true;
}

auto mouse::c_mouse::NtInjectMouse( InjectedInputMouseInfo* input, int count ) -> bool
{
	LPVOID NtUserInjectMouseInput_Spoof = VirtualAlloc( 0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
	if ( !NtUserInjectMouseInput_Spoof )
		return false;

	this->decrypt_function_bytes( NtUserInjectMouseInput_Bytes, 30 );
	memcpy( NtUserInjectMouseInput_Spoof, NtUserInjectMouseInput_Bytes, 30 );
	this->encrypt_function_bytes( NtUserInjectMouseInput_Bytes, 30 );


	NTSTATUS Result = reinterpret_cast< NTSTATUS( NTAPI* )( InjectedInputMouseInfo*, int ) >( NtUserInjectMouseInput_Spoof )( input, count );
	ZeroMemory( NtUserInjectMouseInput_Spoof, 0x1000 );
	VirtualFree( NtUserInjectMouseInput_Spoof, 0, MEM_RELEASE );
	return ( Result > 0 );
}

auto mouse::c_mouse::simulate_left_click( ) -> bool
{
	InjectedInputMouseInfo downInfo = { 0 };
	downInfo.DeltaX = 0;
	downInfo.DeltaY = 0;
	downInfo.MouseOptions = InjectedInputMouseOptions::left_down;

	if ( !this->NtInjectMouse( &downInfo, 1 ) )
		return false;

	InjectedInputMouseInfo upInfo = { 0 };
	upInfo.DeltaX = 0;
	upInfo.DeltaY = 0;
	upInfo.MouseOptions = InjectedInputMouseOptions::left_up;

	if ( !this->NtInjectMouse( &upInfo, 1 ) )
		return false;

	return true;
}

#endif // !MOUSE_CPP
