// TrayWnd.cpp : implementation file
//

#include "stdafx.h"
#include "DenAgent.h"
#include "TrayWnd.h"
#include "..\Inject\InjectApi.h"

#include "forcelib.h"

#include "DenAgentDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define ID_SYSTRAY 1
#define NM_SYSTRAY ( WM_USER + 1000 )

const UINT	s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));

bool	g_bOldInject;

// Inject Enable/Inject Disable function pointers
typedef void (*VoidNoParams)();

VoidNoParams InjEnable = NULL;
VoidNoParams InjDisable = NULL;

// HMODULE for Inject dll
HMODULE g_hInj = NULL;

// This is for our windows enumeration process
BOOL CALLBACK		EnumerationCallbackProc( HWND, LPARAM );
CTrayWnd* CTrayWnd::s_pWnd = NULL;

/////////////////////////////////////////////////////////////////////////////
// CTrayWnd

CTrayWnd::CTrayWnd()
: m_pDialog( NULL ), m_uiTimer( NULL )
{
	s_pWnd = this;
}

CTrayWnd::~CTrayWnd()
{
	s_pWnd = NULL;
}

BEGIN_MESSAGE_MAP(CTrayWnd, CWnd)
	//{{AFX_MSG_MAP(CTrayWnd)
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_WM_TIMER()
	ON_COMMAND(ID_SYSTRAY_CONFIGURE, OnSystrayConfigure)
	ON_COMMAND(ID_SYSTRAY_EXIT, OnSystrayExit)
	ON_REGISTERED_MESSAGE(s_uTaskbarRestart, OnTaskbarRestart)
	//}}AFX_MSG_MAP
   ON_MESSAGE(NM_SYSTRAY, OnSysTray)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTrayWnd message handlers

LRESULT CTrayWnd::OnTaskbarRestart(WPARAM, LPARAM)
{
   NOTIFYICONDATA nid;
   ::memset( &nid, 0, sizeof( NOTIFYICONDATA ) );

   nid.cbSize = sizeof( NOTIFYICONDATA );
   nid.hWnd = m_hWnd;
   nid.uID = ID_SYSTRAY;
   nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
   nid.uCallbackMessage = NM_SYSTRAY;
   nid.hIcon = AfxGetApp()->LoadIcon( IDR_TRAYICON );
   ::_tcscpy( nid.szTip, _T( "Decal Agent" ) );

   ::Shell_NotifyIcon( NIM_DELETE, &nid );
   ::Shell_NotifyIcon( NIM_ADD, &nid );

   DestroyIcon(nid.hIcon);
	return TRUE;
}

void CTrayWnd::showDialog()
{
   if( m_pDialog == NULL )
   {
      CDenAgentDlg dlg;

      m_pDialog = &dlg;
      dlg.DoModal();
      m_pDialog = NULL;
   }
   else
   {
      m_pDialog->SetForegroundWindow();
      m_pDialog->BringWindowToTop();
   }
}

int CTrayWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

   ::CoInitialize( NULL );
   
   // Check if asheron's call is already running
   HWND wndAC = ::FindWindowEx( NULL, NULL, _T( "Asheron's Call" ), _T( "Asheron's Call" ) );

   bEnabled = ( wndAC == NULL );
   if( wndAC != NULL )
   {
      ::AfxMessageBox( _T( "Asheron's Call was started before the Agent.\r\nNo plugins will be installed until Asheron's Call and the agent are exited and restarted.\r\n\r\nThe Agent must be run before Asheron's Call to properly install plugins." ), MB_ICONERROR | MB_OK );
      ::PostQuitMessage( 0 );
   }

	else
	{
		RegKey key;
		DWORD dwOldInj;
		if( key.Create( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Decal" )) != ERROR_SUCCESS )
		{
			g_bOldInject = false;
			m_uiTimer = SetTimer (1, 1000, NULL);
		}

		else
		{
			if( key.QueryDWORDValue( "OldInjection", dwOldInj ) == ERROR_SUCCESS )
			{
				if( dwOldInj == 1 )
				{
					g_bOldInject = true;

					TCHAR szInjectDll[ MAX_PATH ];
					memset( szInjectDll, 0, sizeof( szInjectDll ) / sizeof( szInjectDll[0] ) );

					// Open the COM CoClass for IPager to get Inject.dll path
					if( key.Open( HKEY_CLASSES_ROOT, _T( "CLSID\\{C79E2F76-06F8-4CD0-A613-4829237D297D}\\InprocServer32" ), KEY_READ ) == ERROR_SUCCESS )
					{
						DWORD dwChars = MAX_PATH - 1;
						if( key.QueryStringValue( NULL, szInjectDll, &dwChars ) != ERROR_SUCCESS )
						{
							::AfxMessageBox( _T( "There is a serious problem with the Decal Agent registry settings!!\n\nExiting." ), MB_ICONERROR | MB_OK );
							::PostQuitMessage( 0 );
						}
					}

					g_hInj = LoadLibrary( szInjectDll );

					InjEnable = (VoidNoParams) GetProcAddress( g_hInj, (LPCSTR) 0x00000012 );
					InjDisable = (VoidNoParams) GetProcAddress( g_hInj, (LPCSTR) 0x00000011 );

					if( !InjEnable || !InjDisable )
					{
						::AfxMessageBox( _T( "Can't load Inject.dll. Please turn off old style injection." ), MB_ICONERROR | MB_OK );
						g_bOldInject = false;
					}

					else
						::InjEnable();
				}

				else
				{
					g_bOldInject = false;
					m_uiTimer = SetTimer( 1, 1000, NULL );
				}
			}

			else
			{
				g_bOldInject = false;
				m_uiTimer = SetTimer( 1, 1000, NULL );
			}
		}
   }

   // Create the system tray icon
   NOTIFYICONDATA nid;
   ::memset( &nid, 0, sizeof( NOTIFYICONDATA ) );

   nid.cbSize = sizeof( NOTIFYICONDATA );
   nid.hWnd = m_hWnd;
   nid.uID = ID_SYSTRAY;
   nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
   nid.uCallbackMessage = NM_SYSTRAY;
   nid.hIcon = AfxGetApp()->LoadIcon( IDR_TRAYICON );
   ::_tcscpy( nid.szTip, _T( "Decal Agent" ) );

   ::Shell_NotifyIcon( NIM_ADD, &nid );

   DestroyIcon(nid.hIcon);

	// Get the image path (path of parent executable)
	TCHAR szImagePath[ MAX_PATH ];
	::GetModuleFileName( NULL, szImagePath, MAX_PATH );

	LPTSTR strProcessName = ::_tcsrchr( szImagePath, _T( '\\' ) );
	*( strProcessName + 1 ) = _T( '\0' );

	RegKey key;
	key.Create( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Decal\\Agent" ) );
	key.SetStringValue( _T( "AgentPath" ), szImagePath );

	return 0;
}

void CTrayWnd::OnDestroy() 
{
	CWnd::OnDestroy();

   NOTIFYICONDATA nid;
   ::memset( &nid, 0, sizeof( NOTIFYICONDATA ) );

   nid.cbSize = sizeof( NOTIFYICONDATA );
   nid.hWnd = m_hWnd;
   nid.uID = ID_SYSTRAY;

   ::Shell_NotifyIcon( NIM_DELETE, &nid );

	if( g_bOldInject )
	{
		if( bEnabled )
			::InjDisable();

		InjEnable = NULL;
		InjDisable = NULL;
		FreeLibrary( g_hInj );
	}

	else
		if( m_uiTimer )
		{
			KillTimer (m_uiTimer);
			m_uiTimer = 0;
		}


   ::CoUninitialize();
}

void CTrayWnd::OnTimer (UINT_PTR nIDEvent)
{
	::EnumWindows( EnumerationCallbackProc, (LPARAM) NULL );
}

BOOL CALLBACK EnumerationCallbackProc( HWND hwnd, LPARAM lParam )
{
	TCHAR szClassName[64];
	memset( szClassName, 0, sizeof( szClassName ) / sizeof( szClassName[0] ) );
	GetClassName( hwnd, szClassName, 64 );

	if( _tcsicmp( _T( "ZoneLobbyWindow" ), szClassName ) != 0 )
	{
		return TRUE;
	}

	if( CTrayWnd::s_pWnd != NULL )
		return CTrayWnd::s_pWnd->OnEnum( hwnd );
	else
		return FALSE;
}

BOOL CTrayWnd::OnEnum( HWND hWndLobby )
{
	if( hWndLobby != NULL )
	{
		DWORD dwProcessId = 0;
		GetWindowThreadProcessId( hWndLobby, &dwProcessId );

		if( dwProcessId != 0 )
		{
			TCHAR tszBuffer [256];

			::_stprintf (tszBuffer, _T("__LOBBYHOOK_%d"), dwProcessId);
			HANDLE hLobbySemaphore = ::CreateSemaphore (NULL, 0, 1, tszBuffer);

			DWORD dwLastError = ::GetLastError ();

			if (hLobbySemaphore)
			{
				::CloseHandle (hLobbySemaphore);

				if (dwLastError == ERROR_ALREADY_EXISTS)
				{
					// The lobbyhook has already been injected, we know because it created the semaphore.
					return TRUE;
				}
			}

			RegKey key;

			TCHAR szDll[ MAX_PATH ];
			TCHAR szDllPath[ MAX_PATH ];
			memset( szDllPath, 0, sizeof( szDllPath ) / sizeof( szDllPath[0] ) );

			if( key.Open( HKEY_LOCAL_MACHINE, _T( "Software\\Decal\\Agent" ), KEY_READ ) == ERROR_SUCCESS )
			{
				DWORD dwChars = MAX_PATH - 1;
				if( key.QueryStringValue( _T( "AgentPath" ), szDllPath, &dwChars ) == ERROR_SUCCESS )
				{
					lstrcpy( szDll, szDllPath );
					lstrcat( szDll, _T( "\\ForceLibrary.dll" ) );
				}

				else
				{
					DWORD dwError = GetLastError();
					char szBuffer[256];

					_snprintf( szBuffer, sizeof( szBuffer ), "Couldn't query AgentPath value: 0x%08lx", dwError );
					::MessageBox( NULL, szBuffer, _T( "DenAgent" ), MB_OK );
				}
			}

			else
			{
				DWORD dwError = GetLastError();
				char szBuffer[256];

				_snprintf( szBuffer, sizeof( szBuffer ), "Couldn't open HKLM\\Software\\Decal\\Agent key: 0x%08lx", dwError );
				::MessageBox( NULL, szBuffer, _T( "DenAgent" ), MB_OK );
			}

			if( szDllPath[0] )
			{
				HMODULE hLib = (HMODULE) ForceLibraryNow( dwProcessId, szDll );
				if( hLib == NULL )
				{
					DWORD dwError = GetLastError();
					char szBuffer[256];

					_snprintf( szBuffer, sizeof( szBuffer ), "ForceLibraryNow (LobbyHook.dll) has failed( 0x%08lx )\nDo you want to switch to old style injection?\nIf Decal is loading in AC properly answer no.", dwError );

					// Kill timer to avoid 1000 popups
					KillTimer( m_uiTimer );
					m_uiTimer = 0;

					int iRet = ::MessageBox( NULL, szBuffer, _T( "DenAgent" ), MB_YESNO );
					if( iRet == IDYES )
					{
						RegKey key;
						key.Create( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Decal" ) );
						key.SetDWORDValue("OldInjection", 0x1L);
						g_bOldInject = true;

						TCHAR szInjectDll[ MAX_PATH ];
						memset( szInjectDll, 0, sizeof( szInjectDll ) / sizeof( szInjectDll[0] ) );

						// Open the COM CoClass for IPager to get Inject.dll path
						if( key.Open( HKEY_CLASSES_ROOT, _T( "CLSID\\{C79E2F76-06F8-4CD0-A613-4829237D297D}\\InprocServer32" ), KEY_READ ) == ERROR_SUCCESS )
						{
							DWORD dwChars = MAX_PATH - 1;
							if( key.QueryStringValue( NULL, szInjectDll, &dwChars ) != ERROR_SUCCESS )
							{
								::AfxMessageBox( _T( "There is a serious problem with the Decal Agent registry settings!!\n\nExiting." ), MB_ICONERROR | MB_OK );
								::PostQuitMessage( 0 );
							}
						}

						g_hInj = LoadLibrary( szInjectDll );

						InjEnable = (VoidNoParams) GetProcAddress( g_hInj, (LPCSTR) 0x00000012 );
						InjDisable = (VoidNoParams) GetProcAddress( g_hInj, (LPCSTR) 0x00000011 );

						::InjEnable();
					}

					else // no
					{
						_snprintf( szBuffer, sizeof( szBuffer ), "Lobby Injection halted... restart DenAgent to try again." );
						::MessageBox( NULL, szBuffer, _T( "DenAgent" ), MB_OK );
					}

				}

				lstrcpy( szDll, szDllPath );
				lstrcat( szDll, _T( "\\LobbyHook.dll" ) );
				hLib = (HMODULE) ForceLibraryNow( dwProcessId, szDll );

				if( hLib == NULL )
				{
					DWORD dwError = GetLastError();
					char szBuffer[256];

					_snprintf( szBuffer, sizeof( szBuffer ), "ForceLibraryNow (LobbyHook.dll) has failed( 0x%08lx )\nDo you want to switch to old style injection?\nIf Decal is loading in AC properly answer no.", dwError );

					// Kill timer
					KillTimer( m_uiTimer );
					m_uiTimer = 0;

					int iRet = ::MessageBox( NULL, szBuffer, _T( "DenAgent" ), MB_YESNO );
					if( iRet == IDYES )
					{
						RegKey key;
						key.Create( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Decal" ) );
						key.SetDWORDValue("OldInjection", 0x1L);
						g_bOldInject = true;

						TCHAR szInjectDll[ MAX_PATH ];
						memset( szInjectDll, 0, sizeof( szInjectDll ) / sizeof( szInjectDll[0] ) );

						// Open the COM CoClass for IPager to get Inject.dll path
						if( key.Open( HKEY_CLASSES_ROOT, _T( "CLSID\\{C79E2F76-06F8-4CD0-A613-4829237D297D}\\InprocServer32" ), KEY_READ ) == ERROR_SUCCESS )
						{
							DWORD dwChars = MAX_PATH - 1;
							if( key.QueryStringValue( NULL, szInjectDll, &dwChars ) != ERROR_SUCCESS )
							{
								::AfxMessageBox( _T( "There is a serious problem with the Decal Agent registry settings!!\n\nExiting." ), MB_ICONERROR | MB_OK );
								::PostQuitMessage( 0 );
							}
						}

						g_hInj = LoadLibrary( szInjectDll );

						InjEnable = (VoidNoParams) GetProcAddress( g_hInj, (LPCSTR) 0x00000012 );
						InjDisable = (VoidNoParams) GetProcAddress( g_hInj, (LPCSTR) 0x00000011 );

						::InjEnable();
					}

					else // no
					{
						_snprintf( szBuffer, sizeof( szBuffer ), "Lobby Injection halted... restart DenAgent to try again." );
						::MessageBox( NULL, szBuffer, _T( "DenAgent" ), MB_OK );
					}
				}
			}

			else
			{
				DWORD dwError = GetLastError();
				char szBuffer[256];

				_snprintf( szBuffer, sizeof( szBuffer ), "DLL path was blank: 0x%08lx", dwError );
				::MessageBox( NULL, szBuffer, _T( "DenAgent" ), MB_OK );
			}
		}

		else
		{
			DWORD dwError = GetLastError();
			char szBuffer[256];

			_snprintf( szBuffer, sizeof( szBuffer ), "Couldn't get process id: 0x%08lx", dwError );
			::MessageBox( NULL, szBuffer, _T( "DenAgent" ), MB_OK );
		}

	}

	return TRUE;
}

LRESULT CTrayWnd::OnSysTray(WPARAM nID, LPARAM uMsg)
{
   // We should only receive message for the one systray we register
   ASSERT( nID == ID_SYSTRAY );

   switch( uMsg )
   {
   case WM_RBUTTONUP:
      {
         POINT pt;
         ::GetCursorPos( &pt );
         CMenu menu;
         menu.LoadMenu( IDR_POPUPS );
         CMenu *pSystray = menu.GetSubMenu( 0 );

         pSystray->SetDefaultItem( 0, TRUE );
         SetForegroundWindow();
         pSystray->TrackPopupMenu( TPM_RIGHTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this );
      }
      break;

   case WM_LBUTTONUP:
      showDialog();
      break;
   }

   return 0;
}

void CTrayWnd::OnSystrayConfigure() 
{
   showDialog();
}

void CTrayWnd::OnSystrayExit() 
{
	DestroyWindow();
   ::PostQuitMessage( 0 );
}

void CTrayWnd::UpdateXMLFiles()
{
	CDenAgentDlg dlg;
	m_pDialog = &dlg;
	dlg.m_bDoUpdate = true;
    dlg.DoModal( );
    m_pDialog = NULL;
}
