//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code 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 3 of the License, or (at your option) any later version.

// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
// in the hope that it will be useful, but with permitted additional restrictions 
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
// distributed with this program. You should have received a copy of the 
// GNU General Public License along with permitted additional restrictions 
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection

#ifdef WOLAPI_INTEGRATION

//	wol_chat.cpp
//	ajw 7/8/98

#include "function.h"
#include "iconlist.h"
#include "WolapiOb.h"
#include "SEditDlg.h"
#include "WolStrng.h"
#include "ToolTip.h"

//#include "WolDebug.h"

void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE );
void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap );
bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame );
bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame );
bool ExitChatChannel( WolapiObject* pWO );
void CreateChatChannel( WolapiObject* pWO );
bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi );
bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex );

enum LIST_EXPAND_STATE
{
	LES_NORMAL,
	LES_CHANNELS_EXPANDED,
	LES_USERS_EXPANDED,
};
static LIST_EXPAND_STATE	lesCurrent = LES_NORMAL;

bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist );
bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist );
void ResizeChannelList( IconListClass& chanlist, bool bExpand );
void ResizeUserList( IconListClass& userlist, bool bExpand );

bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind );

extern CREATEGAMEINFO WOL_CreateGame_Dialog( WolapiObject* pWO );

static int d_chanlist_w;
static int d_chanlist_h;
static int d_chanlist_x;
static int d_chanlist_y;

static int d_userlist_w;
static int d_userlist_h;
static int d_userlist_x;
static int d_userlist_y;

#define DRAWTOGDOWN		Turn_Off()
#define DRAWTOGUP		Turn_On()

//***********************************************************************************************
int WOL_Chat_Dialog( WolapiObject* pWO )
{
	int rc;
	bool bFirsttime = true;
	bool bHackFocus = true;

	//------------------------------------------------------------------------
	//	Dialog & button dimensions
	//------------------------------------------------------------------------
	int d_dialog_w = 320 *RESFACTOR;											// dialog width
	int d_dialog_h = 200 *RESFACTOR;											// dialog height
	int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2);				// dialog x-coord
	int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2);				// centered y-coord
	int d_dialog_cx = d_dialog_x + (d_dialog_w / 2);		// center x-coord

	int d_text_h = 12;
	int d_margin1 = 34;															// large margin
	int d_margin2 = 14;															// small margin

	int d_chatlist_w = 340;
	int d_chatlist_x = d_dialog_x + d_margin1;
	int d_chatlist_y = d_dialog_y + d_margin2 + d_margin1 + 27;
	int d_chatlist_h = 337 - d_chatlist_y;

	d_chanlist_w = 227;
	d_chanlist_h = 50 * RESFACTOR;
	d_chanlist_x = d_dialog_x + d_dialog_w - (d_margin1 + d_chanlist_w);
	d_chanlist_y = d_chatlist_y;

	d_userlist_w = d_chanlist_w;
//		int d_userlist_h = ((10 * 6) + 3) *RESFACTOR;
	d_userlist_x = d_chanlist_x;
	d_userlist_y = d_chanlist_y + d_chanlist_h + 14 + 5;

	d_userlist_h = d_chatlist_y + d_chatlist_h - d_userlist_y;

	int d_action_w = 100;
	int d_action_h = 9 *RESFACTOR;
	int d_action_x = d_dialog_x + 500;
	int d_action_y = 365;

//	int d_chanpriv_w = 60;
//	int d_chanpriv_h = 9 *RESFACTOR;
//	int d_chanpriv_x = d_dialog_x + 150;
//	int d_chanpriv_y = d_action_y;

//	int d_cgame_w = 60;
//	int d_cgame_h = 9 *RESFACTOR;
//	int d_cgame_x = d_dialog_x + 390; //d_dialog_cx - d_cgame_w / 2;
//	int d_cgame_y = d_action_y;

	int d_back_w = 100;
	int d_back_h = 9 *RESFACTOR;
	int d_back_x = d_dialog_x + 100;
	int d_back_y = d_action_y;

	int d_join_w = 100;
	int d_join_h = 9 *RESFACTOR;
	int d_join_x = d_dialog_x + 210;
	int d_join_y = d_action_y;

	int d_create_w = 100;
	int d_create_h = 9 *RESFACTOR;
	int d_create_x = d_dialog_x + 320;	//((d_dialog_w * 5) / 6) - (d_create_w / 2);
	int d_create_y = d_action_y;

	int d_send_w = d_chanlist_x + d_chanlist_w - d_chatlist_x;
	int d_send_h = 9 *RESFACTOR;
	int d_send_x = d_chatlist_x;
	int d_send_y = d_chatlist_y + d_chatlist_h + 5;

	//------------------------------------------------------------------------
	//	Button Enumerations
	//------------------------------------------------------------------------
	enum 
	{
		BUTTON_DISCONNECT = 100,	//	Note: standard WOL button IDs must match values in WolapiObject::PrepareButtonsAndIcons().
		BUTTON_LEAVE,
		BUTTON_REFRESH,
		BUTTON_SQUELCH,
		BUTTON_BAN,
		BUTTON_KICK,
		BUTTON_FINDPAGE,
		BUTTON_OPTIONS,
		BUTTON_LADDER,
		BUTTON_HELP,

		BUTTON_CHATLIST,
		BUTTON_CHANLIST,
		BUTTON_USERLIST,
		BUTTON_SENDEDIT,
		BUTTON_ACTION,
//		BUTTON_CGAME,
		BUTTON_CREATE,
		BUTTON_JOIN,
		BUTTON_BACK,
		BUTTON_EXPANDCHANNELS,
		BUTTON_EXPANDUSERS,
		BUTTON_RANKRA,
		BUTTON_RANKAM
	};

	//------------------------------------------------------------------------
	//	Redraw values: in order from "top" to "bottom" layer of the dialog
	//------------------------------------------------------------------------
	typedef enum {
		REDRAW_NONE = 0,
		REDRAW_BACKGROUND,
		REDRAW_ALL = REDRAW_BACKGROUND
	} RedrawType;

	//------------------------------------------------------------------------
	//	Dialog variables
	//------------------------------------------------------------------------
	RedrawType display = REDRAW_ALL;		// redraw level
	bool process = true;						// process while true
	KeyNumType input;

	TTimerClass<SystemTimerClass> lastclick_timer;
	int lastclick_idx = 0;					// index of item last clicked on
	RemapControlType * scheme = GadgetClass::Get_Color_Scheme();

	//------------------------------------------------------------------------
	//	Buttons
	//------------------------------------------------------------------------
	GadgetClass *commands;										// button list

	char* pShpExpand = (char*)MFCD::Retrieve( "exp.shp" );
	char* pShpUnexpand = (char*)MFCD::Retrieve( "unexp.shp" );

	IconListClass chatlist( BUTTON_CHATLIST, d_chatlist_x, d_chatlist_y, d_chatlist_w, d_chatlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 500 );
	ShapeButtonClass ExpandChanBtn( BUTTON_EXPANDCHANNELS, pShpExpand, d_chanlist_x + d_chanlist_w - 17, d_chanlist_y - 14 );
	IconListClass chanlist( BUTTON_CHANLIST, d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 );
	ShapeButtonClass ExpandUserBtn( BUTTON_EXPANDUSERS, pShpExpand, d_userlist_x + d_userlist_w - 17, d_userlist_y - 14 );
	IconListClass userlist( BUTTON_USERLIST, d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 );
	TextButtonClass ActionBtn( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w );
	TextButtonClass CreateBtn( BUTTON_CREATE, TXT_WOL_NEWSOMETHING, TPF_BUTTON, d_create_x, d_create_y, d_create_w );
	TextButtonClass JoinBtn( BUTTON_JOIN, TXT_WOL_JOIN, TPF_BUTTON, d_join_x, d_join_y, d_join_w );
	TextButtonClass BackBtn( BUTTON_BACK, TXT_WOL_BACK, TPF_BUTTON, d_back_x, d_back_y, d_back_w );
	char* szRecordToStartWith;
	if( pWO->bShowRankRA )
		szRecordToStartWith = pWO->szMyRecord;
	else
		szRecordToStartWith = pWO->szMyRecordAM;
	StaticButtonClass chatlistTitle( 0, szRecordToStartWith, TPF_TYPE, d_chatlist_x + 2, d_chatlist_y - 13, d_chatlist_w - 4, 12 );
	StaticButtonClass chanlistTitle( 0, "", TPF_TYPE, d_chanlist_x + 2, d_chanlist_y - 16 + 4, d_chanlist_w - 4 - 16, 12 );
	StaticButtonClass userlistTitle( 0, TXT_WOL_NOUSERLIST, TPF_TYPE, d_userlist_x + 2, d_userlist_y - 16 + 4, d_userlist_w - 4 - 16 * 4, 12 );

	char szSendBuffer[MAXCHATSENDLENGTH] = "";
	EditClass sendedit( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h );

	char* pShpRankRA = (char*)MFCD::Retrieve( "rank_ra.shp" );
	char* pShpRankAM = (char*)MFCD::Retrieve( "rank_am.shp" );
	ShapeButtonClass RankRABtn( BUTTON_RANKRA, pShpRankRA, d_userlist_x + d_userlist_w - ( 16 * 4 + 1 ), d_userlist_y - 14 );
	ShapeButtonClass RankAMBtn( BUTTON_RANKAM, pShpRankAM, d_userlist_x + d_userlist_w - ( 16 * 3 + 1 ), d_userlist_y - 14 );
	//	Change draw behavior of toggle buttons.
	RankRABtn.ReflectButtonState = true;
	RankAMBtn.ReflectButtonState = true;

	//	Build the button list.
	commands = pWO->pShpBtnDiscon;
	pWO->pShpBtnLeave->Add_Tail(*commands);
	pWO->pShpBtnRefresh->Add_Tail(*commands);
	pWO->pShpBtnSquelch->Add_Tail(*commands);
	pWO->pShpBtnBan->Add_Tail(*commands);
	pWO->pShpBtnKick->Add_Tail(*commands);
	pWO->pShpBtnFindpage->Add_Tail(*commands);
	pWO->pShpBtnOptions->Add_Tail(*commands);
	pWO->pShpBtnLadder->Add_Tail(*commands);
	pWO->pShpBtnHelp->Add_Tail(*commands);
	chatlist.Add_Tail(*commands);
	ExpandChanBtn.Add_Tail(*commands);
	chanlist.Add_Tail(*commands);
	ExpandUserBtn.Add_Tail(*commands);
	userlist.Add_Tail(*commands);
	ActionBtn.Add_Tail(*commands);
	CreateBtn.Add_Tail(*commands);
//	CreatePrivBtn.Add_Tail(*commands);
	JoinBtn.Add_Tail(*commands);
	BackBtn.Add_Tail(*commands);
//	CGameBtn.Add_Tail(*commands);
	chatlistTitle.Add_Tail(*commands);
	chanlistTitle.Add_Tail(*commands);
	userlistTitle.Add_Tail(*commands);
	sendedit.Add_Tail(*commands);

	//	Tooltips...
	DWORD				timeToolTipAppear;
	ToolTipClass*		pToolTipHead = NULL;		//	Head of list of ToolTips that parallels gadget list.
	ToolTipClass*		pToolTipHitLast = NULL;		//	ToolTip the mouse was last over, or null.

	ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon;
	pToolTip->next = pWO->pTTipLeave;
	pToolTip = pToolTip->next;
	pToolTip->next = pWO->pTTipRefresh;
	pToolTip = pToolTip->next;
	pToolTip->next = pWO->pTTipSquelch;
	pToolTip = pToolTip->next;
	pToolTip->next = pWO->pTTipBan;
	pToolTip = pToolTip->next;
	pToolTip->next = pWO->pTTipKick;
	pToolTip = pToolTip->next;
	pToolTip->next = pWO->pTTipFindpage;
	pToolTip = pToolTip->next;
	pToolTip->next = pWO->pTTipOptions;
	pToolTip = pToolTip->next;
	pToolTip->next = pWO->pTTipLadder;
	pToolTip = pToolTip->next;
	pToolTip->next = pWO->pTTipHelp;
	pToolTip = pToolTip->next;
	ToolTipClass TTipChanExpand( &ExpandChanBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandChanBtn.X + 8, ExpandChanBtn.Y - 9, true );
	pToolTip->next = &TTipChanExpand;
	pToolTip = pToolTip->next;
	ToolTipClass TTipUserExpand( &ExpandUserBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandUserBtn.X + 8, ExpandUserBtn.Y - 9, true );
	pToolTip->next = &TTipUserExpand;
	pToolTip = pToolTip->next;
	ToolTipClass TTipChanList( &chanlist, 0, chanlist.X + 1, chanlist.Y + 1, true, true );
	pToolTip->next = &TTipChanList;
	pToolTip = pToolTip->next;
	ToolTipClass TTipJoin( &JoinBtn, TXT_WOL_TTIP_JOIN, d_join_x + d_join_w/2, d_join_y - 6 );
	pToolTip->next = &TTipJoin;
	pToolTip = pToolTip->next;
	ToolTipClass TTipBack( &BackBtn, TXT_WOL_TTIP_BACK, d_back_x + d_back_w/2, d_back_y - 6 );
	pToolTip->next = &TTipBack;
	pToolTip = pToolTip->next;
	ToolTipClass TTipCreate( &CreateBtn, TXT_WOL_TTIP_CREATE, d_create_x + d_create_w/2, d_create_y - 6 );
	pToolTip->next = &TTipCreate;
	pToolTip = pToolTip->next;
	ToolTipClass TTipAction( &ActionBtn, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true );
	pToolTip->next = &TTipAction;
	pToolTip = pToolTip->next;
	ToolTipClass TTipRankRA( &RankRABtn, TXT_WOL_TTIP_RANKRA, RankRABtn.X + 8, RankRABtn.Y - 9, true );
	pToolTip->next = &TTipRankRA;
	pToolTip = pToolTip->next;
	ToolTipClass TTipRankAM( &RankAMBtn, TXT_WOL_TTIP_RANKAM, RankAMBtn.X + 8, RankAMBtn.Y - 9, true );
	pToolTip->next = &TTipRankAM;
	pToolTip = pToolTip->next;
	pToolTip->next = NULL;

	//........................................................................
	// List boxes
	//........................................................................
	int tabs[] = { 150 };			//	tabs for channel list
	chanlist.Set_Tabs( tabs );

//	Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_TEXT);

	lastclick_timer = 0;

	//	Tell WolapiObject about lists to use for output.
	//	(Sure wish I'd gone against the grain and made this dialog a class...)
	pWO->LinkToChatDlg( &chatlist, &chanlist, &userlist, &userlistTitle );

	if( !pWO->bChatShownBefore )
	{
		//	Print message of the day.
		chatlist.Add_Item( pWO->pChatSink->szMotd, NULL, NULL, ICON_SHAPE );
	}
	else
	{
		//	We have returned to the chat dialog after being in either game setup or an actual game.
		pWO->RestoreChat();
		pWO->DeleteSavedChat();

		if( pWO->bReturningAfterGame )
			pWO->RejoinLobbyAfterGame();
		else
		{
			if( pWO->pChatSink->bGotKickedTrigger )
			{
				//	We got kicked out of a game setup.
				WOL_PrintMessage( chatlist, TXT_WOL_YOUWEREKICKEDFROMGAME, WOLCOLORREMAP_KICKORBAN );
				pWO->pChatSink->bGotKickedTrigger = false;
			}
		}

		if( pWO->iLobbyReturnAfterGame != -1 )
		{
			char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ];
			//sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, pWO->iLobbyReturnAfterGame );
			sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, pWO->iLobbyReturnAfterGame );
			pWO->OnEnteringChatChannel( szChannelToJoin, false, iChannelLobbyNumber( (unsigned char*)szChannelToJoin ) );
		}
		else
			//	Will never happen presumably, if games are always entered via a lobby chat channel.
			pWO->EnterLevel_Top();

		pWO->iLobbyReturnAfterGame = -1;

		if( pWO->bReturningAfterGame )
		{
			Sound_Effect( WOLSOUND_LOGIN );
			pWO->bReturningAfterGame = false;
		}
		else
			Sound_Effect( WOLSOUND_EXITGAME );
	}

	//	Cause a refresh of szMyRecord, the string showing my win/loss record.
	pWO->RequestLadders( pWO->szMyName );

	//------------------------------------------------------------------------
	//	Init Mono Output
	//------------------------------------------------------------------------
	#if(SHOW_MONO)
 	Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), sizeof(NetCommandType), GlobalPacketNames, 0, 13);
	Ipx.Mono_Debug_Print(-1,1);
	#endif

	//------------------------------------------------------------------------
	//	Processing loop
	//------------------------------------------------------------------------
	while (process) 
	{
		#if(SHOW_MONO)
		Ipx.Mono_Debug_Print(-1,0);
		#endif

		//	Regularly check for incoming messages from wolapi.
		if( ::timeGetTime() > pWO->dwTimeNextWolapiPump )
		{
/*
			if( pToolTipHitLast && pToolTipHitLast->bShowing )	//	Lame hack. Problem is draws that occur in callbacks.
			{
				pToolTipHitLast->Unshow();
				pWO->pChat->PumpMessages();
				pWO->pNetUtil->PumpMessages();
				pToolTipHitLast->Show();
			}
			else
			{
				pWO->pChat->PumpMessages();
				pWO->pNetUtil->PumpMessages();
			}
*/
			pWO->pChat->PumpMessages();
			pWO->pNetUtil->PumpMessages();

			//	Special post-callback processing...
			if( pWO->bSelfDestruct )
			{
				if( pWO->pChatSink->bConnected )
					pWO->Logout();
				rc = -1;		//	As if the user logged himself out.
				process = false;
				break;
			}
			if( pWO->pChatSink->bGotKickedTrigger )
			{
				if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
				{
					pWO->OnExitingChatChannel();
					pWO->EnterLevel_UserChat();
				}
				else if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
				{
					pWO->OnExitingChatChannel();
					pWO->EnterLevel_Lobbies();
				}
				else		//	Must be WOL_LEVEL_INOFFICIALCHATCHANNEL.
				{
					pWO->OnExitingChatChannel();
					pWO->EnterLevel_OfficialChat();
				}
				pWO->pChatSink->bGotKickedTrigger = false;
				display = REDRAW_ALL;
			}
			if( pWO->bMyRecordUpdated )
			{
				if( pWO->bShowRankRA )
					chatlistTitle.Set_Text( pWO->szMyRecord );
				else
					chatlistTitle.Set_Text( pWO->szMyRecordAM );
				pWO->bMyRecordUpdated = false;
			}
			if( pWO->bChannelListTitleUpdated )
			{
				chanlistTitle.Set_Text( pWO->szChannelListTitle );
				pWO->bChannelListTitleUpdated = false;
			}
			pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT;
		}

		//	Synch rank toggle buttons state.
		if( BackBtn.Get_Prev() == &RankAMBtn )
		{
			if( pWO->CurrentLevel != WOL_LEVEL_INLOBBY )
			{
				//	Rank buttons are there and shouldn't be.
				RankRABtn.Remove();
				RankAMBtn.Remove();
				display = REDRAW_ALL;
			}
			else
			{
				if( pWO->bShowRankUpdated )
				{
					if( pWO->bShowRankRA )
					{
						RankRABtn.DRAWTOGDOWN;
						RankAMBtn.DRAWTOGUP;
					}
					else
					{
						RankRABtn.DRAWTOGUP;
						RankAMBtn.DRAWTOGDOWN;
					}
					//	Buttons have been refreshed.
					pWO->bShowRankUpdated = false;
					//	Cause my own record to get refreshed.
					pWO->bMyRecordUpdated = true;
					//	Refresh list to show different rankings type.
					pWO->ListChannelUsers();
				}
			}
		}
		else
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
			{
				//	Rank buttons aren't there and should be.
				RankRABtn.Add( JoinBtn );
				RankAMBtn.Add( RankRABtn );
				RankRABtn.Flag_To_Redraw();
				RankAMBtn.Flag_To_Redraw();
				if( pWO->bShowRankRA )
				{
					RankRABtn.DRAWTOGDOWN;
					RankAMBtn.DRAWTOGUP;
				}
				else
				{
					RankRABtn.DRAWTOGUP;
					RankAMBtn.DRAWTOGDOWN;
				}
				//	Buttons have been refreshed.
				pWO->bShowRankUpdated = false;
			}
		}

		//	Regularly update the channels list in certain cases.
		if( ( pWO->CurrentLevel == WOL_LEVEL_OFFICIALCHAT || pWO->CurrentLevel == WOL_LEVEL_USERCHAT || 
				pWO->CurrentLevel == WOL_LEVEL_LOBBIES || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) && 
				::timeGetTime() > pWO->dwTimeNextChannelUpdate )
		{
			switch( pWO->CurrentLevel )
			{
			case WOL_LEVEL_OFFICIALCHAT:
				pWO->UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false );
				break;
			case WOL_LEVEL_USERCHAT:
				pWO->UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false );
				break;
			case WOL_LEVEL_LOBBIES:		//	Overkill in this case to update so often...
				pWO->UpdateChannels( 0, CHANNELFILTER_LOBBIES, false );
				break;
			case WOL_LEVEL_INLOBBY:
				pWO->UpdateChannels( GAME_TYPE, CHANNELFILTER_LOCALLOBBYGAMES, true );
				break;
			}
			pWO->dwTimeNextChannelUpdate = ::timeGetTime() + CHANNELUPDATEWAIT;
		}

#ifdef WIN32
		/*
		** If we have just received input focus again after running in the background then
		** we need to redraw.
		*/
		if (AllSurfaces.SurfacesRestored) {
			AllSurfaces.SurfacesRestored=FALSE;
			display = REDRAW_ALL;
		}
#endif

		if( bFirsttime && !pWO->bChatShownBefore )
		{
			WWMessageBox().Process( TXT_WOL_FINDINGLOBBY, TXT_NONE );
			char szLobbyName[ WOL_CHANNAME_LEN_MAX ];
			if( pWO->GetNameOfBeginningLobby( szLobbyName ) )
			{
//				debugprint( "Found lobby to go into: '%s'\n", szLobbyName );
				if( !EnterChannel( pWO, chatlist, NULL, szLobbyName, false ) )
				{
					//	Could not enter channel for some reason. Go to top instead.
					pWO->EnterLevel_Top();
				}
			}
			else
			{
				//	Could not find name of a lobby for some reason. Go to top instead.
				pWO->EnterLevel_Top();
			}
			pWO->bChatShownBefore = true;
			display = REDRAW_ALL;
			//	Play login sound.
			Sound_Effect( WOLSOUND_LOGIN );
		}
		bFirsttime = false;

		//.....................................................................
		//	Refresh display if needed
		//.....................................................................
		if (display) 
		{
			Hide_Mouse();

			//..................................................................
			//	Redraw backgound & dialog box
			//..................................................................
			if (display >= REDRAW_BACKGROUND) 
			{
				if( pToolTipHitLast && pToolTipHitLast->bShowing )
				{
					pToolTipHitLast->Unshow();
				}


				Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h);

				//...............................................................
				//	Dialog & Field labels
				//...............................................................

				commands->Draw_All();

				//	Draw title bar above channel list.
				Draw_Box( d_chanlist_x, d_chanlist_y - 15, d_chanlist_w, 16, BOXSTYLE_BOX, false );
				switch( lesCurrent )
				{
				case LES_CHANNELS_EXPANDED:
					//	Draw users title bar at bottom.
					Draw_Box( d_userlist_x, d_userlist_y + d_userlist_h - 16, d_userlist_w, 16, BOXSTYLE_BOX, false );
					break;
				case LES_USERS_EXPANDED:
					//	Draw users title bar at top.
					Draw_Box( d_chanlist_x, d_chanlist_y, d_chanlist_w, 16, BOXSTYLE_BOX, false );
					break;
				default:
					//	Draw users title bar in middle.
					Draw_Box( d_userlist_x, d_userlist_y - 15, d_userlist_w, 16, BOXSTYLE_BOX, false );
					break;
				}
			}

			Show_Mouse();
			display = REDRAW_NONE;
		}

		//	Force mouse visible, as some beta testers report unexplicable disappearing cursors.
		while( Get_Mouse_State() )
			Show_Mouse();
		//	Be nice to other apps.
		Sleep( 50 );

		//.....................................................................
		//	Get user input
		//.....................................................................
		if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) )
		{
			//	Mouse button is down.
			timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY;
			if( pToolTipHitLast && pToolTipHitLast->bShowing )
			{
				pToolTipHitLast->Unshow();
			}
		}

		//	If anything currently on the controls list is set to redraw, hide tooltip.
		if( pToolTipHitLast && pToolTipHitLast->bShowing && commands->Is_List_To_Redraw() )
		{
			pToolTipHitLast->Unshow();
		}

		input = commands->Input();

		//	This hack, used elsewhere in this form, appears to be the standard dodge around GadgetClass::Input's 
		//	tendency to remove any focus the first time it runs for a 'commands' list.

		//	ajw - Perhaps I could try doing this every cycle regardless - would avoid stupid non-focused editbox key reactions bug.
		if( bHackFocus )
		{
			sendedit.Set_Focus();
			sendedit.Flag_To_Redraw();
			input = commands->Input();
			bHackFocus = false;
		}

		//	Tooltips...
		if( pToolTipHead )
		{
			ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit();
			if( pToolTipHit == pToolTipHitLast )
			{
				if( pToolTipHit && bLinkInList( commands, pToolTipHit->pGadget ) )	//	(Gadget must be in controls list.)
				{
					if( !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && 
						!( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) )
					{
						pToolTipHit->Show();
					}
					else if( pToolTipHit->bIconList && pToolTipHit->bOverDifferentLine() )
					{
						pToolTipHit->Unshow();
						pToolTipHit->Show();
					}
				}
			}
			else
			{
				if( pToolTipHitLast && pToolTipHitLast->bShowing )
					pToolTipHitLast->Unshow();
				pToolTipHitLast = pToolTipHit;
				timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY;
			}
		}

		//.....................................................................
		//	Process input
		//.....................................................................
		switch (input) 
		{
			case ( BUTTON_SENDEDIT | KN_BUTTON ):
				//	Enter has been pressed - was caught by sendedit control.
				if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
				{
					pWO->SendMessage( sendedit.Get_Text(), userlist, false );
					//	Clear sendedit, reset focus.
					szSendBuffer[0] = 0;
					sendedit.Set_Focus();
					//	Mark for redraw.
					sendedit.Flag_To_Redraw();
				}
				else
				{
					WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS );
					Sound_Effect( WOLSOUND_ERROR );
					sendedit.Set_Focus();
					//	Mark for redraw.
					sendedit.Flag_To_Redraw();
				}
				break;

			case KN_LMOUSE:
				break;

			case ( BUTTON_EXPANDCHANNELS | KN_BUTTON ):
				if( OnExpandChannelList( chanlist, userlist ) )
				{
					//	Hide userlist.
					if( ExpandUserBtn.Get_Next() == &userlist )
						userlist.Remove();
					//	Ensure chanlist is visible.
					if( ExpandChanBtn.Get_Next() != &chanlist )
						chanlist.Add( ExpandChanBtn );
					//	Move userlist expand button.
					ExpandUserBtn.Set_Position( ExpandUserBtn.X, chanlist.Y + chanlist.Height );
					TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 );
					userlistTitle.Set_Position( userlistTitle.X, chanlist.Y + chanlist.Height + 2 );
					//	Set buttons.
					ExpandChanBtn.Set_Shape( pShpUnexpand );
					ExpandUserBtn.Set_Shape( pShpExpand );
				}
				else
				{
					//	Show userlist.
					if( ExpandUserBtn.Get_Next() != &userlist )
						userlist.Add( ExpandUserBtn );
					//	Move userlist expand button.
					ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 );
					TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 );
					userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 );
					//	Set buttons.
					ExpandChanBtn.Set_Shape( pShpExpand );
					ExpandUserBtn.Set_Shape( pShpExpand );
				}
				//	Move rank buttons.
				RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y );
				RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y );
				TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 );
				TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 );
				display = REDRAW_ALL;
				break;

			case ( BUTTON_EXPANDUSERS | KN_BUTTON ):
				if( OnExpandUserList( chanlist, userlist ) )
				{
					//	Hide chanlist controls.
					if( ExpandChanBtn.Get_Next() == &chanlist )
						chanlist.Remove();
					//	Ensure userlist is visible.
					if( ExpandUserBtn.Get_Next() != &userlist )
						userlist.Add( ExpandUserBtn );
					//	Set buttons.
					ExpandChanBtn.Set_Shape( pShpExpand );
					ExpandUserBtn.Set_Shape( pShpUnexpand );
				}
				else
				{
					//	Show chanlist.
					if( ExpandChanBtn.Get_Next() != &chanlist )
						chanlist.Add( ExpandChanBtn );
					//	Set buttons.
					ExpandChanBtn.Set_Shape( pShpExpand );
					ExpandUserBtn.Set_Shape( pShpExpand );
				}
				//	Move userlist expand button.
				ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 );
				TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 );
				userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 );
				//	Move rank buttons.
				RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y );
				RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y );
				TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 );
				TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 );
				display = REDRAW_ALL;
				break;

			case ( BUTTON_CHANLIST | KN_BUTTON ):
				//	User clicks on the game list.
				//...............................................................
				// Handle a double-click
				//...............................................................
				if( lastclick_timer < 30 && chanlist.Current_Index() == lastclick_idx )
				{
					//	Doubleclick on channel list.
					if( ProcessChannelListSelection( pWO, chatlist, chanlist, lastclick_idx ) )
					{
						//	Exit the chat dialog, go to game dialog.
						rc = 2;
						process = false;
					}
					display = REDRAW_ALL;
					bHackFocus = true;
				}
				else
				{
					//...............................................................
					// Handle a single-click
					//...............................................................
					//............................................................
					// If no double-click occurred, set the last-clicked index
					// & double-click timer.
					//............................................................
					lastclick_timer = 0;
					lastclick_idx = chanlist.Current_Index();

				}
				break;

			case ( BUTTON_JOIN | KN_BUTTON ):
				//	Pressing the join button is exactly like doubleclicking on the selected index in chanlist, except:
				//		if the first item is selected, ignore, unless we are at the top level
				if( pWO->CurrentLevel == WOL_LEVEL_TOP || chanlist.Current_Index() != 0 )
				{
					if( ProcessChannelListSelection( pWO, chatlist, chanlist, chanlist.Current_Index() ) )
					{
						//	Exit the chat dialog, go to game dialog.
						rc = 2;
						process = false;
					}
					display = REDRAW_ALL;
					bHackFocus = true;
				}
				break;

			case ( BUTTON_USERLIST | KN_BUTTON ):
				//	User clicks on user list.
				break;
				
			case ( BUTTON_CREATE | KN_BUTTON ):
				switch( pWO->CurrentLevel )
				{
				case WOL_LEVEL_INCHATCHANNEL:
//					debugprint( "%s\n", TXT_WOL_CANTCREATEINCHANNEL );
					WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS );
					Sound_Effect( WOLSOUND_ERROR );
					break;
				case WOL_LEVEL_INLOBBY:
				{
					pWO->bPump_In_Call_Back = true;
					CREATEGAMEINFO CreateGameInfo = WOL_CreateGame_Dialog( pWO );
					pWO->bPump_In_Call_Back = false;
					if( CreateGameInfo.bCreateGame )
					{
						if( CreateGameChannel( pWO, CreateGameInfo ) )
						{
							rc = 1;
							process = false;
						}
					}
					break;
				}
				case WOL_LEVEL_GAMES:
				case WOL_LEVEL_GAMESOFTYPE:
				case WOL_LEVEL_LOBBIES:
					WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEHERE, WOLCOLORREMAP_LOCALMACHINEMESS );
					Sound_Effect( WOLSOUND_ERROR );
					break;
				default:
					CreateChatChannel( pWO );
				}

				display = REDRAW_ALL;
				bHackFocus = true;
				break;

			case ( BUTTON_LEAVE | KN_BUTTON ):
				//	Because of the way things are set up, this is exactly like selecting the first item in chanlist.
				//	(Button is disabled when this is not appropriate.)
				ProcessChannelListSelection( pWO, chatlist, chanlist, 0 );
				display = REDRAW_ALL;
				break;

			case ( BUTTON_REFRESH | KN_BUTTON ):
				pWO->dwTimeNextChannelUpdate = ::timeGetTime();
				break;

			case ( BUTTON_SQUELCH | KN_BUTTON ):
				pWO->DoSquelch( &userlist );
				break;

			case ( BUTTON_BAN | KN_BUTTON ):
				pWO->DoKick( &userlist, true );
//				display = REDRAW_ALL;
				break;

			case ( BUTTON_KICK | KN_BUTTON ):
				pWO->DoKick( &userlist, false );
//				display = REDRAW_ALL;
				break;

			case ( BUTTON_FINDPAGE | KN_BUTTON ):
				pWO->DoFindPage();
				display = REDRAW_ALL;
				bHackFocus = true;
				break;

			case ( BUTTON_OPTIONS | KN_BUTTON ):
				pWO->DoOptions();
				display = REDRAW_ALL;
				bHackFocus = true;
				break;

			case ( KN_ESC ):
//				break;			ajw		Put back in?

			case ( BUTTON_BACK | KN_BUTTON ):
				//	Pressing the back button is exactly like doubleclicking on the top item in chanlist, except
				//	when we're at the top level.
				if( pWO->CurrentLevel != WOL_LEVEL_TOP )
				{
					ProcessChannelListSelection( pWO, chatlist, chanlist, 0 );
					display = REDRAW_ALL;
					break;
				}
				//	Note no break; here. Fall through if at top level.
			case ( BUTTON_DISCONNECT | KN_BUTTON ):
				if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 )
				{
					if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
						ExitChatChannel( pWO );
					pWO->Logout();
					rc = -1;
					process = false;
				}
				display = REDRAW_ALL;
				bHackFocus = true;
				break;

			case ( BUTTON_ACTION | KN_BUTTON ):
				if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
				{
					pWO->SendMessage( sendedit.Get_Text(), userlist, true );
					//	Clear sendedit, reset focus.
					szSendBuffer[0] = 0;
					sendedit.Set_Focus();
					//	Mark for redraw.
					//chatlist.Flag_To_Redraw();
					sendedit.Flag_To_Redraw();
				}
				else
				{
					WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS );
					Sound_Effect( WOLSOUND_ERROR );
					sendedit.Set_Focus();
					//	Mark for redraw.
					sendedit.Flag_To_Redraw();
				}
				break;

			case ( BUTTON_LADDER | KN_BUTTON ):
				pWO->DoLadder();
				display = REDRAW_ALL;
				bHackFocus = true;
				break;

			case ( BUTTON_HELP | KN_BUTTON ):
				pWO->DoHelp();
				display = REDRAW_ALL;
				bHackFocus = true;
				break;

			case ( BUTTON_RANKRA | KN_BUTTON ):
				pWO->bShowRankRA = true;
				pWO->bShowRankUpdated = true;
				break;
			
			case ( BUTTON_RANKAM | KN_BUTTON ):
				pWO->bShowRankRA = false;
				pWO->bShowRankUpdated = true;
				break;

			default:

				break;
		}

		//.....................................................................
		//	Service the sounds & score; GameActive must be false at this point,
		//	so Call_Back() doesn't intercept global messages from me!
		//.....................................................................
		Call_Back();

	}	// end of while

	if( pToolTipHitLast && pToolTipHitLast->bShowing )
		pToolTipHitLast->Unshow();

/*
	//------------------------------------------------------------------------
	//	Restore screen
	//------------------------------------------------------------------------
	Hide_Mouse();
	Load_Title_Page(true);
	Show_Mouse();
*/

	pWO->SaveChat();

	pWO->ClearListPtrs();

	return rc;
}

//***********************************************************************************************
void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ )
{
	RemapControlType* pColorRemap = ( iColorRemap == PCOLOR_NONE ? NULL : &ColorRemaps[ iColorRemap ] );
	WOL_PrintMessage( ILTarget, szText, pColorRemap );
}

//***********************************************************************************************
void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap )
{
	ILTarget.Add_Item( szText, NULL, NULL, ICON_SHAPE, NULL, NULL, pColorRemap );
	if( !ILTarget.bScrollBeingDragged() )
		ILTarget.Show_Last_Item();
}

//***********************************************************************************************
bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist )
{
	//	Expand channel list button was pressed.
	//	Returns true if userlist controls are to be hidden, false if they are to be shown.
	switch( lesCurrent )
	{
	case LES_NORMAL:
		ResizeChannelList( chanlist, true );
		lesCurrent = LES_CHANNELS_EXPANDED;
		break;
	case LES_USERS_EXPANDED:
		ResizeUserList( userlist, false );
		ResizeChannelList( chanlist, true );
		lesCurrent = LES_CHANNELS_EXPANDED;
		break;
	case LES_CHANNELS_EXPANDED:
		ResizeChannelList( chanlist, false );
		lesCurrent = LES_NORMAL;
		return false;
	}
	return true;
}

//***********************************************************************************************
bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist )
{
	//	Expand user list button was pressed.
	//	Returns true if chanlist controls are to be hidden, false if they are to be shown.
	switch( lesCurrent )
	{
	case LES_NORMAL:
		ResizeUserList( userlist, true );
		lesCurrent = LES_USERS_EXPANDED;
		break;
	case LES_CHANNELS_EXPANDED:
		ResizeChannelList( chanlist, false );
		ResizeUserList( userlist, true );
		lesCurrent = LES_USERS_EXPANDED;
		break;
	case LES_USERS_EXPANDED:
		ResizeUserList( userlist, false );
		lesCurrent = LES_NORMAL;
		return false;
	}
	return true;
}

//***********************************************************************************************
void ResizeChannelList( IconListClass& chanlist, bool bExpand )
{
	//	If bExpand, makes list big, else normal size.
	if( bExpand )
		chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_userlist_y + d_userlist_h - 15 - d_chanlist_y );
	else
		chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h );
}

//***********************************************************************************************
void ResizeUserList( IconListClass& userlist, bool bExpand )
{
	//	If bExpand, makes list big, else normal size.
	if( bExpand )
		userlist.Resize( d_userlist_x, d_chanlist_y + 15, d_userlist_w, d_userlist_y + d_userlist_h - ( d_chanlist_y + 15 ) );
	else
		userlist.Resize( d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h );
}

//***********************************************************************************************
bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame )
{
	//	Enter the channel specified in chanlist at iIndex.
	//	Called to enter chat channels, "lobbies", and game channels.

	//	We've stored the channel pointer in the hidden extra data field.
	//	( Be careful about calling RAChatEventSink::DeleteChannelList()! )
	Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex );
	return EnterChannel( pWO, chatlist, pChannel, NULL, bGame );
}

//***********************************************************************************************
bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame )
{
	//	Called to cause the user to enter a channel (chat, lobby, or game).
	//	If pChannel is NULL, szChannelName will be used.

	Channel ChannelWhenNameOnly;
	if( !pChannel )
	{
		if( !szChannelName )
		{
//			debugprint( "pChannel and szChannelName null in EnterChannel" );
			pWO->bSelfDestruct = true;
			return false;
		}
		pChannel = &ChannelWhenNameOnly;
		strcpy( (char*)pChannel->name, szChannelName );
	}

	if( bGame && pChannel->currentUsers >= pChannel->maxUsers )			//	Pre-emptive fullness check.
	{
		WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS );
		Sound_Effect( WOLSOUND_ERROR );
		return false;
	}

	if( bGame )
	{
		//	It is possible to enter a game channel while currently in a chat channel. (A lobby, presumably.)
		if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
			if( !pWO->ExitChatChannelForGameChannel() )
			{
				*pWO->szChannelReturnOnGameEnterFail = 0;
//				debugprint( "ExitChatChannelForGameChannel on join failed" );
				pWO->bSelfDestruct = true;
				return false;
			}
	}

/*	The following doesn't work because the needpw field is not currently being set in wolapi for chat channels.
	Instead, we wait for a fail on join, then present this dialog and try again.
	if( pChannel->needpw )
	{
		SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT,
											WOL_CHANKEY_LEN_MAX );
		if( !pEditDlg->Show() || !*pEditDlg->szEdit )
			return false;

		strcpy( (char*)pChannel->key, pEditDlg->szEdit );
	}
*/
	bool bKeepTrying = true;

	//	Set password automatically for our lobbies, if trying to join one.
	int iLobby = iChannelLobbyNumber( pChannel->name );
	if( iLobby != -1 )
		strcpy( (char*)pChannel->key, LOBBYPASSWORD );

	char szSuccessfulPassword[ WOL_PASSWORD_LEN + 5 ];
	*szSuccessfulPassword = 0;

	HRESULT hRes;
	while( bKeepTrying )
	{
		hRes = pWO->ChannelJoin( pChannel );
		switch( hRes )
		{
		case CHAT_E_BADCHANNELPASSWORD:
		{
			Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT );	//	Required before String_Pixel_Width() call, for god's sake.
#ifdef ENGLISH
			SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT,
												WOL_CHANKEY_LEN_MAX );
#else
#ifdef GERMAN
			SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 400, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT,
												WOL_CHANKEY_LEN_MAX );
#else
			SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 500, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT,
												WOL_CHANKEY_LEN_MAX );
#endif
#endif
			pWO->bPump_In_Call_Back = true;
			if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) != 0 || !*pEditDlg->szEdit )
			{
				pWO->bPump_In_Call_Back = false;
				delete pEditDlg;
				bKeepTrying = false;
				break;
			}
			pWO->bPump_In_Call_Back = false;
			strcpy( (char*)pChannel->key, pEditDlg->szEdit );
			strcpy( szSuccessfulPassword, pEditDlg->szEdit );
			delete pEditDlg;
			break;
		}
		case CHAT_E_TIMEOUT:
			pWO->bPump_In_Call_Back = true;
			WWMessageBox().Process( TXT_WOL_TIMEOUT );
			pWO->bPump_In_Call_Back = false;
			bKeepTrying = false;
			break;
		case CHAT_E_CHANNELDOESNOTEXIST:
			pWO->bPump_In_Call_Back = true;
			WWMessageBox().Process( TXT_WOL_CHANNELGONE );
			pWO->bPump_In_Call_Back = false;
			bKeepTrying = false;
			break;
		case CHAT_E_BANNED:
			WOL_PrintMessage( chatlist, TXT_WOL_YOUREBANNED, WOLCOLORREMAP_LOCALMACHINEMESS );
			Sound_Effect( WOLSOUND_ERROR );
			bKeepTrying = false;
			break;
		case CHAT_E_CHANNELFULL:
			WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS );
			Sound_Effect( WOLSOUND_ERROR );
			bKeepTrying = false;
			break;
		case E_FAIL:
			pWO->GenericErrorMessage();
			bKeepTrying = false;
			break;
		case S_OK:
			bKeepTrying = false;
			break;
		}
	}

	if( !bGame )
	{
		if( hRes == S_OK )
			return pWO->OnEnteringChatChannel( (char*)pChannel->name, false, iLobby );
		else
			return false;
	}
	else
	{
		if( hRes == S_OK )
		{
			*pWO->szChannelReturnOnGameEnterFail = 0;
			//	Return later to the lobby of the channel creator - which was saved in the channel itself.
			pWO->iLobbyReturnAfterGame = pChannel->reserved & 0x00FFFFFF;
			if( pWO->iLobbyReturnAfterGame == 0x00FFFFFF )
				pWO->iLobbyReturnAfterGame = -1;
			CREATEGAMEINFO CreateGameInfo;
			//	Not all of these values are currently used during setup.
			CreateGameInfo.bCreateGame = false;
			CreateGameInfo.iPlayerMax = pChannel->maxUsers;
			CreateGameInfo.bTournament = pChannel->tournament;
			if( *szSuccessfulPassword )
			{
				CreateGameInfo.bPrivate = true;
				strcpy( CreateGameInfo.szPassword, szSuccessfulPassword );
			}
			else
			{
				CreateGameInfo.bPrivate = false;
				*CreateGameInfo.szPassword = 0;
			}
			CreateGameInfo.GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 );
			return pWO->OnEnteringGameChannel( (char*)pChannel->name, false, CreateGameInfo );
		}
		else
		{
			pWO->OnFailedToEnterGameChannel();
			*pWO->szChannelReturnOnGameEnterFail = 0;
			return false;
		}
	}
}

//***********************************************************************************************
bool ExitChatChannel( WolapiObject* pWO )
{
	//	Called to cause user to leave current chat or lobby channel.

//	debugprint( "ExitChatChannel\n" );
	if( !pWO->ChannelLeave() )
	{
		pWO->GenericErrorMessage();
		return false;
	}

	pWO->OnExitingChatChannel();

	return true;
}

//***********************************************************************************************
//void CreateChatChannel( WolapiObject* pWO, bool bPrivate )
void CreateChatChannel( WolapiObject* pWO )
{
	SimpleEditDlgClass* pEditDlg;
/*	if( !bPrivate )
	{
		pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT,
											WOL_CHANNAME_LEN_MAX );
		if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit )
		{
			if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) )
				pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 );
		}
	}
	else
*/	{
		Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT );	//	Required before String_Pixel_Width() call, for god's sake.
		pEditDlg = new SimpleEditDlgClass( 350, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT,
											WOL_CHANNAME_LEN_MAX, TXT_WOL_OPTIONALPASSPROMPT, WOL_CHANKEY_LEN_MAX );
		pWO->bPump_In_Call_Back = true;
		if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit )
		{
			pWO->bPump_In_Call_Back = false;
			if( *pEditDlg->szEdit2 )
			{
				if( pWO->ChannelCreate( pEditDlg->szEdit, pEditDlg->szEdit2 ) )
					pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 );
			}
			else
			{
				//	Create public channel.
				if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) )
					pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 );
			}
		}
		pWO->bPump_In_Call_Back = false;
	}

	delete pEditDlg;
}

//***********************************************************************************************
bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi )
{
	char szNewChannelName[ WOL_CHANNAME_LEN_MAX ];
	sprintf( szNewChannelName, "%s's_game", pWO->szMyName );

	if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
		if( !pWO->ExitChatChannelForGameChannel() )
		{
			*pWO->szChannelReturnOnGameEnterFail = 0;
//			debugprint( "ExitChatChannelForGameChannel in CreateGameChannel() error" );
			pWO->bSelfDestruct = true;
			return false;
		}

	const char* szKey;
	if( *cgi.szPassword )
		szKey = cgi.szPassword;
	else
		szKey = NULL;

	if( pWO->ChannelCreate( szNewChannelName, szKey, true, cgi.iPlayerMax, cgi.bTournament, pWO->iLobbyLast, cgi.GameKind ) )
		pWO->OnEnteringGameChannel( szNewChannelName, true, cgi );
	else
	{
		pWO->OnFailedToEnterGameChannel();
//		debugprint( "CreateGameChannel fail" );
		return false;
	}
	*pWO->szChannelReturnOnGameEnterFail = 0;

	return true;
}

//***********************************************************************************************
bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex )
{
	//	Takes whatever action necessary due to user selecting iIndex from chanlist.
	//	Returns true if user selected to enter a game channel, else false.
	if( iIndex < 0 )
		return false;

//	debugprint( "iIndex %i\n", iIndex );

	const char* szChannelType = chanlist.Get_Item_ExtraDataString( iIndex );
	if( szChannelType )
	{
//debugprint( "szChannelType %s\n", szChannelType );
		if( strcmp( szChannelType, CHANNELTYPE_OFFICIALCHAT ) == 0 )
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
			{
				if( !ExitChatChannel( pWO ) )
				{
					pWO->bSelfDestruct = true;
					return false;
				}
			}
			pWO->EnterLevel_OfficialChat();
		}
		else if( strcmp( szChannelType, CHANNELTYPE_USERCHAT ) == 0 )
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
			{
				if( !ExitChatChannel( pWO ) )
				{
					pWO->bSelfDestruct = true;
					return false;
				}
			}
			pWO->EnterLevel_UserChat();
		}
		else if( strcmp( szChannelType, CHANNELTYPE_TOP ) == 0 )
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
			{
				//	Now not possible.
				if( !ExitChatChannel( pWO ) )
				{
					pWO->bSelfDestruct = true;
					return false;
				}
			}
			pWO->EnterLevel_Top();
		}
		else if( strcmp( szChannelType, CHANNELTYPE_GAMES ) == 0 )
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
			{
				//	Now not possible.
				if( !ExitChatChannel( pWO ) )
				{
					pWO->bSelfDestruct = true;
					return false;
				}
			}
			pWO->EnterLevel_Games();
		}
		else if( strcmp( szChannelType, CHANNELTYPE_GAMESOFTYPE ) == 0 )
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
			{
				//	Now not possible.
				if( !ExitChatChannel( pWO ) )
				{
					pWO->bSelfDestruct = true;
					return false;
				}
			}
			void* pExtraData = chanlist.Get_Item_ExtraDataPtr( iIndex );
			pWO->EnterLevel_GamesOfType( (WOL_GAMETYPEINFO*)pExtraData );
		}
		else if( strcmp( szChannelType, CHANNELTYPE_CHATCHANNEL ) == 0 )
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
			{
				//	Not currently possible.
//				debugprint( "Trying to jump from channel to channel?!\n" );
				pWO->bSelfDestruct = true;
				return false;
			}
			//	Join the chat channel.
			EnterChannel( pWO, chatlist, chanlist, iIndex, false );		//	Can fail.
		}
		else if( strcmp( szChannelType, CHANNELTYPE_GAMECHANNEL ) == 0 )
		{
			//	User attempting to join game channel.
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
			{
				//	Not currently possible.
//				debugprint( "Trying to jump from channel to channel?!\n" );
				pWO->bSelfDestruct = true;
				return false;
			}
			//	Check if local user is allowed to join GameKind.
			Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex );
			if( pChannel->type == GAME_TYPE )
			{
				//	It is a game of our type, at least.
				CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 );
				switch( GameKind )
				{
				case CREATEGAMEINFO::RAGAME:
					break;
				case CREATEGAMEINFO::CSGAME:
					if( !Is_Counterstrike_Installed() )
					{
						WOL_PrintMessage( chatlist, TXT_WOL_NEEDCOUNTERSTRIKE, WOLCOLORREMAP_LOCALMACHINEMESS );
						Sound_Effect( WOLSOUND_ERROR );
						return false;
					}
					break;
				case CREATEGAMEINFO::AMGAME:
					if( !Is_Aftermath_Installed() )
					{
						WOL_PrintMessage( chatlist, TXT_WOL_NEEDAFTERMATH, WOLCOLORREMAP_LOCALMACHINEMESS );
						Sound_Effect( WOLSOUND_ERROR );
						return false;
					}
					break;
				default:
//					debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name );
					Sound_Effect( WOLSOUND_ERROR );
					return false;
				}
				//	Join the game channel.
				if( EnterChannel( pWO, chatlist, chanlist, iIndex, true ) )
				{
					//	Exit the chat dialog, go to game dialog.
					return true;
				}
			}
			else
			{
				//	User doubleclicked on a game that is of a different type.
				//	Offer to take them to a web page regarding the game type.
				pWO->DoGameAdvertising( pChannel );
			}
		}
		else if( strcmp( szChannelType, CHANNELTYPE_LOBBIES ) == 0 )
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
			{
				//	Not currently possible.
//				debugprint( "Chat channel to lobbies level?!\n" );
				pWO->bSelfDestruct = true;
				return false;
			}
			if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
			{
				if( !ExitChatChannel( pWO ) )
				{
					pWO->bSelfDestruct = true;
					return false;
				}
			}
			pWO->EnterLevel_Lobbies();
		}
		else if( strcmp( szChannelType, CHANNELTYPE_LOBBYCHANNEL ) == 0 )
		{
			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ||
				pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
			{
				//	Not currently possible.
//				debugprint( "Chat or lobby channel to lobby channel?!\n" );
				pWO->bSelfDestruct = true;
				return false;
			}
			//	Join the lobby chat channel.
			EnterChannel( pWO, chatlist, chanlist, iIndex, false );		//	Can fail.
		}
		else if( strcmp( szChannelType, CHANNELTYPE_LOADING ) == 0 )
		{
			//	User clicked on the channel list loading notification - do nothing.
		}
		else
		{
//			debugprint( "Item selected in channel list unidentifiable from extradata field\n" );
			pWO->bSelfDestruct = true;
			return false;
		}

	}
	return false;
}
	
//***********************************************************************************************
bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind )
{
	const LinkClass* pLink = pListHead;
	while( pLink )
	{
		if( pLink == pLinkToFind )
			return true;
		pLink = pLink->Get_Next();
	}
	return false;
}

#endif