/*
    AdvancedAutoAway Plugin for Miranda-IM (www.miranda-im.org)
    Copyright 2003-2006 P. Boon

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	
	Some code is copied from Miranda's AutoAway module
*/
#include "../commonstatus.h"
#include "advancedautoaway.h"
#include "../resource.h"
#include <commctrl.h>

#ifdef _DEBUG
	#define SECS_PER_MINUTE		20 /* speedup */
#else
	#define SECS_PER_MINUTE		60 /* default I believe */
#endif

/////////////////////////////////////////////////////////////////////////////////////////

int CompareSettings( const TAAAProtoSetting* p1, const TAAAProtoSetting* p2 )
{
	return lstrcmpA( p1->szName, p2->szName );
}

OBJLIST<TAAAProtoSetting> *autoAwaySettings;

TAAAProtoSetting::TAAAProtoSetting( PROTOACCOUNT* pa )
{
	cbSize = sizeof(PROTOCOLSETTINGEX);
	szName =  pa->szModuleName;
	tszAccName = pa->tszAccountName;
	lastStatus = status = originalStatusMode = ID_STATUS_CURRENT;
	szMsg = NULL;
	curState = ACTIVE;
	mStatus = FALSE;
}

TAAAProtoSetting::~TAAAProtoSetting()
{
	if ( szMsg )
		free( szMsg );
}

/////////////////////////////////////////////////////////////////////////////////////////

typedef HDESK (WINAPI* pfnOpenInputDesktop)( DWORD, BOOL, DWORD );
static pfnOpenInputDesktop openInputDesktop = NULL;
typedef HDESK (WINAPI* pfnCloseDesktop)( HDESK );
static pfnCloseDesktop closeDesktop = NULL;

extern HANDLE hStateChangedEvent;

static BOOL ignoreLockKeys = FALSE;
static BOOL ignoreSysKeys = FALSE;
static BOOL ignoreAltCombo = FALSE;
static BOOL monitorMouse = TRUE;
static BOOL monitorKeyboard = TRUE;
static HWND confirmDialog;
static HANDLE hAutoAwayOptionsHook;
static HANDLE hAutoAwayShutDownHook;
static HANDLE hProtoAckHook;
static HANDLE hAccChangeHook;
static int mouseStationaryTimer;
HHOOK hMirandaMouseHook = NULL;
HHOOK hMirandaKeyBoardHook = NULL;
#pragma data_seg("Shared")
DWORD lastInput = 0;
POINT lastMousePos = {0};
HHOOK hMouseHook = NULL;
HHOOK hKeyBoardHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")
DWORD lastMirandaInput = 0;
static UINT_PTR hAutoAwayTimer;
// prototypes
extern DWORD StatusModeToProtoFlag(int status);
extern int InitCommonStatus();
static BOOL (WINAPI * MyGetLastInputInfo)(PLASTINPUTINFO);
void LoadOptions(TAAAProtoSetting** loadSettings, BOOL override);
static int HookWindowsHooks(int hookMiranda, int hookAll);
static int UnhookWindowsHooks();
static LRESULT CALLBACK MouseHookFunction(int code, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK KeyBoardHookFunction(int code, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK MirandaMouseHookFunction(int code, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK MirandaKeyBoardHookFunction(int code, WPARAM wParam, LPARAM lParam);
static BOOL IsSaverRunning();
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved);

static VOID CALLBACK AutoAwayTimer(HWND hwnd,UINT message,UINT_PTR idEvent,DWORD dwTime);
extern int AutoAwayOptInitialise(WPARAM wParam,LPARAM lParam);
extern int AutoAwayMsgOptInitialise(WPARAM wParam,LPARAM lParam);
extern int SetStatus(WPARAM wParam, LPARAM lParam);
extern int ShowConfirmDialog(WPARAM wParam, LPARAM lParam);
extern char *StatusModeToDbSetting(int status,const char *suffix);

/////////////////////////////////////////////////////////////////////////////////////////
// Load from DB

void LoadOptions( OBJLIST<TAAAProtoSetting>& loadSettings, BOOL override)
{
	// if override is enabled, samesettings will be ignored (for options loading)
	int monitorMiranda = FALSE; // use windows hooks?
	int monitorAll = FALSE; // use windows hooks?

	if (!override)
		UnhookWindowsHooks();
	if (hAutoAwayTimer != 0)
		KillTimer(NULL, hAutoAwayTimer);

	ignoreLockKeys = db_get_b(NULL, MODULENAME, SETTING_IGNLOCK, FALSE);
	ignoreSysKeys = db_get_b(NULL, MODULENAME, SETTING_IGNSYSKEYS, FALSE);
	ignoreAltCombo = db_get_b(NULL, MODULENAME, SETTING_IGNALTCOMBO, FALSE);
	monitorMouse = db_get_b(NULL, MODULENAME, SETTING_MONITORMOUSE, TRUE);
	monitorKeyboard = db_get_b(NULL, MODULENAME, SETTING_MONITORKEYBOARD, TRUE);
	lastInput = lastMirandaInput = GetTickCount();

	for ( int i=0; i < loadSettings.getCount(); i++ ) {
		char* protoName;
		if ((db_get_b(NULL, MODULENAME, SETTING_SAMESETTINGS, 0)) && !override)
			protoName = SETTING_ALL;
		else
			protoName = loadSettings[i].szName;
		LoadAutoAwaySetting( loadSettings[i], protoName);
		if (!override) {
			if (loadSettings[i].optionFlags & FLAG_MONITORMIRANDA)
				monitorMiranda = TRUE;
			else if ( (MyGetLastInputInfo==NULL) || ignoreLockKeys || ignoreSysKeys || ignoreAltCombo || (monitorMouse != monitorKeyboard))
				monitorAll = TRUE;
	}	}

	if (db_get_b(NULL, "Idle", "AAEnable", 0))
		return;

	HookWindowsHooks(monitorMiranda, monitorAll);
	hAutoAwayTimer = SetTimer(NULL,0, db_get_w(NULL, MODULENAME, SETTING_AWAYCHECKTIMEINSECS, 5)*1000,AutoAwayTimer);
}

int LoadAutoAwaySetting(TAAAProtoSetting& autoAwaySetting, char* protoName)
{
	char setting[128];
	_snprintf(setting, sizeof(setting), "%s_OptionFlags", protoName);
	autoAwaySetting.optionFlags = db_get_b(NULL,MODULENAME,setting,FLAG_LV2ONINACTIVE|FLAG_RESET);
	_snprintf(setting, sizeof(setting), "%s_AwayTime", protoName);
	autoAwaySetting.awayTime = db_get_w(NULL,MODULENAME,setting,SETTING_AWAYTIME_DEFAULT);
	_snprintf(setting, sizeof(setting), "%s_NATime", protoName);
	autoAwaySetting.naTime = db_get_w(NULL,MODULENAME,setting,SETTING_NATIME_DEFAULT);
	_snprintf(setting, sizeof(setting), "%s_StatusFlags", protoName);
	autoAwaySetting.statusFlags = db_get_w(NULL,MODULENAME,setting, StatusModeToProtoFlag(ID_STATUS_ONLINE)|StatusModeToProtoFlag(ID_STATUS_FREECHAT));

	int flags;
	if (db_get_b(NULL, MODULENAME, SETTING_SAMESETTINGS, 0))
		flags = 0xFFFFFF;
	else
		flags = CallProtoService(protoName, PS_GETCAPS,PFLAGNUM_2,0)&~CallProtoService(protoName, PS_GETCAPS, (WPARAM)PFLAGNUM_5, 0);
	_snprintf(setting, sizeof(setting), "%s_Lv1Status", protoName);
	autoAwaySetting.lv1Status = db_get_w(NULL, MODULENAME, setting, (flags&StatusModeToProtoFlag(ID_STATUS_AWAY))?ID_STATUS_AWAY:ID_STATUS_OFFLINE);
	_snprintf(setting, sizeof(setting), "%s_Lv2Status", protoName);
	autoAwaySetting.lv2Status = db_get_w(NULL, MODULENAME, setting, (flags&StatusModeToProtoFlag(ID_STATUS_NA))?ID_STATUS_NA:ID_STATUS_OFFLINE);

	return 0;
}

static int ProcessProtoAck(WPARAM wParam,LPARAM lParam)
{
	ACKDATA *ack = ( ACKDATA* )lParam;
	if ( ack->type != ACKTYPE_STATUS )
		return 0;
	if ( ack->result != ACKRESULT_SUCCESS )
		return 0;
	log_debugA("ProcessProtoAck: ack->szModule: %s", ack->szModule);
	for ( int i=0; i < autoAwaySettings->getCount(); i++ ) {
		log_debugA("chk: %s", (*autoAwaySettings)[i].szName);
		if (!strcmp((*autoAwaySettings)[i].szName, ack->szModule)) {
			log_debugA("ack->szModule: %s (*autoAwaySettings)[i].statusChanged: %d", ack->szModule, (*autoAwaySettings)[i].statusChanged);
			if (!(*autoAwaySettings)[i].statusChanged)
				(*autoAwaySettings)[i].mStatus = TRUE;

			(*autoAwaySettings)[i].statusChanged = FALSE;
	}	}

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Account control event

int OnAccChanged(WPARAM wParam,LPARAM lParam)
{
	PROTOACCOUNT* pa = ( PROTOACCOUNT* )lParam;
	switch( wParam ) {
	case PRAC_ADDED:
		autoAwaySettings->insert( new TAAAProtoSetting( pa ));
		break;
		
	case PRAC_REMOVED:
		{
			for ( int i=0; i < autoAwaySettings->getCount(); i++ ) {
				if ( !lstrcmpA( (*autoAwaySettings)[i].szName, pa->szModuleName )) {
					autoAwaySettings->remove( i );
					break;
		}	}	}
		break;
	}

	return 0;
}

/* this function is from the original auto-away module */
static BOOL IsWorkstationLocked (void)
{
	BOOL rc = FALSE;

	if (openInputDesktop != NULL) {
		HDESK hDesk = openInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
		if (hDesk == NULL)
			rc = TRUE;
		else if (closeDesktop != NULL)
			closeDesktop(hDesk);
	}
	return rc;
}

static char* status2descr( int status )
{
	switch( status ) {
		case ACTIVE:        return "ACTIVE";
		case STATUS1_SET:   return "STATUS1_SET";
		case STATUS2_SET:   return "STATUS2_SET";
		case SET_ORGSTATUS: return "SET_ORGSTATUS";
		case HIDDEN_ACTIVE: return "HIDDEN_ACTIVE";
	}
	return "ERROR";
}

static int changeState(TAAAProtoSetting& setting, STATES newState)
{
	if (setting.curState == newState)
		return 0;

	setting.oldState = setting.curState;
	setting.curState = newState;

	log_debugA("%s state change: %s -> %s", setting.szName, 
		status2descr(setting.oldState), status2descr(setting.curState));

	NotifyEventHooks(hStateChangedEvent, 0, (LPARAM)(AUTOAWAYSETTING*)&setting);
	if ( setting.curState != SET_ORGSTATUS && setting.curState != ACTIVE && setting.statusChanged ) {
		/* change the awaymessage */
		if (setting.szMsg != NULL) {
			free(setting.szMsg);
			setting.szMsg = NULL;
		}

		if (db_get_b(NULL, MODULENAME, StatusModeToDbSetting(setting.status, SETTING_MSGCUSTOM), FALSE)) {
			DBVARIANT dbv;
			if (!db_get_ts(NULL, MODULENAME, StatusModeToDbSetting(setting.status, SETTING_STATUSMSG), &dbv)) {
				setting.szMsg = _tcsdup(dbv.ptszVal);
				db_free(&dbv);
		}	}
	}
	else if (setting.szMsg != NULL) {
		free(setting.szMsg);
		setting.szMsg = NULL;
	}
	
	return 0;
}

static VOID CALLBACK AutoAwayTimer(HWND hwnd,UINT message,UINT_PTR idEvent,DWORD dwTime)
{	
	int statusChanged = FALSE;
	int confirm = FALSE;

	for ( int i=0; i < autoAwaySettings->getCount(); i++ ) {
		TAAAProtoSetting& aas = (*autoAwaySettings)[i];
		aas.status = ID_STATUS_DISABLED;

		BOOL screenSaver = FALSE, locked = FALSE;

		if ( aas.optionFlags & FLAG_MONITORMIRANDA )
			mouseStationaryTimer = (GetTickCount() - lastMirandaInput)/1000;
		else {
			if (MyGetLastInputInfo!=NULL) {
				LASTINPUTINFO lii = { 0 };
				lii.cbSize = sizeof(lii);
				MyGetLastInputInfo(&lii);
				mouseStationaryTimer = (GetTickCount()-lii.dwTime)/1000;
			}
			else mouseStationaryTimer = (GetTickCount() - lastInput)/1000;
		}

		int sts1Time = aas.awayTime * SECS_PER_MINUTE;
		int sts2Time = aas.naTime * SECS_PER_MINUTE;
		int sts1setTime = aas.sts1setTimer==0?0:(GetTickCount() - aas.sts1setTimer)/1000;
		int currentMode = CallProtoService(aas.szName,PS_GETSTATUS,0, 0);
		if ( aas.optionFlags & FLAG_ONSAVER )
			SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &screenSaver, FALSE);

		if (aas.optionFlags & FLAG_ONLOCK)
			locked = IsWorkstationLocked();
	
		/* check states */
		if (aas.curState == ACTIVE) {
			if ((( mouseStationaryTimer >= sts1Time && (aas.optionFlags & FLAG_ONMOUSE)) || screenSaver || locked ) && currentMode != aas.lv1Status && aas.statusFlags&StatusModeToProtoFlag(currentMode)) {
				/* from ACTIVE to STATUS1_SET */
				aas.lastStatus = aas.originalStatusMode = CallProtoService(aas.szName,PS_GETSTATUS,0, 0);
				aas.status = aas.lv1Status;
				aas.sts1setTimer = GetTickCount();
				sts1setTime = 0;
				aas.statusChanged = statusChanged = TRUE;
				changeState(aas, STATUS1_SET);
			}
			else if ( mouseStationaryTimer >= sts2Time && currentMode == aas.lv1Status && currentMode != aas.lv2Status && (aas.optionFlags & FLAG_SETNA) && (aas.statusFlags & StatusModeToProtoFlag(currentMode))) {
				/* from ACTIVE to STATUS2_SET */
				aas.lastStatus = aas.originalStatusMode = CallProtoService(aas.szName,PS_GETSTATUS,0, 0);
				aas.status = aas.lv2Status;
				aas.statusChanged = statusChanged = TRUE;
				changeState(aas, STATUS2_SET);
		}	}

		if (aas.curState == STATUS1_SET) {
			if (( mouseStationaryTimer < sts1Time && !screenSaver && !locked ) && !(aas.optionFlags & FLAG_RESET )) {
				/* from STATUS1_SET to HIDDEN_ACTIVE */
					changeState(aas, HIDDEN_ACTIVE);
					aas.lastStatus = CallProtoService(aas.szName,PS_GETSTATUS,0, 0);
			}
			else if ( ((mouseStationaryTimer < sts1Time) && (!screenSaver) && (!locked)) && 
				((aas.optionFlags&FLAG_LV2ONINACTIVE) || (!(aas.optionFlags&FLAG_SETNA))) &&
				(aas.optionFlags&FLAG_RESET)) {
				/* from STATUS1_SET to SET_ORGSTATUS */
				changeState(aas, SET_ORGSTATUS);
			}
			else if (( aas.optionFlags & FLAG_SETNA) && sts1setTime >= sts2Time ) {
				/* when set STATUS2, currentMode doesn't have to be in the selected status list (statusFlags) */
				/* from STATUS1_SET to STATUS2_SET */
				aas.lastStatus = CallProtoService(aas.szName,PS_GETSTATUS,0, 0);
				aas.status = aas.lv2Status;
				aas.statusChanged = statusChanged = TRUE;
				changeState(aas, STATUS2_SET);
		}	}

		if ( aas.curState == STATUS2_SET ) {
			if ( mouseStationaryTimer < sts2Time && !screenSaver && !locked && ( aas.optionFlags & FLAG_RESET )) {
				/* from STATUS2_SET to SET_ORGSTATUS */
				changeState(aas, SET_ORGSTATUS);
			}
			else if ( mouseStationaryTimer < sts2Time && !screenSaver && !locked && !( aas.optionFlags & FLAG_RESET )) {
				/* from STATUS2_SET to HIDDEN_ACTIVE */
				/* Remember: after status1 is set, and "only on inactive" is NOT set, it implies !reset. */
				changeState(aas, HIDDEN_ACTIVE);
				aas.lastStatus = CallProtoService(aas.szName,PS_GETSTATUS,0, 0);
		}	}

		if ( aas.curState == HIDDEN_ACTIVE ) {
			if ( aas.mStatus ) {
				/* HIDDEN_ACTIVE to ACTIVE */
				//aas.statusChanged = FALSE;
				changeState(aas, ACTIVE);
				aas.sts1setTimer = 0;
				aas.mStatus = FALSE;
			}
			else if (( aas.optionFlags & FLAG_SETNA ) && currentMode == aas.lv1Status &&
				currentMode != aas.lv2Status && (aas.statusFlags & StatusModeToProtoFlag(currentMode)) &&
				(mouseStationaryTimer >= sts2Time || (sts1setTime >= sts2Time && !(aas.optionFlags & FLAG_LV2ONINACTIVE )))) {
				/* HIDDEN_ACTIVE to STATUS2_SET */
				aas.lastStatus = aas.originalStatusMode = CallProtoService(aas.szName,PS_GETSTATUS,0, 0);
				aas.status = aas.lv2Status;
				aas.statusChanged = statusChanged = TRUE;
				changeState(aas, STATUS2_SET);
			}
		}
		if ( aas.curState == SET_ORGSTATUS ) {
			/* SET_ORGSTATUS to ACTIVE */
			aas.lastStatus = CallProtoService(aas.szName,PS_GETSTATUS,0, 0);
			aas.status = aas.originalStatusMode;
			confirm = (aas.optionFlags&FLAG_CONFIRM)?TRUE:confirm;
			aas.statusChanged = statusChanged = TRUE;
			changeState(aas, ACTIVE);
			aas.sts1setTimer = 0;
		}
		(*autoAwaySettings)[i].mStatus = FALSE;
	}

	if ( confirm || statusChanged ) {
		OBJLIST<TAAAProtoSetting> ps = *autoAwaySettings;

		int i;
		for ( i=0; i < ps.getCount(); i++ ) {
			if ( ps[i].szMsg )
				ps[i].szMsg = _tcsdup( ps[i].szMsg );

			if (ps[i].status == ID_STATUS_DISABLED)
				ps[i].szName = "";
		}
		
		if (confirm)
			confirmDialog = (HWND)CallService(MS_CS_SHOWCONFIRMDLGEX, (WPARAM)&ps, db_get_w(NULL, MODULENAME, SETTING_CONFIRMDELAY, 5));
		else if (statusChanged)
			CallService(MS_CS_SETSTATUSEX, (WPARAM)&ps, 0);
}	}
	
/////////////////////////////////////////////////////////////////////////////////////////
// Windows hooks 

static int HookWindowsHooks(int hookMiranda, int hookAll)
{
	if (hookMiranda) {
		if ( monitorKeyboard && hMirandaKeyBoardHook == NULL )
			hMirandaKeyBoardHook = SetWindowsHookEx(WH_KEYBOARD,MirandaKeyBoardHookFunction,NULL,GetCurrentThreadId());
		if ( monitorMouse && hMirandaMouseHook == NULL )
			hMirandaMouseHook = SetWindowsHookEx(WH_MOUSE,MirandaMouseHookFunction,NULL,GetCurrentThreadId());
	}
	if (hookAll) {
		MyGetLastInputInfo=NULL;
		if ( monitorKeyboard && hKeyBoardHook == NULL )
			hKeyBoardHook = SetWindowsHookEx(WH_KEYBOARD,KeyBoardHookFunction,hInst,0);
		if ( monitorMouse && hMouseHook == NULL )
			hMouseHook = SetWindowsHookEx(WH_MOUSE,MouseHookFunction,hInst,0);
	}

	return 0;
}

static int UnhookWindowsHooks()
{
	UnhookWindowsHookEx(hMouseHook);
	UnhookWindowsHookEx(hKeyBoardHook);
	UnhookWindowsHookEx(hMirandaMouseHook);
	UnhookWindowsHookEx(hMirandaKeyBoardHook);

	hMouseHook = hKeyBoardHook = hMirandaMouseHook = hMirandaKeyBoardHook = NULL;

	return 0;
}

static LRESULT CALLBACK MirandaMouseHookFunction(int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0) {
		PMOUSEHOOKSTRUCT mouseInfo = (PMOUSEHOOKSTRUCT)lParam;
		POINT pt = mouseInfo->pt;
		
		/* TioDuke's KeyBoardNotifyExt: only update if a Miranda window is focused */
		DWORD pid;
		GetWindowThreadProcessId(GetForegroundWindow(), &pid);
		if (pid != GetCurrentProcessId())
			return CallNextHookEx(hMirandaMouseHook, code, wParam, lParam);

		if (pt.x!=lastMousePos.x || pt.y!=lastMousePos.y) {
			lastMousePos = pt;
			lastMirandaInput = GetTickCount();	 
	}	}
				
	return CallNextHookEx(hMirandaMouseHook, code, wParam, lParam);
}

static LRESULT CALLBACK MirandaKeyBoardHookFunction(int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0) {
		if (ignoreAltCombo) {//&& ((HIWORD(lParam)&KF_ALTDOWN) || (wParam == VK_MENU))) {
			if ( ((GetKeyState(VK_MENU) < 0) || (wParam == VK_MENU)) ||
				((GetKeyState(VK_TAB) < 0) || (wParam == VK_TAB)) ||
				((GetKeyState(VK_SHIFT) < 0) || (wParam == VK_SHIFT)) ||
				((GetKeyState(VK_CONTROL) < 0) || (wParam == VK_CONTROL)) ||
				((GetKeyState(VK_ESCAPE) < 0) || (wParam == VK_ESCAPE)) ||
				((GetKeyState(VK_LWIN) < 0) || (wParam == VK_LWIN)) ||
				((GetKeyState(VK_RWIN) < 0) || (wParam == VK_RWIN))) {
				return CallNextHookEx(hMirandaKeyBoardHook, code, wParam, lParam);
			}
		}
		switch (wParam) {
		case VK_NUMLOCK:
		case VK_CAPITAL:
		case VK_SCROLL:
			if (!ignoreLockKeys)
				lastMirandaInput = GetTickCount();
			break;

		case VK_TAB:
		case VK_SHIFT:
		case VK_CONTROL:
		case VK_MENU:
		case VK_ESCAPE:
		case VK_LWIN:
		case VK_RWIN:
			if (!ignoreSysKeys)
				lastMirandaInput = GetTickCount();
			break;

		default:
			lastMirandaInput = GetTickCount();
			break;
	}	}
	
	return CallNextHookEx(hMirandaKeyBoardHook, code, wParam, lParam);
}

static LRESULT CALLBACK MouseHookFunction(int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0) {
		PMOUSEHOOKSTRUCT mouseInfo = (PMOUSEHOOKSTRUCT)lParam;
		POINT pt = mouseInfo->pt;

		/* TioDuke's KeyBoardNotifyExt: also grab clicks */
		if ((wParam >= WM_NCLBUTTONDOWN && wParam <= WM_NCXBUTTONDBLCLK && wParam != 0x00AA) || (wParam >= WM_LBUTTONDOWN && wParam <= WM_XBUTTONDBLCLK))
			lastInput = GetTickCount();

		if (pt.x!=lastMousePos.x || pt.y!=lastMousePos.y) {
			lastMousePos = pt;
			lastInput = GetTickCount();	 
	}	}
				
	return CallNextHookEx(hMouseHook, code, wParam, lParam);
}

static LRESULT CALLBACK KeyBoardHookFunction(int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0) {
		if (ignoreAltCombo) {//&& ((HIWORD(lParam)&KF_ALTDOWN) || (wParam == VK_MENU))) {
			if ( ((GetKeyState(VK_MENU) < 0) || (wParam == VK_MENU)) ||
				((GetKeyState(VK_TAB) < 0) || (wParam == VK_TAB)) ||
				((GetKeyState(VK_SHIFT) < 0) || (wParam == VK_SHIFT)) ||
				((GetKeyState(VK_CONTROL) < 0) || (wParam == VK_CONTROL)) ||
				((GetKeyState(VK_ESCAPE) < 0) || (wParam == VK_ESCAPE)) ||
				((GetKeyState(VK_LWIN) < 0) || (wParam == VK_LWIN)) ||
				((GetKeyState(VK_RWIN) < 0) || (wParam == VK_RWIN))) {
				return CallNextHookEx(hKeyBoardHook, code, wParam, lParam);
			}
		}
		switch (wParam) {
		case VK_NUMLOCK:
		case VK_CAPITAL:
		case VK_SCROLL:
			if (!ignoreLockKeys)
				lastInput = GetTickCount();
			break;

		case VK_TAB:
		case VK_SHIFT:
		case VK_CONTROL:
		case VK_MENU:
		case VK_ESCAPE:
		case VK_LWIN:
		case VK_RWIN:
			if (!ignoreSysKeys)
				lastInput = GetTickCount();
			break;

		default:
			lastInput = GetTickCount();
			break;
	}	}
	
	return CallNextHookEx(hKeyBoardHook, code, wParam, lParam);
}

/////////////////////////////////////////////////////////////////////////////////////////
// Inits & stuff

static int AutoAwayShutdown(WPARAM wParam,LPARAM lParam)
{
	KillTimer(NULL, hAutoAwayTimer);

#ifdef TRIGGERPLUGIN
	DeInitTrigger();
#endif
	UnhookEvent(hAutoAwayOptionsHook);
	UnhookEvent(hAutoAwayShutDownHook);
	UnhookEvent(hAccChangeHook);
	UnhookEvent(hProtoAckHook);
	UnhookWindowsHooks();
	DestroyHookableEvent(hStateChangedEvent);
		
	autoAwaySettings->destroy();
	return 0;
}

int CSModuleLoaded(WPARAM wParam, LPARAM lParam)
{
	HMODULE hUser32 = GetModuleHandleA("user32");
	openInputDesktop = ( pfnOpenInputDesktop )GetProcAddress (hUser32, "OpenInputDesktop");
	closeDesktop = ( pfnCloseDesktop )GetProcAddress (hUser32, "CloseDesktop");

	if ( IsWinVer2000Plus() && !db_get_b(NULL, MODULENAME, SETTING_IGNLOCK, FALSE))
		MyGetLastInputInfo = (BOOL (WINAPI *)(PLASTINPUTINFO))GetProcAddress(GetModuleHandleA("user32"),"GetLastInputInfo");
	else
		MyGetLastInputInfo = NULL;

	hAccChangeHook = HookEvent(ME_PROTO_ACCLISTCHANGED, OnAccChanged);
	hAutoAwayOptionsHook = HookEvent(ME_OPT_INITIALISE,AutoAwayOptInitialise);
	hAutoAwayShutDownHook = HookEvent(ME_SYSTEM_OKTOEXIT,AutoAwayShutdown);
	hProtoAckHook = HookEvent(ME_PROTO_ACK, ProcessProtoAck);
	mouseStationaryTimer = 0;
	lastInput = lastMirandaInput = GetTickCount();

	////////////////////////////////////////////////////////////////////////////////////////

	protoList = ( OBJLIST<PROTOCOLSETTINGEX>* )autoAwaySettings;

	int count;
	PROTOACCOUNT** protos;
	ProtoEnumAccounts(&count, &protos);

	for ( int i=0; i < count; i++ )
		if ( IsSuitableProto( protos[i] ))
			autoAwaySettings->insert( new TAAAProtoSetting( protos[i] ));

	////////////////////////////////////////////////////////////////////////////////////////

	LoadOptions(*autoAwaySettings, FALSE);
	return 0;
}