#include "stdafx.h"
#include "activitytracker.h"

#define BUFFERCOUNT 10

LPDIRECTINPUT  ActivityTracker::lpdi;            
GUID           ActivityTracker::MouseGUID;
GUID           ActivityTracker::KeyboardGUID;
BOOL           ActivityTracker::MouseFound;
BOOL           ActivityTracker::KeyboardFound;	

ActivityTracker::ActivityTracker(void)
{
	hDoneEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
	hMouseEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
	hKeyEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
}

ActivityTracker::~ActivityTracker(void)
{
	Stop();
	CloseHandle(hDoneEvent);
	CloseHandle(hMouseEvent);
	CloseHandle(hKeyEvent);
}

void ActivityTracker::Init(HINSTANCE hInst, HWND hWnd)
{
	hInstance = hInst;
	hMainWindow = hWnd;
	TimeOfLastMouse = time(0);
	TimeOfLastKey = time(0);

}

static DWORD WINAPI ThreadStart(LPVOID p)
{
	ActivityTracker *at = (ActivityTracker*)p;
	at->Loop();
	return 1;
}

void ActivityTracker::Start()
{
	done = 0;
	CreateThread(NULL,0,ThreadStart,this,0,NULL);
}

void ActivityTracker::Stop()
{
	done = 1;
	SetEvent(hDoneEvent);
	Sleep(0);
}

int ActivityTracker::GetTimeSinceLastEvent()
{
	time_t now = time(0);
	if(TimeOfLastMouse > TimeOfLastKey)
		return now-TimeOfLastMouse;
	else
		return now-TimeOfLastKey;
}

void ActivityTracker::Loop()
{
	if(!InitInput()){
		print(L"Unable to initialize Activity Tracker\n");
		return;
	}

	HANDLE events[3];
	events[0] = hDoneEvent;
	events[1] = hMouseEvent;
	events[2] = hKeyEvent;

	while(!done){
		switch(WaitForMultipleObjects(3,events,FALSE,INFINITE)-WAIT_OBJECT_0){
			case 0://done
				break;
			case 1: //mouse
				TimeOfLastMouse = time(0);
				//print(L"mouse\n");
				break;
			case 2://keyboard
				TimeOfLastKey = time(0);
				//print(L"key\n");
				break;
		}
	}

	CleanupInput();
}

// the following methods were adopted from Microsoft's
// dinput.exe sample

/* **********************************************************
DIEnumDevicesProc()
Callback function required by EnumDevices
*********************************************************** */

BOOL CALLBACK ActivityTracker::DIEnumDevicesProc(LPCDIDEVICEINSTANCE lpddi, 
														LPVOID pvRef)
{
	if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DIDEVTYPE_MOUSE)
	{
		MouseGUID = lpddi->guidProduct;   
		MouseFound = TRUE;
	}
	if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DIDEVTYPE_KEYBOARD)
	{  
		KeyboardGUID = lpddi->guidProduct;   
		KeyboardFound = TRUE;
	}
	if (MouseFound && KeyboardFound)
		return DIENUM_STOP;
	else
		return DIENUM_CONTINUE;  
} // DIEnumDevicesProc



/* **********************************************************
InitInput()
Initialize DirectInput objects & devices
************************************************************ */

BOOL ActivityTracker::InitInput(void)
{
	DIPROPDWORD dipdw;
	HRESULT     hres;

	// try to create DirectInput object
	if(DirectInputCreate(hInstance, DIRECTINPUT_VERSION, &lpdi, NULL) != DI_OK)
	{
		print(L"Failed to create DirectInput object.\n");
		return FALSE;
	}

	// enumerate devices so we can get the GUIDs
	if (lpdi->EnumDevices(0, 
		DIEnumDevicesProc,
		NULL, 
		DIEDFL_ATTACHEDONLY) != DI_OK)
	{
		print(L"Could not enumerate devices.\n");
		return FALSE;
	}
	if (!MouseFound)
	{
		print(L"Could not find a mouse.\n");
		return FALSE;
	}
	if (!KeyboardFound)
	{
		print(L"Could not find a keyboard.\n");
		return FALSE;
	}

	// create mouse device
	if(lpdi->CreateDevice(MouseGUID, &lpdiMouse, NULL) !=DI_OK)
	{
		print(L"Failed to create mouse device.\n");
		return FALSE;
	}

	// set mouse cooperative level. Non-exclusive won't work on NT!
	if (lpdiMouse->SetCooperativeLevel(hMainWindow,
		DISCL_BACKGROUND | DISCL_NONEXCLUSIVE) != DI_OK)
	{
		//PrintLastError("ActivityTracker");
		//print(L"Failed to set mouse cooperative level.\n");
		//return FALSE;
	}

	// set mouse buffer size
	dipdw.diph.dwSize = sizeof(DIPROPDWORD);
	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	dipdw.diph.dwObj = 0;
	dipdw.diph.dwHow = DIPH_DEVICE;
	dipdw.dwData = BUFFERCOUNT;
	hres = lpdiMouse->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph);
	if (hres != DI_OK) 
	{
		print(L"Failed to set mouse buffer size.\n");
		return FALSE;
	}

	// Set data format. Must be done before mouse can be acquired.
	//   Note: c_dfDIMouse is an external DIDATAFORMAT structure supplied
	//   by DirectInput. Default values are:
	//          dwSize      24
	//          dwObjSize   16
	//          dwFlags      2    = DIDF_RELAXIS
	//          dwDataSize  16
	//          dwNumObjs    7    (3 axes and 4 buttons)
	if (lpdiMouse->SetDataFormat(&c_dfDIMouse) != DI_OK)
	{
		print(L"Failed to set mouse data format.\n");
		return FALSE;
	}

	lpdiMouse->SetEventNotification(hMouseEvent);

	// try to acquire the mouse
	if (lpdiMouse->Acquire() != DI_OK)
	{
		print(L"Failed to acquire the mouse.\n");
		return FALSE;
	}

	MouseAcquired = TRUE;


	/* The Keyboard */

	// try to create keyboard device
	if(lpdi->CreateDevice(KeyboardGUID, &lpdiKeyboard, NULL) !=DI_OK)
	{
		print(L"Failed to create keyboard device.\n");
		return FALSE;
	}

	// set kbd cooperative level
	if (lpdiKeyboard->SetCooperativeLevel(hMainWindow,
		DISCL_BACKGROUND | DISCL_NONEXCLUSIVE) != DI_OK)
	{
		//print(L"Failed to set keyboard cooperative level.\n");
		//return FALSE;
	}

	// set kbd data format
	if (lpdiKeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK)
	{
		print(L"Failed to set keyboard data format.\n");
		return FALSE;
	}

	// set kbd buffer size
	dipdw.diph.dwSize = sizeof(DIPROPDWORD);
	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	dipdw.diph.dwObj = 0;
	dipdw.diph.dwHow = DIPH_DEVICE;
	dipdw.dwData = BUFFERCOUNT; // * sizeof(DIDEVICEOBJECTDATA);
	hres = lpdiKeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph);
	if (hres != DI_OK) 
	{
		print(L"Failed to set keyboard buffer size.\n");
		return FALSE;
	}

	lpdiKeyboard->SetEventNotification(hKeyEvent);

	// try to acquire the keyboard
	if (lpdiKeyboard->Acquire() != DI_OK)
	{
		print(L"Failed to acquire the keyboard.\n");
		return FALSE;
	}

	KeyboardAcquired = TRUE;

	return TRUE;    
}  // InitInput()


/* **********************************************
CleanupInput()
Cleans up DirectInput objects
************************************************ */
void ActivityTracker::CleanupInput(void)
{
	if(MouseAcquired)
	{
		lpdiMouse->Unacquire();
		MouseAcquired = FALSE;
	}
	if(lpdiMouse != NULL) lpdiMouse->Release();

	if(KeyboardAcquired)
	{
		lpdiKeyboard->Unacquire();
		KeyboardAcquired = FALSE;
	}

	if(lpdiKeyboard != NULL) lpdiKeyboard->Release();

	if(lpdi!= NULL) lpdi->Release();
} // CleanupInput()


/* ********************************************************
ReacquireInputDevices
Needs to be called whenever we have lost a device 
****************************************************** */

BOOL ActivityTracker::ReacquireInputDevices(void)
{
	// try to acquire the Mouse
	if(lpdiMouse != NULL)
	{
		lpdiMouse->Acquire();
	}
	else
	{
		// mouse device has not been created
		return FALSE;
	}
	// if we get here, we are acquired again
	MouseAcquired = TRUE;

	// now the keyboard
	if(lpdiKeyboard != NULL)
	{
		//  lpdiKeyboard->Acquire();
	}
	else
	{
		// keyboard device has not been created.
		return FALSE;
	}
	// if we get here, we are acquired again
	KeyboardAcquired = TRUE;

	return TRUE;
}  // ReacquireInputDevices()
