#include "StdAfx.h"
#include "mainwindow.h"
#include "resource.h"
#include "bridgeserver_h.h"
#include "bridgeclient_h.h"
#include "atlcomtime.h"
#include "activitytracker.h"

static ActivityTracker activity;

typedef std::list<ChatWindow*>::iterator cwpos;
typedef std::list<ChatRoom*>::iterator crpos;

int MainWindow::LoadTaskbar()
{
    // Load the menu resource. 

	if ((hTaskbarMenu = LoadMenu(env.hInstance, MAKEINTRESOURCE(IDC_BRIDGECLIENT))) == NULL) {
		print(L"unable to load shortcut menu\n");
        return 0; 
	}
 

	hTaskbarMenu = GetSubMenu(hTaskbarMenu,0);
	if(hTaskbarMenu==NULL){
		print(L"unable to load shortcut menu\n");
        return 0; 
	}

	hPrivacyMenu = GetSubMenu(hTaskbarMenu,12);

	// load all the taskbar icons
	NormalIcon = (HICON)LoadImage(NULL,env.GetWideString("taskbar::icons","normal",L"none"),IMAGE_ICON,0,0,LR_LOADFROMFILE);
	if(NormalIcon==NULL){
		print(L"Unable to load taskbar icon: NormalIcon.\n");
	} 
	LoggedOffIcon = (HICON)LoadImage(NULL,env.GetWideString("taskbar::icons","loggedoff",L"none"),IMAGE_ICON,0,0,LR_LOADFROMFILE);
	if(LoggedOffIcon==NULL){
		print(L"Unable to load taskbar icon: LoggedOffIcon.\n");
	} 
	AwayIcon = (HICON)LoadImage(NULL,env.GetWideString("taskbar::icons","away",L"none"),IMAGE_ICON,0,0,LR_LOADFROMFILE);
	if(AwayIcon==NULL){
		print(L"Unable to load taskbar icon: AwayIcon.\n");
	} 
	VisibleToBuddiesIcon = (HICON)LoadImage(NULL,env.GetWideString("taskbar::icons","visibletobuddies",L"none"),IMAGE_ICON,0,0,LR_LOADFROMFILE);;
	if(VisibleToBuddiesIcon==NULL){
		print(L"Unable to load taskbar icon: VisibleToBuddiesIcon.\n");
	} 
	IdleIcon = (HICON)LoadImage(NULL,env.GetWideString("taskbar::icons","idle",L"none"),IMAGE_ICON,0,0,LR_LOADFROMFILE);
	if(IdleIcon==NULL){
		print(L"Unable to load taskbar icon: IdleIcon.\n");
	} 

	// create the icon in the taskbar notification area


	taskbar.cbSize = sizeof(NOTIFYICONDATA);
	taskbar.hWnd = hWnd;
	taskbar.uID = 0; // only one icon please
	taskbar.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP;
	taskbar.uCallbackMessage = ID_TASKBAR;
	taskbar.hIcon = NormalIcon;
	_tcscpy(taskbar.szTip,env.GetWideString("taskbar","tip",L"BridgeClient"));
	if(!Shell_NotifyIcon(NIM_ADD,&taskbar)){
		print(L"Failed to add myself to the taskbar notification area.\n");
	}

	return 1;
}

void MainWindow::UpdateTaskbar()
{
	if(user.LoggedIn){
		EnableMenuItem(hTaskbarMenu,ID_FILE_LOGON,MF_GRAYED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_LOGOFF,MF_ENABLED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_CREATEEVENT,MF_ENABLED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_CREATEGROUP,MF_ENABLED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_EDITPROFILE,MF_ENABLED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_CREATEEVENT,MF_ENABLED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_CREATEGROUP,MF_ENABLED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_SETPASSWORD,MF_ENABLED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_AWAY,MF_ENABLED);
	} else {
		EnableMenuItem(hTaskbarMenu,ID_FILE_LOGON,MF_ENABLED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_LOGOFF,MF_GRAYED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_CREATEEVENT,MF_GRAYED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_CREATEGROUP,MF_GRAYED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_EDITPROFILE,MF_GRAYED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_CREATEEVENT,MF_GRAYED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_CREATEGROUP,MF_GRAYED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_SETPASSWORD,MF_GRAYED);
		EnableMenuItem(hTaskbarMenu,ID_FILE_AWAY,MF_GRAYED);
	}

	SetTaskbarIcon();
	SetTaskbarText();
}

void MainWindow::SetTaskbarIcon(HICON hIcon)
{
	if(hIcon){
		taskbar.hIcon = hIcon;
	} else {
		if(!user.LoggedIn){
			taskbar.hIcon = LoggedOffIcon;
		} else if(user.Away){
			taskbar.hIcon = AwayIcon;
		} else if(user.Idle){
			taskbar.hIcon = IdleIcon;	
		} else {
			switch(user.PrivacyLevel){
			case bcNormal:
				taskbar.hIcon = NormalIcon;
				break;
			case bcVisibleToBuddies:
				taskbar.hIcon = VisibleToBuddiesIcon;
				break;
			default:
				print(L"MainWindow::SetTaskbarIcon: Unknown privacy level.\n");
				taskbar.hIcon = NormalIcon;
				break;
			}
		}
	}
	taskbar.uFlags = NIF_ICON;
	if(!Shell_NotifyIcon(NIM_MODIFY,&taskbar)){
		print(L"Failed to modify the taskbar icon.\n");
	}	
}

void MainWindow::SetTaskbarText(const TCHAR * text)
{
	if(text)
		_tcscpy(taskbar.szTip,text);
	else {
		std::wstring buffer = env.GetWideString("taskbar","tip",L"BridgeClient");
		if(!user.LoggedIn){
			buffer += L" (logged off)";
		} else if(user.Away){
			buffer += L" (away)";
		} else if(user.Idle){
			buffer += L" (idle)";
		} else {
			buffer += L" (logged on)";
		}
		_tcscpy(taskbar.szTip,buffer.data());
	}
	taskbar.uFlags = NIF_TIP;
	if(!Shell_NotifyIcon(NIM_MODIFY,&taskbar)){
		print(L"Failed to modify the taskbar text.\n");
	}	
}

void MainWindow::UnloadTaskbar()
{
    DestroyMenu(hTaskbarMenu); 
	Shell_NotifyIcon(NIM_DELETE,&taskbar);
}

LRESULT MainWindow::DisplayTaskbarMenu() 
{ 

	POINT pt;
	if(!GetCursorPos(&pt)){
		print(L"Unable to read cursor position.\n");
	}

	// update the taskbar menu to reflect the privacy setting
	if(env.GetInt(NULL,"PrivaceEnabled",0)){
		switch(user.PrivacyLevel){
		case bcNormal:
			if(CheckMenuRadioItem(hTaskbarMenu,ID_FILE_VISIBLETOEVERYONE,ID_FILE_VISIBLETOBUDDIES,ID_FILE_VISIBLETOEVERYONE,MF_BYCOMMAND)==0)
				PrintLastError("MainWindow::DisplayTaskbarMenu");
			break;
		case bcVisibleToBuddies:
			if(CheckMenuRadioItem(hTaskbarMenu,ID_FILE_VISIBLETOEVERYONE,ID_FILE_VISIBLETOBUDDIES,ID_FILE_VISIBLETOBUDDIES,MF_BYCOMMAND)==0)
				PrintLastError("MainWindow::DisplayTaskbarMenu");
			break;
		}
	}

	SetForegroundWindow(hWnd);
    LRESULT x = TrackPopupMenu(hTaskbarMenu, 
            TPM_RETURNCMD | TPM_BOTTOMALIGN | TPM_RIGHTALIGN | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, 
            pt.x, pt.y, 0, hWnd, NULL); 
	PostMessage(hWnd,WM_NULL,0,0);
	return x;
} 

void MainWindow::Activate()
{
	// bring the relevant window to the foreground
	if(user.LoggedIn){
		sidebar.Show();
		BringWindowToTop(sidebar.hWnd);
		SetForegroundWindow(sidebar.hWnd);
	} else {
		loginwindow.Show();
		BringWindowToTop(loginwindow.hWnd);
		SetForegroundWindow(loginwindow.hWnd);
	}
}

LRESULT CALLBACK MainWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	MainWindow * wnd = (MainWindow*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
	if(wnd==NULL) return 0;

	switch (message) {
		case WM_DESTROY:
			wnd->OnDestroy();
			break;
		case WM_TIMER:
			wnd->OnTimer();
			break;
		case ID_TASKBAR:
			switch(lParam){
				case WM_LBUTTONDOWN:
					wnd->OnLButtonDown();
					break;
				case WM_RBUTTONDOWN:
					wnd->OnRButtonDown();
					break;
				case WM_LBUTTONDBLCLK:
					wnd->OnLButtonDblClk();
					break;
				case WM_MOUSEMOVE:

					break;
			}
			return 0;
		case WM_COMMAND:
			wmId    = LOWORD(wParam); 
			wmEvent = HIWORD(wParam); 
			switch (wmEvent) {
				case ID_FILE_ABOUT:
					wnd->OnAbout();
					break;
				case ID_FILE_EXIT:
					PostQuitMessage(0);
					break;
			}
			break;
		case ID_ONCHATMESSAGE:
			wnd->OnChatMessage((ChatMessage *)wParam);
			break;		
		case ID_ONGROUPMESSAGE:
			wnd->OnGroupMessage((ChatMessage *)wParam);
			break;
		case ID_ORGANIZELISTS:
			wnd->OnOrganizeLists();
			break;
		case ID_SENDSERVERNOTIFY:
			wnd->SendServerNotify((ServerNotifyMessage*)wParam);
			break;
		case ID_CUSTOMNOTIFY:
			{
				int id = (int)wParam;
				Notification * p = (Notification *)lParam;
				wnd->OnCustomNotify(id,*p);
				delete p;
			}
			break;
 		case ID_APPLICATION_ACTIVATE:
			wnd->Activate();
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

MainWindow::MainWindow(void)
{
	HaveNetwork = 0;
	NetworkAdapter = "unknown";
	Wireless = 0;
	HelpBalloonsEnabled = 0;
}

MainWindow::~MainWindow(void)
{
}

void MainWindow::OnDestroy()
{
	UnloadTaskbar();
	LogOff();	
	activity.Stop();

}

void MainWindow::OnRButtonDown()
{
	print(L"taskbar right button click\n");
	OnLButtonDown();
}

void MainWindow::OnLButtonDown()
{
	print(L"taskbar left button click\n");
	switch(DisplayTaskbarMenu())
	{
	case ID_FILE_ABOUT:
		OnAbout();
		break;
	case ID_FILE_EDITPROFILE:
		EditProfile();
		break;
	case ID_FILE_EXIT:
		PostQuitMessage(0);
		DoEvents();
		break;
	case ID_FILE_LOGON:
		StartLogOn();
	case ID_FILE_LOGOFF:
		LogOff();
		break;
	case ID_FILE_CREATEEVENT:
		CreateEvent(L"");
		break;
	case ID_FILE_CREATEGROUP:
		CreateGroup(L"");
		break;
	case ID_FILE_CREATEUSER:
		NewAccount();
		break;
	case ID_FILE_SETPASSWORD:
		setpassword.Show();
		BringWindowToTop(setpassword.hWnd);
		break;
	case ID_FILE_NETWORKINFO:
		networkinfo.Show();
		BringWindowToTop(networkinfo.hWnd);
		break;
	case ID_FILE_AWAY:
		SetAwayStatus();
		break;
	case ID_FILE_VISIBLETOEVERYONE:
		SetPrivacyLevel(bcNormal);
		break;
	case ID_FILE_VISIBLETOBUDDIES:
		SetPrivacyLevel(bcVisibleToBuddies);
		break;
	case ID_FILE_HELPBALLOONS:
		ToggleHelpBalloons();
		break;
	}
}

void MainWindow::OnLButtonDblClk(){
	print(L"taskbar left button double click\n");
}

int MainWindow::Create()
{
	// load demo mode and info
	DemoMode = env.GetInt("DemoMode","on",0);
	DemoLocationId = env.GetInt("DemoMode","locationId",-1);
	DemoIdle = env.GetInt("DemoMode","idle",0);

	UpdateNetworkInfo();
	print(L"Network Info---------------------\n");
	if(Wireless)
		print(L"The network is wireless.\n");
	else 
		print(L"The network is wired.\n");
	print(L"Network Adapter: %S\n",NetworkAdapter.data());
	if(MacAddress.length>0){
		int i;
		print(L"MAC Address: (%2x",MacAddress[0]);
		for(i=1;i<MacAddress.length;i++){
			print(L":%2x",MacAddress[i]);
		}
		print(L")\n");
	} else {
		print(L"MAC Address: unknown\n");
	}
	print(L"---------------------------------\n");

	if(env.mode==L"LocationEditor"){
		print(L"Starting in Location Editor mode...\n");
		db.Connect();
		SkinnableWindow::Create(HWND_MESSAGE);
		SetWindowText(hWnd,L"BridgeClient's Main (Message-Only) Window");
		// subclass this window
		SetWindowLongPtr(hWnd,GWLP_WNDPROC,(LONG_PTR)MainWindow::WndProc);

		mapwindow.Create();
		mapwindow.LoadLocations();
		mapwindow.Show();
		mapwindow.ShowAllLocations();
		mapwindow.CreateBogusUser();
		CurrentLocation.LocationId = -2; //force update

		networkinfo.Create();
		networkinfo.Show();

		SetTimer(hWnd,0,500,NULL);
		return 1;
	} else {
		print(L"Starting in client mode...\n");
		_tcscpy(DateFormat,env.GetWideString(NULL,"dateformat",L"%m/%d/%y %I:%M %p"));
		COleDateTime now = COleDateTime::GetCurrentTime();
		print(L"%s\n",now.Format(DateFormat));

		ChatFlashPeriod = env.GetInt(NULL,"ChatFlashPeriod",1000);
		ChatWindow::LoadTranslator("ChatTranslator");

		ShowInTaskBar = 0;

		SkinnableWindow::Create(HWND_MESSAGE);
		SetWindowText(hWnd,L"BridgeClient's Main (Message-Only) Window");

		// subclass this window
		SetWindowLongPtr(hWnd,GWLP_WNDPROC,(LONG_PTR)MainWindow::WndProc);

		print(L"Create windows...\n");
		// create all the application windows in hidden state
		print(L"------ loginwindow...\n");
		loginwindow.Hide();
		loginwindow.Create();
#ifdef SUPERUSER
		loginwindow.status.SetText(L"Warning! SUPER USER mode.");
#endif

		print(L"------ mapwindow...\n");
		mapwindow.Hide();
		mapwindow.Create();

		print(L"------ sidebar...\n");
		sidebar.Hide();
		sidebar.Create(); // this will take care of all the list windows

		print(L"------ editprofile...\n");
		editprofile.Hide();
		editprofile.Create();

		print(L"------ viewprofile...\n");
		viewprofile.Hide();
		viewprofile.Create();

		print(L"------ about...\n");
		about.Hide();
		about.Create();

		print(L"------ alert...\n");
		alert.Hide();
		alert.Create();

		print(L"------ creategroup...\n");
		creategroup.Hide();
		creategroup.Create();

		print(L"------ editgroup...\n");
		editgroup.Hide();
		editgroup.Create();

		print(L"------ viewgroup...\n");
		viewgroup.Hide();
		viewgroup.Create();

		print(L"------ editevent...\n");
		editevent.Hide();
		editevent.Create();

		print(L"------ createevent...\n");
		createevent.Hide();
		createevent.Create();

		print(L"------ viewevent...\n");
		viewevent.Hide();
		viewevent.Create();

		print(L"------ newaccount...\n");
		newaccount.Hide();
		newaccount.Create();

		print(L"------ setpassword...\n");
		setpassword.Hide();
		setpassword.Create();

		print(L"------ networkinfo...\n");
		networkinfo.Hide();
		networkinfo.Create();

		print(L"------ awaywindow...\n");
		awaywindow.Hide();
		awaywindow.Create();

		print(L"------ newswindow...\n");
		newswindow.Hide();
		newswindow.Create();

		OnOrganizeLists();

		print(L"Load taskbar...\n");
		LoadTaskbar();

		// enable/disable privacy
		if(env.GetInt(NULL,"EnablePrivacy",0)==0){
			RemoveMenu(hTaskbarMenu,ID_FILE_VISIBLETOEVERYONE,MF_BYCOMMAND);
			RemoveMenu(hTaskbarMenu,ID_FILE_VISIBLETOBUDDIES,MF_BYCOMMAND);
		}

		// enable/disable account creation
		if(env.GetInt(NULL,"EnableAccountCreation",0)==0){
			RemoveMenu(hTaskbarMenu,ID_FILE_CREATEUSER,MF_BYCOMMAND);
		} 

		activity.Init(env.hInstance,hWnd);

		AddCustomNotification(bcSystem);

		UpdateTaskbar();

		StartLogOn();

		return 1;
	}
}

void MainWindow::StartLogOn()
{
	// now start a login
	if(DemoMode){
		print(L"***DEMO MODE***\n");
	}
	print(L"Starting logon sequence...\n");
	loginwindow.password.SetText(L"");
	loginwindow.Show();
	BringWindowToTop(loginwindow.hWnd);
	loginwindow.username.GrabFocus();

#ifdef SUPERUSER
	loginwindow.username.SetText(L"superuser");
	loginwindow.username.Disable();
	loginwindow.password.GrabFocus();
#endif
}

void MainWindow::LogOn(const TCHAR * uname, const TCHAR * pass)
{
	print(L"%s logging on\n",uname);

	loginwindow.status.SetText(env.GetWideString("login::status","SQLConnect",L"Connecting to database..."));
	DoEvents();

	// set db timeouts and connect
	db.ConnectionTimeout = env.GetInt("sql","ConnectionTimeout",db.ConnectionTimeout);
	db.CommandTimeout = env.GetInt("sql","CommandTimeout",db.CommandTimeout);
	db.Connect();
	ResumeAllCustomNotifications();

	// check for version-specific info
	loginwindow.status.SetText(env.GetWideString("login::status","VersionChecking",L"Checking for updates..."));
	DoEvents();
	VersionRecord versioninfo;
	db.GetVersionInfo(BUILD_NUMBER,versioninfo);
	if(versioninfo.ShowURL){
		newswindow.Show();
		BringWindowToTop(newswindow.hWnd);
		newswindow.SetURL(versioninfo.URL);
	}
	if(!versioninfo.AllowLogin){
		TCHAR buffer[100];
		_stprintf(buffer,L"You cannot login with this version (build %d).",BUILD_NUMBER);
		loginwindow.status.SetText(buffer);
		StartLogOn();
		return;
	}

	// Log on to the BridgeServer
	loginwindow.status.SetText(env.GetWideString("login::status","LoggingOn",L"Logging on to server..."));
	DoEvents();

	// first try V1 Security
	_bstr_t encryptedV1 = securityV1.Encrypt(pass,pass);
	int x = rpc.LogOn((TCHAR*)uname,(TCHAR*)encryptedV1);

	if(x==bsPasswordMismatch){
		// now try V2 Security
		_bstr_t encryptedV2 = securityV2.Encrypt(pass,pass);
		x = rpc.LogOn((TCHAR*)uname,(TCHAR*)encryptedV2);
	}

	switch(x){
		case bsOk:
			loginwindow.status.SetText(L""); // clear the login status
			break;
		case bsPasswordMismatch:
			print(L"Error: password doesn't match.\n");
			loginwindow.status.SetText(env.GetWideString("login::status","PasswordMismatch",L"Password doesn't match."));
			StartLogOn();
			return;	
		case bsUsernameMismatch:
			print(L"Error: username doesn't exist.\n");
			loginwindow.status.SetText(env.GetWideString("login::status","UsernameMismatch",L"User doesn't exist."));
			StartLogOn();
			return;	
		case bsFailed:
		default:
			print(L"Error: failed to log on %s\n",(TCHAR*)user.UserLogin);
			loginwindow.status.SetText(env.GetWideString("login::status","ServerNotFound",L"Unable to connect to server."));
			StartLogOn();
			return;	
	}

	// Now set up the lists
	loginwindow.status.SetText(L"Accessing user info...");
	DoEvents();
	db.GetUserId(uname,user.UserId);
	db.GetCachedUser(user.UserId,user,1);
	UpdateTaskbar();
	SetPrivacyLevel();
	
	CurrentLocation.SetDefault();
	CurrentLocation.LocationId = -2; // to force an update on the first time we estimate our location

	loginwindow.status.SetText(L"Updating map...");
	DoEvents();
	mapwindow.LoadLocations();
	mapwindow.DeepRefresh();

	// prepare the sidebar
	TCHAR sidebarTitle[255];
	_stprintf(sidebarTitle,L"%s - %s",(TCHAR*)user.UserLogin,sidebar.title.data());
	SetWindowText(sidebar.hWnd,sidebarTitle);
	sidebar.Show();

	loginwindow.status.SetText(L"Updating buddy list...");
	DoEvents();
	buddylist.DeepRefreshEdit(0); DoEvents();
	buddylist.DeepRefresh(0); DoEvents();
	loginwindow.status.SetText(L"Updating location list...");
	DoEvents();
	locationlist.DeepRefreshEdit(0); DoEvents();
	locationlist.DeepRefresh(0); DoEvents();
	loginwindow.status.SetText(L"Updating group list...");
	DoEvents();
	grouplist.DeepRefreshEdit(0); DoEvents();
	grouplist.DeepRefresh(0); DoEvents();
	loginwindow.status.SetText(L"Updating event list...");
	DoEvents();
	eventlist.DeepRefreshEdit(0); DoEvents();
	eventlist.DeepRefresh(0); DoEvents();

	loginwindow.status.SetText(L"");
	DoEvents();

	loginwindow.Hide();

	activity.Start();
	SetTimer(hWnd,0,1000,NULL);

}

void MainWindow::LogOff()
{
	KillTimer(hWnd,0);

	CurrentLocation.LocationId = NullLocationId;
	db.SetUserLocation(user.UserId,CurrentLocation.LocationId);
	rpc.ServerNotifyImmediate(bcUser,user.UserId,bcMove,CurrentLocation.LocationId);

	activity.Stop();
	rpc.LogOff();
	user.LoggedIn = 0;
	UpdateTaskbar();
	sidebar.Hide();
	mapwindow.Hide();
	createevent.Hide();
	viewevent.Hide();
	editevent.Hide();
	creategroup.Hide();
	editgroup.Hide();
	viewgroup.Hide();
	editprofile.Hide();
	setpassword.Hide();
	awaywindow.Hide();
	loginwindow.Show();

	// destroy all the chat windows
	CloseAllChatWindows();
	CloseAllChatRooms();

	// wait for all current notifications to finish
	DoEvents();
	SuspendAllCustomNotifications();

	db.Disconnect();
	db.ClearCache();
}

void MainWindow::OnAbout()
{
	print(L"Displaying About information\n");
	about.Show();
	BringWindowToTop(about.hWnd);
}

void MainWindow::OnChatMessage(ChatMessage * msg)
{
	if(theApp.user.Away && msg->senderId!=theApp.user.UserId){

		// show the message if we already have a window, but don't flash it
		ChatWindow * cw;	
		cw = FindChatWindow(msg->sender.data());
		if(cw)
			cw->FriendMessage(msg->sender.data(),msg->text.data());

		// send the automatic reply
		std::wstring buffer = L" (automatic reply) ";
		buffer += theApp.user.AwayMessage;
		if(!rpc.SendInstantMessage(msg->senderId,buffer.data()))
			print(L"MainWindow::OnChatMessage: Failed to send away message to %s\n",msg->sender.data());

		delete msg;
		return;
	}

	// find the chat window this is meant for
	ChatWindow * cw;
	cw = FindChatWindow(msg->sender.data());
	if(cw)
		cw->FriendMessage(msg->sender.data(),msg->text.data());
	else {
		// it is a request for a chat session
		cw = OpenChatWindow(msg->senderId,msg->sender.data());
		cw->Show();
		cw->FriendMessage(msg->sender.data(),msg->text.data());
	}

	// flash the window until it is foreground
	FLASHWINFO fi;
	fi.cbSize = sizeof(FLASHWINFO);
	fi.dwTimeout = ChatFlashPeriod;
	fi.dwFlags = FLASHW_TIMERNOFG|FLASHW_ALL;
	fi.hwnd = cw->hWnd;
	fi.uCount = -1;
	FlashWindowEx(&fi);

	delete msg;
}

void MainWindow::OnGroupMessage(ChatMessage * msg)
{
	if(theApp.user.Away){
		delete msg;
		return;
	}
	
	// find the chat window this is meant for
	ChatRoom * cw;
	cw = FindChatRoom(msg->groupId);
	if(cw)
		cw->FriendMessage(msg->sender.data(),msg->text.data());
	else {
		// it is a request for a group chat session
		cw = OpenChatRoom(msg->groupId,msg->group.data());

		cw->SystemMessage(env.GetWideString("chatroom","InvitationHeader",L""));
		cw->FriendMessage(msg->sender.data(),msg->text.data());
		cw->SystemMessage(env.GetWideString("chatroom","InvitationSuffix",L""));

		cw->Show();

		// set the interaction state
		cw->RespondToInvitation();
	}
	BringWindowToTop(cw->hWnd);
	SetForegroundWindow(cw->hWnd);
	delete msg;
}

ChatWindow * MainWindow::FindChatWindow(const TCHAR * recipient)
{
	cwpos i;
	for(i=chatwindows.begin();i!=chatwindows.end();i++){
		ChatWindow * cw = *i;
		if(cw->recipient==recipient)
			return cw;
	}
	return NULL;
}

ChatWindow * MainWindow::FindChatWindow(int id)
{
	cwpos i;
	for(i=chatwindows.begin();i!=chatwindows.end();i++){
		ChatWindow * cw = *i;
		if(cw->recipientId==id)
			return cw;
	}
	return NULL;
}

ChatWindow * MainWindow::NewChatWindow(int id, const TCHAR * recipient)
{
	ChatWindow * cw = new ChatWindow(id,recipient);
	chatwindows.push_back(cw);
	cw->Hide();
	cw->Create();
	return cw;
}

ChatWindow * MainWindow::OpenChatWindow(int id, const TCHAR * recipient)
{
	ChatWindow * cw = FindChatWindow(id);
	if(cw)
		return cw;
	else {
		print(L"Creating chatwindow for %s\n",recipient);
		return NewChatWindow(id,recipient);
	}
}

int MainWindow::CloseChatWindow(const TCHAR * recipient)
{
	print(L"Destroying chatwindow for %s\n",recipient);
	cwpos i;
	for(i=chatwindows.begin();i!=chatwindows.end();i++){
		ChatWindow * cw = *i;
		if(cw->recipient==recipient){
			DestroyWindow(cw->hWnd);
			delete cw;
			chatwindows.erase(i);
			return 1;
		}
	}
	return 0;
}

int MainWindow::CloseChatWindow(int id)
{
	cwpos i;
	for(i=chatwindows.begin();i!=chatwindows.end();i++){
		ChatWindow * cw = *i;
		if(cw->recipientId==id){
			print(L"Destroying chatwindow for %s\n",cw->recipient.data());
			DestroyWindow(cw->hWnd);
			delete cw;
			chatwindows.erase(i);
			return 1;
		}
	}
	return 0;
}

void MainWindow::CloseAllChatWindows()
{
	cwpos i;
	for(i=chatwindows.begin();i!=chatwindows.end();i++){
		ChatWindow * cw = *i;
		print(L"Destroying chatwindow for %s\n",cw->recipient.data());
		DestroyWindow(cw->hWnd);
		delete cw;
	}
	chatwindows.clear();
}

//////// CHAT ROOM routines
ChatRoom * MainWindow::FindChatRoom(const TCHAR * recipient)
{
	crpos i;
	for(i=chatrooms.begin();i!=chatrooms.end();i++){
		ChatRoom * cw = *i;
		if(cw->recipient==recipient)
			return cw;
	}
	return NULL;
}

ChatRoom * MainWindow::FindChatRoom(int id)
{
	crpos i;
	for(i=chatrooms.begin();i!=chatrooms.end();i++){
		ChatRoom * cw = *i;
		if(cw->recipientId==id)
			return cw;
	}
	return NULL;
}


ChatRoom * MainWindow::NewChatRoom(int id, const TCHAR * recipient)
{
	ChatRoom * cw = new ChatRoom(id,recipient);
	chatrooms.push_back(cw);
	cw->Hide();
	cw->Create();
	return cw;
}

ChatRoom * MainWindow::OpenChatRoom(int id, const TCHAR * recipient)
{
	ChatRoom * cw = FindChatRoom(id);
	if(cw)
		return cw;
	else {
		print(L"Creating chat room for %s\n",recipient);
		return NewChatRoom(id,recipient);
	}
}


int MainWindow::CloseChatRoom(const TCHAR * recipient)
{
	print(L"Destroying chat room for %s\n",recipient);
	crpos i;
	for(i=chatrooms.begin();i!=chatrooms.end();i++){
		ChatRoom * cw = *i;
		if(cw->recipient==recipient){
			DestroyWindow(cw->hWnd);
			delete cw;
			chatrooms.erase(i);
			return 1;
		}
	}
	return 0;
}

int MainWindow::CloseChatRoom(int id)
{
	crpos i;
	for(i=chatrooms.begin();i!=chatrooms.end();i++){
		ChatRoom * cw = *i;
		if(cw->recipientId==id){
			print(L"Destroying chat room for %s\n",cw->recipient.data());
			DestroyWindow(cw->hWnd);
			delete cw;
			chatrooms.erase(i);
			return 1;
		}
	}
	return 0;
}
void MainWindow::CloseAllChatRooms()
{
	crpos i;
	for(i=chatrooms.begin();i!=chatrooms.end();i++){
		ChatRoom * cw = *i;
		print(L"Destroying chat room for %s\n",cw->recipient.data());
		DestroyWindow(cw->hWnd);
		delete cw;
	}
	chatrooms.clear();
}


void MainWindow::OnOrganizeLists()
{
	sidebar.Organize();
	return;
}

void MainWindow::AddLocationSignature(int locationId)
{
	int Mac1=0, Mac2=0;
	TCHAR MACstr[100];

	// determine the locations
	if(Wireless){
		netcard.Update();
		memcpy(&HubMAC, netcard.info.EtherAddress, 6);
		// Converts MacAddress into 2 ints.
		memcpy(&Mac1, &HubMAC, 4);
		memcpy(&Mac2, &(HubMAC.M5), 2);
		SignalStrength = netcard.info.SignalStrength;
		_stprintf(MACstr,L"%2x:%2x:%2x:%2x:%2x:%2x",
			HubMAC.M1,HubMAC.M2,HubMAC.M3,
			HubMAC.M4,HubMAC.M5,HubMAC.M6);
	} else {
		memcpy(&Mac1, MacAddress.data, 4);
		memcpy(&Mac2, &(MacAddress[4]),2);
		SignalStrength = 0;
		_stprintf(MACstr,L"%2x:%2x:%2x:%2x:%2x:%2x",
			MacAddress[0],MacAddress[1],MacAddress[2],
			MacAddress[3],MacAddress[4],MacAddress[5]);
	}
	db.AddLocationSignature(locationId,SignalStrength,Mac1,Mac2);
	print(L"Added signature: id=%d MAC=(%s) strength = %d\n",
		locationId,MACstr,SignalStrength);
}

void MainWindow::OnTimer()
{
	if(env.mode==L"LocationEditor"){
		// maintain our network connectivity
		UpdateNetworkInfo();
		if(!HaveNetwork){
			db.LoseConnection();
			SuspendAllCustomNotifications();
		} else if(!db.connected) {
			db.Connect();
			ResumeAllCustomNotifications();
		}

		// estimate our current location and update the map
		if(UpdateLocation()){
			mapwindow.UpdateBogusUser(CurrentLocation.LocationId);
			print(L"Current Location is %s\n",
				(TCHAR*)CurrentLocation.LocationName);
		}
	} else {
		rpc.BeginTransaction();

		// maintain our network connectivity
		UpdateNetworkInfo();
		if(!HaveNetwork){
			db.LoseConnection();
			SuspendAllCustomNotifications();
			rpc.EndTransaction();
			return;
		} else if(!db.connected){
			// re-connect to the SQL server
			db.Connect();
			ResumeAllCustomNotifications();
			// and re-logon to the BridgeServer
			db.GetCachedUser(user.UserId,user,1);
			if(!user.LoggedIn){
				print(L"Lost connection to the server, automatically logging back in...\n");
				LogOff();
				LogOn((TCHAR*)user.UserLogin,(TCHAR*)user.Password);
			}
		}

		// notify BridgeServer if the location has changed
		if(UpdateLocation()){
			db.SetUserLocation(user.UserId,CurrentLocation.LocationId);
			rpc.ServerNotify(bcUser,user.UserId,bcMove,CurrentLocation.LocationId);
			// have the locationlist reflect this change also
			locationlist.OnTimer();
			mapwindow.UpdateUser(user.UserId);
			mapwindow.GlideToUser(user.UserId);
			print(L"Current Location is %s\n",(TCHAR*)CurrentLocation.LocationName);
		}

		// update idle state
		int idleSeconds = activity.GetTimeSinceLastEvent();
		if(idleSeconds%10==0 && idleSeconds>0)
			print(L"idle for %d seconds.\n",idleSeconds);
		if((DemoMode && DemoIdle) || idleSeconds > env.GetInt(NULL,"idleafter",5*60)){
			if(!user.Idle){
				db.SetUserIdle(user.UserId,1,idleSeconds);
				db.GetCachedUser(user.UserId,user);
				rpc.ServerNotify(bcUser,user.UserId,bcIdle,idleSeconds);
				UpdateTaskbar();
			}
		} else {
			if(user.Idle){
				db.SetUserIdle(user.UserId,0,0);
				db.GetCachedUser(user.UserId,user);
				rpc.ServerNotify(bcUser,user.UserId,bcActive,0);
				UpdateTaskbar();
			}
		}

		// finally just ping the server
		//rpc.ServerNotify(bcSystem,user.UserId,bcAlive,0);

		rpc.EndTransaction();
	}
}

// messages from the server
void MainWindow::OnCustomNotify(int id, Notification& n)
{
	if(id==bcSystem){
		switch(n.reason){
			case bcLoggedOnElsewhere:
				print(L"OnLogOffNotify: %s logged on somewhere else, logging off...\n",(TCHAR*)user.UserLogin);
				LogOff();
				break;
			case bcServerShutdown:
				print(L"OnLogOffNotify: Server is shutting down, logging off...\n");
				LogOff();
				break;
		}
	}
}

void MainWindow::Alert(const TCHAR * msg, int x, int y)
{
	alert.message.SetText(msg);
	alert.SetLocation(x-alert.size.Width,y-alert.size.Height);
	alert.Show();
	BringWindowToTop(alert.hWnd);
}

void MainWindow::NewAccount()
{
	newaccount.DeepRefresh();
	newaccount.Show();
	BringWindowToTop(newaccount.hWnd);
}

void MainWindow::EditProfile()
{
	editprofile.DeepRefresh();
	editprofile.Show();
	BringWindowToTop(editprofile.hWnd);
}

void MainWindow::CreateGroup(const TCHAR * name)
{
	creategroup.DeepRefresh(name);
	creategroup.Show();
	BringWindowToTop(creategroup.hWnd);
}

void MainWindow::EditGroup(int groupId)
{
	editgroup.DeepRefresh(groupId);
	editgroup.Show();
	BringWindowToTop(editgroup.hWnd);
}

void MainWindow::ViewGroup(int groupId)
{
	viewgroup.DeepRefresh(groupId);
	viewgroup.Show();
	BringWindowToTop(viewgroup.hWnd);
}

void MainWindow::CreateEvent(const TCHAR * name)
{
	createevent.DeepRefresh(name);
	createevent.Show();
	BringWindowToTop(createevent.hWnd);
}

void MainWindow::EditEvent(int eventId)
{
	editevent.DeepRefresh(eventId);
	editevent.Show();
	BringWindowToTop(editevent.hWnd);
}

void MainWindow::ViewEvent(int eventId)
{
	viewevent.DeepRefresh(eventId);
	viewevent.Show();
	BringWindowToTop(viewevent.hWnd);
}

int MainWindow::UpdateNetworkInfo()
{
	// get network info
	int OldWireless = Wireless;
	std::string OldNetworkAdapter = NetworkAdapter;
	std::wstring OldIPAddress = IPAddress;
	Vector<unsigned char> OldMacAddress = MacAddress;

	HaveNetwork = IsInternetConnection();

	Wireless = IsNetworkWireless();
	NetworkAdapter = GetNetworkAdapterName();
	GetMacAddress(MacAddress);

	IPAddress = env.GetWideString(NULL,"LocalIPAddress",L"auto");
	if(IPAddress==L"auto"){
		IPAddress = GetLocalIPAddress();
	}

	if(Wireless!=OldWireless)
		return 1;
	else if(NetworkAdapter!=OldNetworkAdapter)
		return 1;
	else if(OldMacAddress!=MacAddress)
		return 1;
	else if(OldIPAddress!=IPAddress)
		return 1;
	else
		return 0;
}

int MainWindow::UpdateLocation()
{	
	int LocationId;

	if(DemoMode){
		LocationId = DemoLocationId;	
	} else {

		int Mac1=0, Mac2=0;

		// determine the locations
		if(Wireless){
			netcard.Update();
			memcpy(&HubMAC, netcard.info.EtherAddress, 6);
			// Converts MacAddress into 2 ints.
			memcpy(&Mac1, &HubMAC, 4);
			memcpy(&Mac2, &(HubMAC.M5), 2);
			SignalStrength = netcard.info.SignalStrength;
		} else {
			memcpy(&Mac1, MacAddress.data, 4);
			memcpy(&Mac2, &(MacAddress[4]),2);
			SignalStrength = 0;
		}

		db.EstimateLocation(Mac1,Mac2,SignalStrength,LocationId);
	}
	
	if(LocationId!=CurrentLocation.LocationId){
		db.GetCachedLocation(LocationId,CurrentLocation);
		return 1;
	} else
		return 0;
}

void MainWindow::PostServerNotify(int type, int theId, int reason, int param)
{
	ServerNotifyMessage * p = new ServerNotifyMessage;
	p->type = type;
	p->theId = theId;
	p->reason = reason;
	p->param = param;
	PostMessage(hWnd,ID_SENDSERVERNOTIFY,(WPARAM)p,0);
}

void MainWindow::SendServerNotify(ServerNotifyMessage * p)
{
	rpc.ServerNotifyImmediate(p->type,p->theId,p->reason,p->param);
	delete p;
}

// set away status 
// call with no args to show window
void MainWindow::SetAwayStatus(int away, const TCHAR * message)
{
	if(away==-1){
		awaywindow.DeepRefresh();
		awaywindow.Show();
		BringWindowToTop(awaywindow.hWnd);
	} else {
		rpc.BeginTransaction();
		user.Away = away;
		user.AwayMessage = message;
		db.SetUserAway(user.UserId,user.Away,user.AwayMessage);
		UpdateTaskbar();
		if(away){
			rpc.ServerNotify(bcUser,user.UserId,bcAway,0);
		} else {
			rpc.ServerNotify(bcUser,user.UserId,bcBack,0);
		}
		rpc.EndTransaction();
	}
}

void MainWindow::SetPrivacyLevel(int privacy)
{
	if(privacy!=-1){
		rpc.BeginTransaction();
		db.SetUserPrivacyLevel(user.UserId,privacy);
		user.PrivacyLevel = privacy;
		rpc.ServerNotify(bcUser,user.UserId,bcPrivacy,privacy);
		rpc.EndTransaction();
		UpdateTaskbar();
	}
}

void MainWindow::ShowUserOnMap(int id)
{
	mapwindow.Show();
	BringWindowToTop(mapwindow.hWnd);
	mapwindow.GlideToUser(id);
}

void MainWindow::ViewProfile(int id)
{
	if(id==-1)
		id = user.UserId;
	viewprofile.DeepRefresh(id);
	viewprofile.Show();
	BringWindowToTop(viewprofile.hWnd);
}

void MainWindow::ChatWithUser(int id, const TCHAR * name)
{
	ChatWindow * cw = OpenChatWindow(id,name);
	cw->Show();
	BringWindowToTop(cw->hWnd);
	cw->msg.GrabFocus();
}

void MainWindow::BeginDiscussion(int id, const TCHAR * name)
{
	ChatRoom * cw = OpenChatRoom(id,name);
	cw->Show();
	BringWindowToTop(cw->hWnd);
	cw->msg.GrabFocus();

	// join the group meeting
	db.EnterGroupMeeting(user.UserId,id);

	// notify everyone of the start of the meeting
	rpc.ServerNotifyImmediate(bcGroup,id,bcActive,user.UserId);

	// set the interaction state of the chat room to invitation
	cw->SystemMessage(env.GetWideString("chatroom","InvitationInstructions",L""));
	cw->SendInvitation();
}

void MainWindow::JoinDiscussion(int id, const TCHAR * name)
{
	ChatRoom * cw = OpenChatRoom(id,name);
	cw->Show();
	BringWindowToTop(cw->hWnd);
	cw->msg.GrabFocus();

	cw->AcceptInvitation();
}

void MainWindow::ToggleHelpBalloons()
{
	HelpBalloonsEnabled = !HelpBalloonsEnabled;
	if(HelpBalloonsEnabled){
		SkinnableWindow::EnableHelpBalloons();
		CheckMenuItem(hTaskbarMenu,ID_FILE_HELPBALLOONS,MF_CHECKED);
	} else {
		SkinnableWindow::DisableHelpBalloons();
		CheckMenuItem(hTaskbarMenu,ID_FILE_HELPBALLOONS,MF_UNCHECKED);
	}
}

const TCHAR * MainWindow::GetDisplayName(LocationRecord & record)
{
	if(record.Visible)
		return (TCHAR *)record.LocationName;
	else {
		if(record.ParentId!=NullLocationId && record.ParentId!=OtherLocationId){
			LocationRecord parent;
			db.GetCachedLocation(record.ParentId,parent);
			return GetDisplayName(parent);
		} else {
			return (TCHAR *)record.LocationName;
		}
	}
}
