//========= Copyright Valve Corporation, All rights reserved. ============//
//
//	VPROJECT.CPP
//
//=====================================================================================//
#include "vproject.h"

#define MAX_PROJECTS 50

#define ID_PROJECTS_LISTVIEW 100

// column id
#define ID_PROJECT_NAME			0
#define ID_PROJECT_DIRECTORY	1

#define WM_TRAY					(WM_APP + 1)
#define ID_TRAY					5000
#define ID_TRAY_ADDVPROJECT		6000
#define ID_TRAY_MODIFYVPROJECT	6001
#define ID_TRAY_MOVEUP			6002
#define ID_TRAY_MOVEDOWN		6003
#define ID_TRAY_EXIT			6004

typedef struct
{
	char	*pName;
	char	*pGamedir;
} project_t;

HWND				g_hWnd;
char				g_project_name[256];
char				g_project_gamedir[256];
HINSTANCE			g_hInstance;
project_t			g_projects[MAX_PROJECTS];
int					g_numProjects;
NOTIFYICONDATA		g_iconData;
HMENU				g_hMenu;
int					g_nActiveVProject;
POINT				g_cursorPoint;			

void TrayMessageHandler( HWND hWnd, UINT uMessageID );

//-----------------------------------------------------------------------------
//	SetVProject
//
//-----------------------------------------------------------------------------
void SetVProject( const char *pProjectName )
{
	char	*pGamedir;
	char	project[256];
	int		i;

	if ( pProjectName )
	{
		strcpy( project, pProjectName );
		Sys_StripQuotesFromToken( project );

		for ( i=0; i<g_numProjects; i++ )
		{
			if ( !stricmp( g_projects[i].pName, project ) )
			{
				// found
				break;
			}
		}

		if ( i >= g_numProjects )
		{
			// not found
			return;
		}

		pGamedir = g_projects[i].pGamedir;
	}
	else
	{
		pGamedir = "";
	}

	// Changed to CURRENT_USER to solve security issues in vista!
	Sys_SetRegistryString( 
		//"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\Environment\\VProject",
		"HKEY_CURRENT_USER\\Environment\\VProject"
		pGamedir );

	DWORD	result;
	SendMessageTimeout(
		HWND_BROADCAST,
		WM_SETTINGCHANGE,
		0,
		(LPARAM)"Environment",
		SMTO_ABORTIFHUNG,
		0,
		&result );
}

//-----------------------------------------------------------------------------
//	ModifyVProject
//
//-----------------------------------------------------------------------------
void ModifyVProject( int index, const char *pName, const char *pGamedir )
{
	free( g_projects[index].pName );
	free( g_projects[index].pGamedir );

	if ( !pName || !pName[0] )
	{
		// delete
		if ( g_numProjects-index-1 > 0 )
		{
			// shift remaining elements
			memcpy( &g_projects[index], &g_projects[index+1], (g_numProjects-index-1)*sizeof( project_t ) );
		}

		g_projects[g_numProjects-1].pName = NULL;
		g_projects[g_numProjects-1].pGamedir = NULL;

		g_numProjects--;

		if ( g_nActiveVProject == index+1 )
		{
			// deleted current vproject
			if ( !g_numProjects )
			{
				// no more projects
				g_nActiveVProject = 0;
				SetVProject( NULL );
			}
			else
			{
				// set to top
				g_nActiveVProject = 1;
				SetVProject( g_projects[0].pName );
			}
		}
	}
	else
	{
		g_projects[index].pName = strdup( pName );
		g_projects[index].pGamedir = strdup( pGamedir );
	}
}

//-----------------------------------------------------------------------------
//	AddVProject
//
//-----------------------------------------------------------------------------
void AddVProject( const char *pName, const char *pGamedir )
{
	if ( !pName || !pName[0] )
	{
		// do not add empty projects
		return;
	}

	ModifyVProject( g_numProjects, pName, pGamedir );
	g_numProjects++;
}

//-----------------------------------------------------------------------------
//	LoadRegistryValues
//
//-----------------------------------------------------------------------------
void LoadRegistryValues()
{
	char	keyBuff[32];
	char	valueBuff[256];
	char	projectName[256];
	char	gamedirString[256];
	char	*ptr;
	char	*token;
	int		i;

	for ( i=0; i<MAX_PROJECTS; i++ )
	{
		projectName[0] = '\0';
		gamedirString[0] = '\0';

		sprintf( keyBuff, "project%d", i );
		Sys_GetRegistryString( keyBuff, valueBuff, "", sizeof( valueBuff ) );

		// parse and populate valid values
		ptr = valueBuff;
		token = Sys_GetToken( &ptr, false, NULL );
		if ( token[0] )
		{
			strcpy( projectName, token );
		}
		else
		{
			continue;
		}

		token = Sys_GetToken( &ptr, false, NULL );
		if ( token[0] )
		{
			strcpy( gamedirString, token );
		}

		AddVProject( projectName, gamedirString );
	}
}

//-----------------------------------------------------------------------------
//	SaveRegistryValues
//
//-----------------------------------------------------------------------------
void SaveRegistryValues()
{
	char			valueBuff[256];
	char			keyBuff[32];
	char			*pProjectName;
	char			*pGamedir;
	int				len;	
	int				i;

	for ( i=0; i<MAX_PROJECTS; i++ )
	{
		sprintf( keyBuff, "project%d", i );

		pProjectName = g_projects[i].pName;
		if ( !pProjectName )
		{
			pProjectName = "";
		}

		pGamedir = g_projects[i].pGamedir;
		if ( !pGamedir )
		{
			pGamedir = "";
		}

		len = _snprintf( valueBuff, sizeof( valueBuff ), "\"%s\" \"%s\"", pProjectName, pGamedir );
		if ( len == -1 )
		{
			// kill it
			valueBuff[0] = '\0';		
		}

		Sys_SetRegistryString( keyBuff, valueBuff );
	}
}

//-----------------------------------------------------------------------------
//	ShiftActiveProjectUp
//
//-----------------------------------------------------------------------------
void ShiftActiveProjectUp()
{
	if ( g_numProjects <= 1 || !g_nActiveVProject )
	{
		// nothing to do
		return;
	}

	int	active = g_nActiveVProject-1;
	int previous = (active + g_numProjects - 1) % g_numProjects;

	project_t	tempProject;
	tempProject = g_projects[previous];
	g_projects[previous] = g_projects[active];
	g_projects[active] = tempProject;

	g_nActiveVProject = previous + 1;
}

//-----------------------------------------------------------------------------
//	ShiftActiveProjectDown
//
//-----------------------------------------------------------------------------
void ShiftActiveProjectDown()
{
	if ( g_numProjects <= 1 || !g_nActiveVProject )
	{
		// nothing to do
		return;
	}

	int	active = g_nActiveVProject-1;
	int next = (active + g_numProjects + 1) % g_numProjects;

	project_t tempProject;
	tempProject = g_projects[next];
	g_projects[next] = g_projects[active];
	g_projects[active] = tempProject;

	g_nActiveVProject = next + 1;
}

//-----------------------------------------------------------------------------
//	ModifyDlg_Proc
//
//-----------------------------------------------------------------------------
BOOL CALLBACK ModifyDlg_Proc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) 
{
	size_t	len;
	int		width;
	int		height;
	RECT	rect;

	switch ( message ) 
	{
		case WM_INITDIALOG:
			SetDlgItemText( hWnd, IDC_MODIFY_PROJECT, g_project_name );
			SetDlgItemText( hWnd, IDC_MODIFY_GAMEDIR, g_project_gamedir );

			// center dialog
			GetWindowRect( hWnd, &rect );
			width = GetSystemMetrics( SM_CXSCREEN ); 
			height = GetSystemMetrics( SM_CYSCREEN ); 
			SetWindowPos( hWnd, NULL, ( width - ( rect.right - rect.left ) )/2, ( height - ( rect.bottom - rect.top ) )/2, 0, 0, SWP_NOSIZE | SWP_NOZORDER );
			return ( TRUE );

		case WM_COMMAND: 
            switch ( LOWORD( wParam ) ) 
            {
				case IDC_OK: 
					GetDlgItemText( hWnd, IDC_MODIFY_PROJECT, g_project_name, sizeof( g_project_name ) );
					GetDlgItemText( hWnd, IDC_MODIFY_GAMEDIR, g_project_gamedir, sizeof( g_project_gamedir ) );

					// remove trailing seperator
					Sys_NormalizePath( g_project_gamedir, false );
					len = strlen( g_project_gamedir );
					if ( len > 2 && g_project_gamedir[len-1] == '\\' )
					{
						g_project_gamedir[len-1] = '\0';
					}
					// fall through

				case IDCANCEL:
				case IDC_CANCEL: 
					EndDialog( hWnd, wParam );
					return ( TRUE ); 
			}
			break; 
	}
	return ( FALSE ); 
} 

//-----------------------------------------------------------------------------
//	ModifyDlg_Open
//
//-----------------------------------------------------------------------------
BOOL ModifyDlg_Open()
{
	int result;
	
	result = DialogBox( g_hInstance, MAKEINTRESOURCE( IDD_VPROJECT ), g_hWnd, ( DLGPROC )ModifyDlg_Proc );
	if ( LOWORD( result ) != IDC_OK )
	{
		return FALSE;
	}

	return TRUE;
}

//-----------------------------------------------------------------------------
//	ShowPopupMenu
//
//-----------------------------------------------------------------------------
void ShowPopupMenu( HWND hWnd, bool bUseCachedMenuPos )
{
	BOOL bDelete = true;

	// delete existing entries
	for ( int nIndex = 1; nIndex < ID_TRAY_ADDVPROJECT && bDelete; nIndex++ )
	{
		bDelete = DeleteMenu( g_hMenu, nIndex, MF_BYCOMMAND );
	}

	// Insert projects
	char szMenuItem[MAX_PATH];
	for ( int nIndex = 0; nIndex < g_numProjects; nIndex++ )
	{
		strcpy( szMenuItem, g_projects[nIndex].pName );
		strcat( szMenuItem, "\t" );
		strcat( szMenuItem, g_projects[nIndex].pGamedir );
		strcat( szMenuItem, "     " );

		InsertMenu( g_hMenu, nIndex, MF_BYPOSITION | MF_STRING, nIndex + 1, szMenuItem );
	}

	if ( g_nActiveVProject )
	{
		CheckMenuItem( g_hMenu, g_nActiveVProject, MF_BYCOMMAND|MF_CHECKED );
		SetMenuDefaultItem( g_hMenu, g_nActiveVProject, FALSE );
	}

	// Display the popup menu at the current cursor location
	// Use the cached cursor position if set
	if ( !bUseCachedMenuPos || ( !g_cursorPoint.x && !g_cursorPoint.y ) )
	{
		GetCursorPos( &g_cursorPoint );
	}
	SetForegroundWindow( hWnd );
	TrackPopupMenu( g_hMenu, 0, g_cursorPoint.x, g_cursorPoint.y, 0, hWnd, 0 );
	PostMessage( hWnd, WM_NULL, 0, 0 );
}

//-----------------------------------------------------------------------------
//	TrayMessageHandler
//
//-----------------------------------------------------------------------------
void TrayMessageHandler( HWND hWnd, UINT uMessageID )
{
	switch ( uMessageID )
	{
	case 0:
		break;

	case ID_TRAY_EXIT:
		DestroyWindow( hWnd );
		break;

	case ID_TRAY_ADDVPROJECT:
		SetForegroundWindow( hWnd );
		g_project_name[0] = '\0';
		g_project_gamedir[0] = '\0';
		if ( ModifyDlg_Open() )
		{
			AddVProject( g_project_name, g_project_gamedir );
			SaveRegistryValues();
		}
		ShowPopupMenu( hWnd, true );
		break;

	case ID_TRAY_MODIFYVPROJECT:
		SetForegroundWindow( hWnd );
		if ( g_nActiveVProject )
		{
			strcpy( g_project_name, g_projects[g_nActiveVProject-1].pName );
			strcpy( g_project_gamedir, g_projects[g_nActiveVProject-1].pGamedir );
			if ( ModifyDlg_Open() )
			{
				ModifyVProject( g_nActiveVProject-1, g_project_name, g_project_gamedir );
				SaveRegistryValues();
			}
			ShowPopupMenu( hWnd, true );
		}
		break;

	case ID_TRAY_MOVEUP:
		SetForegroundWindow( hWnd );
		if ( g_nActiveVProject )
		{
			ShiftActiveProjectUp();
			SaveRegistryValues();
			ShowPopupMenu( hWnd, true );
		}
		break;

	case ID_TRAY_MOVEDOWN:
		SetForegroundWindow( hWnd );
		if ( g_nActiveVProject )
		{
			ShiftActiveProjectDown();
			SaveRegistryValues();
			ShowPopupMenu( hWnd, true );
		}
		break;

	default:
		if ( uMessageID >= 1 && uMessageID <= MAX_PROJECTS )
		{
			// set current vproject
			g_nActiveVProject = uMessageID;
			SetVProject( g_projects[uMessageID-1].pName );
		}
		break;
	}
}

//-----------------------------------------------------------------------------
//	Main_WndProc
// 
//-----------------------------------------------------------------------------
LRESULT CALLBACK Main_WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	switch ( message )
	{
		case WM_DESTROY:
			PostQuitMessage( 0 );
			return 0L;
	
		case WM_TRAY:
			if ( lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN )
			{
				ShowPopupMenu( hWnd, false );
				return 0L;
			}
			break;

		case WM_COMMAND:
			TrayMessageHandler( hWnd, LOWORD( wParam ) );
			break;
	}	

	return ( DefWindowProc( hWnd, message, wParam, lParam ) );
}

//-----------------------------------------------------------------------------
//	Startup
//
//-----------------------------------------------------------------------------
bool Startup()
{
	int				i;

	// set up our window class
	WNDCLASS wndclass;
	memset( &wndclass, 0, sizeof( wndclass ) );
	wndclass.style         = 0;
	wndclass.lpfnWndProc   = Main_WndProc;
	wndclass.cbClsExtra    = 0;
	wndclass.cbWndExtra    = 0;
	wndclass.hInstance     = g_hInstance;
	wndclass.hIcon         = LoadIcon( g_hInstance, (LPCTSTR)IDI_VPROJECT );
	wndclass.hCursor       = LoadCursor( NULL, IDC_ARROW );
	wndclass.hbrBackground = NULL;
	wndclass.lpszMenuName  = NULL;
	wndclass.lpszClassName = VPROJECT_CLASSNAME;
	if ( !RegisterClass( &wndclass ) )
	{
		return false;
	}

	// create the hidden window
	g_hWnd = CreateWindow( 
				VPROJECT_CLASSNAME,
				0,
				WS_OVERLAPPEDWINDOW,
				CW_USEDEFAULT,
				0,
				CW_USEDEFAULT,
				0,
				NULL,
				NULL,
				g_hInstance,
				NULL );
	
	// Create tray icon
	g_iconData.cbSize           = sizeof( NOTIFYICONDATA );
	g_iconData.hIcon            = LoadIcon( g_hInstance, (LPCTSTR)IDI_VPROJECT );
	g_iconData.hWnd             = g_hWnd;
	g_iconData.uCallbackMessage = WM_TRAY;
	g_iconData.uFlags           = NIF_ICON | NIF_MESSAGE | NIF_TIP;
	g_iconData.uID              = ID_TRAY;
	strcpy(g_iconData.szTip, "VPROJECT");
	Shell_NotifyIcon( NIM_ADD, &g_iconData );

	// Create popup menu and add initial items
	g_hMenu = CreatePopupMenu();
	AppendMenu( g_hMenu, MF_SEPARATOR, 0, 0);
	AppendMenu( g_hMenu, MF_STRING, ID_TRAY_ADDVPROJECT, "Add VProject" );
	AppendMenu( g_hMenu, MF_STRING, ID_TRAY_MODIFYVPROJECT, "Modify VProject" );
	AppendMenu( g_hMenu, MF_STRING, ID_TRAY_MOVEUP, "Move Up" );
	AppendMenu( g_hMenu, MF_STRING, ID_TRAY_MOVEDOWN, "Move Down" );
	AppendMenu( g_hMenu, MF_SEPARATOR, 0, 0);
	AppendMenu( g_hMenu, MF_STRING, ID_TRAY_EXIT, "Remove From Tray" );

	// find the current vproject
	char* vproject = getenv( "vproject" );
	if ( vproject && vproject[0] )
	{
		char temp[MAX_PATH];
		strcpy( temp, vproject );
		Sys_NormalizePath( temp, false );
		for ( i=0; i<g_numProjects; i++ )
		{
			if ( !stricmp( g_projects[i].pGamedir, temp ) )
			{
				// found
				g_nActiveVProject = i+1;
				break;
			}
		}
	}

	return true;
}

//-----------------------------------------------------------------------------
//	Shutdown
// 
// Free all resources
//-----------------------------------------------------------------------------
void Shutdown()
{
}

//-----------------------------------------------------------------------------
//	WinMain
//
//	Entry point for program
//-----------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow )
{
	bool	error = true;
	MSG		msg = {0};

	g_hInstance = hInstance;

	// get the project list
	LoadRegistryValues();

	if ( pCmdLine && pCmdLine[0] )
	{
		// set directly
		SetVProject( pCmdLine );
		return 0;
	}

	HWND hwnd = FindWindow( VPROJECT_CLASSNAME, NULL );
	if ( hwnd ) 
	{
		// single instance only
		return ( FALSE );
	}

    if ( !Startup() )
	{
		goto cleanUp;
	}

	// message pump
	while ( GetMessage( &msg, NULL, 0, 0 ) )
    {
		TranslateMessage( &msg );
		DispatchMessage( &msg );
    }

	// no-error, end of app
	error = false;

cleanUp:
	if ( error )
	{
        char str[255];
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, str, 255, NULL );
		MessageBox( NULL, str, NULL, MB_OK );
	}

	Shutdown();

    return ( (int)msg.wParam );
}