/*===========================================================================

FILE: RoadWarrior.c

AUTHOR:
   Jeff Beckley
   QUALCOMM Incorporated

SERVICES: 

DESCRIPTION:
   Connects up to the CalTrans web site to get real-time traffic info.
   Gives freeway speeds for almost all of San Diego County.
  
PUBLIC CLASSES:  
   N/A


   	   Copyright © 2000-2002 QUALCOMM Incorporated.
                 All Rights Reserved.
              QUALCOMM Proprietary/GTDR
===========================================================================*/


/*===============================================================================
                     INCLUDES AND VARIABLE DEFINITIONS
=============================================================================== */
#include "AEE.h"			// Standard AEE Declarations
#include "AEEShell.h"		// AEE Shell Services
#include "AEEDisp.h"		// AEE Display Services
#include "AEEStdLib.h"		// AEE StdLib Services
#include "AEEAppGen.h"		// AEEApplet structure and Applet services
#include "AEEFile.h"		// IFILEMGR and IFILE
#include "AEEMenu.h"		// AEE MenuCtl Services
#include "AEEWeb.h"			// AEE Web Services
#include "RoadWarrior.bid"	// App ID

#include "roadwarrior.brh"	// App Resources

#include "nmdef.h"


/*-------------------------------------------------------------------
            Type Declarations
-------------------------------------------------------------------*/

#define PREFS_MAX_FWYS 64

typedef struct
{
	int DefaultFwy;
	char DefaultRamp[PREFS_MAX_FWYS];
} CRoadWarriorPrefs;

#define ROAD_WARRIOR_PREFS_VERSION 1

#define MAX_TTS_DIGITS 3

//Mandatory structure.
//Structure to contain app data. This is the Applet structure that will be allocated
// for the app. Define this structure if the app needs to store any data that needs
// to be remembered across different events or callbacks. The first element of this
// structure MUST be of type AEEApplet. This is extremely important. Following this
// element, store the app-specific data. Note that AEEApplet structure already exposes
// the most commonly needed interface pointers such as Shell, Display, etc. 
typedef struct _CRoadWarriorApp
{
	//First element of this structure must be AEEApplet.
	AEEApplet a ;

	//App specific data
	IFileMgr*	m_pIFileMgr;	// IFileMgr interface ptr
	IWeb*		m_pIWeb;		// IWeb interface ptr
	IWebResp*	m_pIWebResp;	// IWebResp interface ptr
	AEECallback	m_Callback;

	char*		m_BodyBuffer;		// Buffer to hold HTML data
	int			m_BodySize;			// Size of HTML data in buffer
	int			m_BodyAllocSize;	// Allocated size of HTML data buffer
	IMenuCtl*	m_pIMainMenu;		// Pointer to Main menu control
	IMenuCtl*	m_pISpeedMenu;		// Pointer to Speed menu control
	IStatic*	m_pIHelpText;		// Pointer to static control for help text

	CRoadWarriorPrefs	m_Prefs;

	uint16		m_cxScreen, m_cyScreen;
	AEERect		m_MenuFrame;
	uint16		m_DeviceColorDepth;
	IImage*		m_pLogo;				// Logo
	IImage*		m_pSplashImage;			// Splash
	IImage*		m_pTrafficAnim[3]; 		// slow, mod., fast
	IImage*		m_pProgressImages[3];	
	IImage*		m_pHighway;				// Background for the animation
	int			m_HighwayOffsetX;
	int			m_ProgDisplayCX, m_ProgDisplayCY;
	int			m_nCurrentAnimID;		// Index of the current Traffic Animation

	boolean		m_bAnimPlaying;
	boolean		m_bIsProgressDisp;
	boolean 	m_bDownloadErr;
	boolean		m_bSuspended;

	uint32		m_LastDigitPress;				// Last time a digit was pressed
	char		m_DigitBuffer[MAX_TTS_DIGITS];	// Buffer to hold digits typed so far
	uint16		m_ValidDigits;					// Number of digits in buffer

} CRoadWarriorApp;


#define ARRAYSIZE(a)   (sizeof(a) / sizeof((a)[0]))

#ifdef AEE_SIMULATOR
#define NO_REF(p)	p;
#else
#define NO_REF(p)
#endif

#ifndef MIF_COMPANY_NAME_SIZE
#define MIF_COMPANY_NAME_SIZE 23
#endif

#ifndef IDS_MIF_COMPANY
#define IDS_MIF_COMPANY 6
#endif

/*-------------------------------------------------------------------
            Function Prototypes
-------------------------------------------------------------------*/

// Mandatory function: App Handle Event function

static boolean RoadWarriorApp_HandleEvent(CRoadWarriorApp* pMe, AEEEvent eCode, uint16 wParam, uint32 dwParam);

// RoadWarrior operation:

static void GetFreewaySpeeds(CRoadWarriorApp* pMe);
static void WebReadCB(void* cxt);
static void WebCleanup(CRoadWarriorApp* pMe);
static void ShowMainMenu(CRoadWarriorApp* pMe);

// App Helper Functions

/*-------------------------------------------------------------------
            Global Constant Definitions
-------------------------------------------------------------------*/

// Layout constant
#define LOGO_HEIGHT	10
#define ANIM_WIDTH	65
#define ANIM_HEIGHT	9
#define SLOW		0
#define MODERATE	1
#define FAST		2
#define NO_SPEED	3


/*===============================================================================
                     FUNCTION DEFINITIONS
=============================================================================== */

#define SPEED_CATEGORY(s) ((s) < 0? NO_SPEED : ((s) > 50? FAST : ((s) > 35? MODERATE : SLOW)))

#ifndef WEB_ERROR_SUCCEEDED
#define WEB_ERROR_SUCCEEDED(nCode) (((nCode) >= 200) && ((nCode) < 300))
#endif // WEB_ERROR_SUCCEEDED

#ifndef STRDUP
char* STRDUP(const char* psz)
{
	char* pszDup = (char*)MALLOC(STRLEN(psz) + 1);

	if (pszDup)
		STRCPY(pszDup, psz);

	return pszDup;
}
#endif // STRDUP

/*===========================================================================

FUNCTION: DrawHighway

DESCRIPTION:
	This function draws the highway bitmap

===========================================================================*/

static void DrawHighway(CRoadWarriorApp *pMe)
{
	IIMAGE_Draw(pMe->m_pHighway, pMe->m_HighwayOffsetX, pMe->m_cyScreen - ANIM_HEIGHT);
}

/*===========================================================================

FUNCTION: StartProgressDisplay

DESCRIPTION:
	This function starts the progress display. It is used when the app 
	downloads data.

===========================================================================*/
#define PROG_ANIM 			0
#define PROG_PHONE			1
#define PROG_FILE			2

static void StartProgressDisplay(CRoadWarriorApp *pMe)
{
	if (!pMe->m_bSuspended && !pMe->m_bIsProgressDisp)
	{
		int ImageOffsetX;
		int ImageOffsetY;
		AEERect r;

		pMe->m_bIsProgressDisp = TRUE;

		SETAEERECT(&r, (pMe->m_cxScreen - pMe->m_ProgDisplayCX)/2, (pMe->m_cyScreen - pMe->m_ProgDisplayCY)/2, 
															pMe->m_ProgDisplayCX, pMe->m_ProgDisplayCY);
		IDISPLAY_EraseRect(pMe->a.m_pIDisplay, &r);
		IDISPLAY_FrameRect(pMe->a.m_pIDisplay, &r);

		ImageOffsetX = r.x + (r.dx - 54) / 2;
		ImageOffsetY = r.y + 6 + 2 * IDISPLAY_GetFontMetrics(pMe->a.m_pIDisplay, AEE_FONT_NORMAL, NULL, NULL);

		IIMAGE_Draw(pMe->m_pProgressImages[PROG_PHONE], ImageOffsetX,      ImageOffsetY     );
		IIMAGE_Draw(pMe->m_pProgressImages[PROG_FILE],  ImageOffsetX + 42, ImageOffsetY + 12);
		IIMAGE_Start(pMe->m_pProgressImages[PROG_ANIM], ImageOffsetX + 18, ImageOffsetY + 16);
	}
}


/*===========================================================================

FUNCTION: StopProgressDisplay

DESCRIPTION:
	This function stops the progress display.

PARAMETERS:
	pMe: Pointer to the AEEApplet structure. 

===========================================================================*/
static void StopProgressDisplay(CRoadWarriorApp *pMe)
{
	if (pMe->m_bIsProgressDisp)
	{
		pMe->m_bIsProgressDisp = FALSE;
		IIMAGE_Stop(pMe->m_pProgressImages[PROG_ANIM]);
	}
}


/*===========================================================================

FUNCTION: StopTrafficAnim

DESCRIPTION:
	This function stops the Traffic Animation.

PARAMETERS:
	pMe: 		Pointer to the AEEApplet structure. 

===========================================================================*/
static void StopTrafficAnim(CRoadWarriorApp *pMe)
{
	if (pMe->m_nCurrentAnimID >= 0)
	{
		IIMAGE_Stop(pMe->m_pTrafficAnim[pMe->m_nCurrentAnimID]);

		DrawHighway(pMe);

		pMe->m_nCurrentAnimID    = -1;
		pMe->m_bAnimPlaying      = FALSE;
	}
}


/*===========================================================================

FUNCTION: StartTrafficAnim

DESCRIPTION:
	This function starts the Traffic Animation. This is used for the speed 
	display (m_pISpeedMenu).

PARAMETERS:
	pMe: 		Pointer to the AEEApplet structure. 

===========================================================================*/

static void StartTrafficAnim(CRoadWarriorApp* pMe)
{
	uint16 wSel = IMENUCTL_GetSel(pMe->m_pISpeedMenu);
	int nAnimID;
	int iSpeed;
	int iMsec;

	StopTrafficAnim(pMe);
	DrawHighway(pMe);

	IMENUCTL_GetItemData(pMe->m_pISpeedMenu, wSel, (uint32*)&iSpeed);
	iMsec = 700 - (iSpeed * 5);
	iMsec = MAX(0, iMsec);

	if (iSpeed > 0)
	{
		nAnimID = SPEED_CATEGORY(iSpeed);
		pMe->m_bAnimPlaying = TRUE;

		pMe->m_nCurrentAnimID = nAnimID;

		IIMAGE_SetParm(pMe->m_pTrafficAnim[nAnimID], IPARM_RATE, iMsec, 0);
		IIMAGE_Start(pMe->m_pTrafficAnim[nAnimID], pMe->m_HighwayOffsetX + 12, pMe->m_cyScreen - 6);
	}

	IDISPLAY_UpdateEx(pMe->a.m_pIDisplay, TRUE);
}


/*===========================================================================

FUNCTION: xStatus

DESCRIPTION
	Convenience Function: Display a status line on the screen.

PARAMETERS:
	nLine:  Indicates the line number.
	nResID: ID number of string resource to load for text.

===========================================================================*/

#define LINEHEIGHT   14
#define TOPLINE      0
#define MARGIN 		 3

static void xStatus(CRoadWarriorApp* pMe, int nLine, int16 nResID)
{
	AECHAR TextBuf[32];

	if (!pMe->m_bSuspended && 
		ISHELL_LoadResString(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE, nResID,
			TextBuf, ARRAYSIZE(TextBuf)))
	{
		int nMaxLines;
		AEERect rc;

		nMaxLines = (pMe->m_cyScreen / LINEHEIGHT) - 2;
		if (nMaxLines < 1)
			nMaxLines = 1;
   
		// 0 always first; lines 1...n cycle
		// 0 clears all other lines

		rc.x = (int16)((pMe->m_cxScreen - pMe->m_ProgDisplayCX)/2 + MARGIN);
		rc.dx = (int16)(pMe->m_ProgDisplayCX - 2 * MARGIN);

		if (nLine > 0)
			nLine = (nLine - 1) % nMaxLines + 1;

		rc.y = (int16)((pMe->m_cyScreen - pMe->m_ProgDisplayCY)/2 + 1 + (nLine * LINEHEIGHT));
		rc.dy = LINEHEIGHT;
		if (nLine == 0)
			rc.dy = (int16)(LINEHEIGHT * 2);

		IDISPLAY_DrawText(pMe->a.m_pIDisplay,
					(AEEFont)(nLine == 0 ? AEE_FONT_BOLD : AEE_FONT_NORMAL),
					TextBuf, -1, rc.x, rc.y, &rc, IDF_RECT_FILL);
		IDISPLAY_Update(pMe->a.m_pIDisplay);
	}
}

/*===========================================================================

Function:  RoadWarrior_InitAppData()

Description:
	This function initializes the app specific data.

Prototype:
	void RoadWarrior_InitAppData(IApplet* po)

Parameters:
	IApplet* po: Pointer to the applet structure. This is of the type defined
	to store applet data. (CRoadWarriorApp*, in this case).

Return Value:
	TRUE: If successful
	FALSE: IF failed

Comments:  None

Side Effects: None

==============================================================================*/

boolean RoadWarrior_InitAppData(IApplet* po)
{
	CRoadWarriorApp *pMe = (CRoadWarriorApp*)po;
	AEEDeviceInfo di;
	AEEImageInfo ii;
	int FontHeight;
	int16 i;

	// Initialize Data Members
	MEMSET(((char*)pMe) + sizeof(AEEApplet), 0, sizeof(*pMe) - sizeof(AEEApplet));

	if (ISHELL_GetPrefs(pMe->a.m_pIShell, AEECLSID_ROADWARRIOR, ROAD_WARRIOR_PREFS_VERSION,
						&pMe->m_Prefs, sizeof(pMe->m_Prefs)) != SUCCESS)
	{
		MEMSET(&pMe->m_Prefs, 0, sizeof(pMe->m_Prefs));
	}

	if (ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_FILEMGR, (void **)&pMe->m_pIFileMgr) != SUCCESS)
	{
		pMe->m_pIFileMgr = NULL;
		return FALSE;
	}

	ISHELL_GetDeviceInfo(pMe->a.m_pIShell,&di);
	pMe->m_cxScreen = di.cxScreen;
	pMe->m_cyScreen = di.cyScreen;
	SETAEERECT(&(pMe->m_MenuFrame), 0, LOGO_HEIGHT+1, pMe->m_cxScreen, pMe->m_cyScreen - (LOGO_HEIGHT + 1) - ANIM_HEIGHT);
	pMe->m_DeviceColorDepth = di.nColorDepth;

	if ((pMe->m_pLogo = ISHELL_LoadResImage(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE, IDB_LOGO))==NULL ||
		(pMe->m_pHighway = ISHELL_LoadResImage(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE, IDB_HIGHWAY))==NULL ||
		(pMe->m_pProgressImages[PROG_PHONE] = ISHELL_LoadResImage(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE, IDB_PHONE))==NULL ||
		(pMe->m_pProgressImages[PROG_FILE]  = ISHELL_LoadResImage(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE, IDB_FILE))==NULL ||
		(pMe->m_pProgressImages[PROG_ANIM]  = ISHELL_LoadResImage(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE, IDB_ANIM))==NULL)
	{
		return FALSE;
	}

	IIMAGE_GetInfo(pMe->m_pHighway, &ii);
	pMe->m_HighwayOffsetX = (pMe->m_cxScreen - ii.cx) / 2;

	IIMAGE_GetInfo(pMe->m_pProgressImages[PROG_PHONE], &ii);
	FontHeight = IDISPLAY_GetFontMetrics(pMe->a.m_pIDisplay, AEE_FONT_NORMAL, NULL, NULL);
	pMe->m_ProgDisplayCX = FontHeight * 7;
	pMe->m_ProgDisplayCY = FontHeight * 2 + ii.cy + 8;

	IIMAGE_SetParm(pMe->m_pProgressImages[PROG_ANIM], IPARM_NFRAMES,   4, 0);
	IIMAGE_SetParm(pMe->m_pProgressImages[PROG_ANIM], IPARM_CXFRAME,  15, 0);  	
	IIMAGE_SetParm(pMe->m_pProgressImages[PROG_ANIM], IPARM_RATE,    300, 0);

	for (i = SLOW; i <= FAST; i++)
	{
		if ((pMe->m_pTrafficAnim[i] = ISHELL_LoadResImage(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE,
			(int16)(IDB_ANIM_TRAFFIC_SLOW + i))) == NULL)
		{
			return FALSE;
		}
		IIMAGE_SetParm(pMe->m_pTrafficAnim[i], IPARM_NFRAMES,          9, 0);
		IIMAGE_SetParm(pMe->m_pTrafficAnim[i], IPARM_CXFRAME, ANIM_WIDTH, 0);  	
		IIMAGE_SetParm(pMe->m_pTrafficAnim[i], IPARM_RATE,           600, 0);
	}

	pMe->m_nCurrentAnimID = -1;
	pMe->m_bAnimPlaying = FALSE;
	pMe->m_bIsProgressDisp = FALSE;
	pMe->m_bDownloadErr = FALSE;
	pMe->m_bSuspended = FALSE;

	return TRUE;
}

/*===========================================================================

Function:  RoadWarrior_FreeAppData()

Description:
	This function frees the app data. This function is registered with the
	applet framework when the applet is created (inside AEEClsCreateInstance() function).
	This function is called by the app framework when the reference count of the 
	applet reaches zero. This function must free all the app data that has been
	allocated by this app. For ex: if their is data that was remembered when
	the app is suspended and resumed, those data must be freed here.

Prototype:
	void RoadWarrior_FreeAppData(IApplet* po)

Parameters:
	IApplet* po: Pointer to the applet structure. This is of the type defined
	to store applet data. (CRoadWarriorApp*, in this case).

Return Value:
	None

Comments:  None

Side Effects: None

==============================================================================*/
void RoadWarrior_FreeAppData(IApplet* po)
{
	int i;
	CRoadWarriorApp *pMe = (CRoadWarriorApp*)po;

	WebCleanup(pMe);

	if (pMe->m_pIMainMenu)
	{
		uint16 i = 0;
		char* data;

		while (IMENUCTL_GetItemData(pMe->m_pIMainMenu, i++, (uint32*)&data))
		{
			if (data)
				FREE(data);
		}

		IMENUCTL_Release(pMe->m_pIMainMenu);
		pMe->m_pIMainMenu = NULL;
	}
	if (pMe->m_pISpeedMenu)
	{
		IMENUCTL_Release(pMe->m_pISpeedMenu);
		pMe->m_pISpeedMenu = NULL;
	}
	if (pMe->m_pIHelpText)
	{
		ISTATIC_Release(pMe->m_pIHelpText);
		pMe->m_pIHelpText = NULL;
	}

	if (pMe->m_pIFileMgr)
	{
		IFILEMGR_Release(pMe->m_pIFileMgr);
		pMe->m_pIFileMgr = NULL;
	}

	// Release images
	if(pMe->m_pLogo != NULL)
		IIMAGE_Release(pMe->m_pLogo);

	if(pMe->m_pSplashImage != NULL)
		IIMAGE_Release(pMe->m_pSplashImage);

	if(pMe->m_pHighway != NULL)
		IIMAGE_Release(pMe->m_pHighway);

	for (i = 0 ; i < 3 ; i++)
	{
		if(pMe->m_pTrafficAnim[i] != NULL)
			IIMAGE_Release(pMe->m_pTrafficAnim[i]);

		if(pMe->m_pProgressImages[i] != NULL)
			IIMAGE_Release(pMe->m_pProgressImages[i]);
	}

}

/*===========================================================================

FUNCTION: AEEClsCreateInstance

DESCRIPTION
	This function is invoked while the app is being loaded. All Modules must provide this 
	function. Ensure to retain the same name and parameters for this function.
	In here, the module must verify the ClassID and then invoke the AEEApplet_New() function
	that has been provided in AEEAppGen.c. 

	After invoking AEEApplet_New(), this function can do app specific initialization. In this
	example, a generic structure is provided so that app developers need not change app specific
	initialization section every time.

PROTOTYPE:
	int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * po,void ** ppObj)

PARAMETERS:
	clsID: [in]: Specifies the ClassID of the applet which is being loaded

	pIShell: [in]: Contains pointer to the IShell interface. 

	pIModule: pin]: Contains pointer to the IModule interface to the current module to which
	this app belongs

	ppObj: [out]: On return, *ppApplet must point to a valid IBase object. 
	If the classID	represnts an applet, then ppObj must point to a valid AEEApplet structure.Allocation
	of memory for this structure and initializing the base data members is done by AEEApplet_New().

DEPENDENCIES
	none

RETURN VALUE
	AEE_SUCCESS: If the class/app creation was successful. 
	EFAILED: Error occurred while creating the class/app. In this case, the app/class will
	not be loaded.

SIDE EFFECTS
	none
===========================================================================*/
//Mandatory Function
int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * po,void ** ppObj)
{
	*ppObj = NULL;

	if(ClsId == AEECLSID_ROADWARRIOR)
	{
		//Create the applet
		if(AEEApplet_New(sizeof(CRoadWarriorApp), ClsId, pIShell,po,(IApplet**)ppObj,(AEEHANDLER)RoadWarriorApp_HandleEvent,(PFNFREEAPPDATA)RoadWarrior_FreeAppData))
		{
			//Initialize applet data
			if(RoadWarrior_InitAppData((IApplet*)*ppObj))
			{
				//Data initialized successfully
				return(AEE_SUCCESS);
			}
			else
			{
				//Release the applet. This will free the memory allocated for the applet when
				// AEEApplet_New was called.
				IAPPLET_Release((IApplet*)*ppObj);
				return EFAILED;
			}

		}//AEEApplet_New

	}// ClsId == AEECLSID_ROADWARRIOR

	return(EFAILED);
}


/*===========================================================================
FUNCTION SaveDefaultRamp()

DESCRIPTION
	Save the currently selected ramp as the default for the current freeway.

===========================================================================*/
static void SaveDefaultRamp(CRoadWarriorApp* pMe)
{
	if (pMe->m_pISpeedMenu)
	{
		char CurSel = (char)IMENUCTL_GetSel(pMe->m_pISpeedMenu);

		pMe->m_Prefs.DefaultRamp[pMe->m_Prefs.DefaultFwy] = CurSel;
		ISHELL_SetPrefs(pMe->a.m_pIShell, AEECLSID_ROADWARRIOR, ROAD_WARRIOR_PREFS_VERSION,
						&pMe->m_Prefs, sizeof(pMe->m_Prefs));
	}
}


/*===========================================================================
FUNCTION TypeToSelectFwy()

DESCRIPTION
	Select a freeway based on the digit key that was pressed.

===========================================================================*/
static void TypeToSelectFwy(CRoadWarriorApp* pMe, AVKType Digit)
{
	uint16 Start = IMENUCTL_GetSel(pMe->m_pIMainMenu);
	uint16 Current = Start;
	const char SearchChar = (char)('0' + (Digit - AVK_0));
	const char* data;
	const uint32 Now = GETUPTIMEMS();

	if (pMe->m_ValidDigits >= MAX_TTS_DIGITS || Now > pMe->m_LastDigitPress + 1000)
		pMe->m_ValidDigits = 0;

	pMe->m_DigitBuffer[pMe->m_ValidDigits++] = SearchChar;
	pMe->m_LastDigitPress = Now;

	while (1)
	{
		if (IMENUCTL_GetItemData(pMe->m_pIMainMenu, Current, (uint32*)&data))
		{
			if (data)
			{
				// Ignore characters until we get to a digit
				while (*data && (*data < '0' || *data > '9'))
					data++;
				if (STRNCMP(data, pMe->m_DigitBuffer, pMe->m_ValidDigits) == 0)
				{
					IMENUCTL_SetSel(pMe->m_pIMainMenu, Current);
					return;
				}
			}

			// If we got back to the item we started at, that means we've wrapped around the
			// list without finding a match, so just quit
			if (++Current == Start)
				break;
		}
		else
		{
			// If we get to the end of the list without a match, start back at the beginning
			if (Current && Start && Current > Start)
				Current = 0;
			else
				break;
		}
	}

	// Didn't find a match, so start the type-to-select buffer to the beginning
	pMe->m_ValidDigits = 0;
}

/*===========================================================================
FUNCTION ShowSplash()

DESCRIPTION
	This function displays a splash image before the main menu is displayed.

===========================================================================*/
static void ShowSplash(CRoadWarriorApp *pMe)
{
	IDISPLAY_ClearScreen(pMe->a.m_pIDisplay);
	ISHELL_SetTimer(pMe->a.m_pIShell, pMe->m_pIMainMenu? 3000 : 1500, (PFNNOTIFY) ShowMainMenu, (uint32*) pMe);

	if (!pMe->m_pSplashImage)
		pMe->m_pSplashImage = ISHELL_LoadResImage(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE, IDB_SPLASH);
	if (pMe->m_pSplashImage)
	{
		int SplashSpaceX;
		int SplashSpaceY;
		AEEImageInfo ii;
		AEEAppInfo ai;

		IIMAGE_GetInfo(pMe->m_pSplashImage, &ii);
		SplashSpaceX = (pMe->m_cxScreen - ii.cx) / 2;
		SplashSpaceY = (pMe->m_cyScreen - ii.cy) / 2;

		IIMAGE_Draw(pMe->m_pSplashImage, SplashSpaceX, SplashSpaceY);

		// Get the app info, which contains the name of the MIF file
		if (ISHELL_QueryClass(pMe->a.m_pIShell, AEECLSID_ROADWARRIOR, &ai))
		{
			AECHAR Company[MIF_COMPANY_NAME_SIZE + 1];

			// Get the Company field in the MIF file, and extract the version from it
			if (ISHELL_LoadResString(pMe->a.m_pIShell, ai.pszMIF, IDS_MIF_COMPANY,
										Company, sizeof(Company)))
			{
				AECHAR* Version = WSTRCHR(Company, '\n');

				if (Version)
				{
					AEERect rect;

					// Display the version number just under the splash image
					SETAEERECT(&rect, 0, pMe->m_cyScreen - SplashSpaceY, pMe->m_cxScreen, SplashSpaceY);
					IDISPLAY_DrawText(pMe->a.m_pIDisplay, AEE_FONT_NORMAL, Version + 1, -1, 0, 0,
										&rect, IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);
				}
			}
		}

		IDISPLAY_Update(pMe->a.m_pIDisplay);
	}
}

/*===========================================================================
FUNCTION HideSplash()

DESCRIPTION
	This function releases the splash image (m_pSplashImage), and clears the screen.

===========================================================================*/
static void HideSplash(CRoadWarriorApp *pMe)
{
	if(pMe->m_pSplashImage != NULL)
	{
		IIMAGE_Release(pMe->m_pSplashImage);
		pMe->m_pSplashImage = NULL;
	}
	IDISPLAY_ClearScreen(pMe->a.m_pIDisplay);
}

/*===========================================================================
FUNCTION DrawLogo

DESCRIPTION
	This function draws the RoadWarrior logo at the top of the screen.

===========================================================================*/

static void DrawLogo(CRoadWarriorApp *pMe)
{
	AEEImageInfo ii;

	if(pMe->m_pLogo != NULL)
	{
		IIMAGE_GetInfo(pMe->m_pLogo, &ii);
		IIMAGE_Draw(pMe->m_pLogo, (pMe->m_cxScreen - ii.cx)/2, 0);
	}

	IDISPLAY_DrawHLine(pMe->a.m_pIDisplay, 0, LOGO_HEIGHT, pMe->m_cxScreen);
}


/*===========================================================================
FUNCTION ShowHelp()

DESCRIPTION
	This function displays information to the user.

===========================================================================*/
static void ShowHelp(CRoadWarriorApp *pMe)
{
	if (!pMe->m_pIHelpText)
		ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_STATIC, (void**)&pMe->m_pIHelpText);

	if (pMe->m_pIHelpText)
	{
		const int BufSize = 1024 * sizeof(AECHAR);
		AECHAR* pHelpText = (AECHAR*)MALLOC(BufSize);
		const AECHAR ClrSymbol = IDISPLAY_GetSymbol(pMe->a.m_pIDisplay, AEE_SYM_KEY_CLR, AEE_FONT_NORMAL);
		const AECHAR EndSymbol = IDISPLAY_GetSymbol(pMe->a.m_pIDisplay, AEE_SYM_KEY_END, AEE_FONT_NORMAL);
		AECHAR* pText;

		if (!pHelpText || !ISHELL_LoadResString(pMe->a.m_pIShell, ROADWARRIOR_RES_FILE, IDS_HELP_TEXT, pHelpText, BufSize))
		{
			if (pHelpText)
				FREE(pHelpText);
			ISTATIC_Release(pMe->m_pIHelpText);
			return;
		}

		for (pText = pHelpText; (pText = WSTRCHR(pText, (AECHAR)'|')) != NULL; *pText++ = ClrSymbol)
			;
		for (pText = pHelpText; (pText = WSTRCHR(pText, (AECHAR)'\\')) != NULL; *pText++ = EndSymbol)
			;

		ISTATIC_SetProperties(pMe->m_pIHelpText, ST_TEXTALLOC | ST_NOSCROLL);
		ISTATIC_SetTextEx(pMe->m_pIHelpText, (byte*)pHelpText, NULL, FALSE);

		IMENUCTL_SetActive(pMe->m_pIMainMenu, FALSE);
		IDISPLAY_ClearScreen(pMe->a.m_pIDisplay);
		ISTATIC_Redraw(pMe->m_pIHelpText);
	}
}

/*===========================================================================

FUNCTION RoadWarriorApp_HandleEvent

DESCRIPTION
	This is the EventHandler for this app. All events to this app are handled in this
	function. All APPs must supply an Event Handler.

PROTOTYPE:
	boolean RoadWarriorApp_HandleEvent(CRoadWarriorApp* pMe, AEEEvent eCode, uint16 wParam, uint32 dwParam)

PARAMETERS:
	pi: Pointer to the AEEApplet structure. This structure contains information specific
	to this applet. It was initialized during the AppCreateInstance() function.

	eCode: Specifies the Event sent to this applet

	wParam, dwParam: Event specific data.

DEPENDENCIES
	none

RETURN VALUE
	TRUE: If the app has processed the event
	FALSE: If the app did not process the event

SIDE EFFECTS
	none
===========================================================================*/
//Mandatory Function
static boolean RoadWarriorApp_HandleEvent(CRoadWarriorApp* pMe, AEEEvent eCode, uint16 wParam, uint32 dwParam)
{
	switch (eCode) 
	{
	case EVT_APP_START:
		ShowSplash(pMe);
		return(TRUE);

	case EVT_APP_STOP:
		SaveDefaultRamp(pMe);

		// Release any acquired resources for doing the web access
		WebCleanup(pMe);

		return(TRUE);

	case EVT_APP_SUSPEND:
		// Gotta handle the suspend event, otherwise the app will be quit on a suspend.
		// Also need to cancel any network connections and timers, stop animations and
		// deactivate controls so that things like auto-scrolling aren't done while in
		// a suspended state.
		pMe->m_bSuspended = TRUE;

		WebCleanup(pMe);
		ISHELL_CancelTimer(pMe->a.m_pIShell, 0, pMe);

		if (pMe->m_pISpeedMenu)
		{
			StopTrafficAnim(pMe);
			IMENUCTL_SetActive(pMe->m_pISpeedMenu, FALSE);
		}
		else if (pMe->m_pIMainMenu)
		{
			IMENUCTL_SetActive(pMe->m_pIMainMenu, FALSE);
		}

		return TRUE;

	case EVT_APP_RESUME:
		// During the suspend something may have drawn to the screen, so
		// redraw based on what state we were in last
		pMe->m_bSuspended = FALSE;
		pMe->m_bDownloadErr = FALSE;

		IDISPLAY_ClearScreen(pMe->a.m_pIDisplay);

		if (pMe->m_pSplashImage)
		{
			ShowSplash(pMe);
		}
		else if (pMe->m_pIHelpText)
		{
			ISTATIC_Redraw(pMe->m_pIHelpText);
		}
		else
		{
			DrawLogo(pMe);
			DrawHighway(pMe);

			if (pMe->m_pISpeedMenu)
			{
				IMENUCTL_Redraw(pMe->m_pISpeedMenu);
				IMENUCTL_SetActive(pMe->m_pISpeedMenu, TRUE);
				StartTrafficAnim(pMe);
			}
			else
			{
				ShowMainMenu(pMe);
			}
		}
		return TRUE;

	case EVT_KEY:
		if(pMe->m_pSplashImage != NULL)
		{
			ISHELL_CancelTimer(pMe->a.m_pIShell, 0, pMe);
			ShowMainMenu(pMe);
			return TRUE;
		}
		else if (pMe->m_bIsProgressDisp)
		{
			// Cancel out of a web connection if the CLR key was pressed
			if (wParam == AVK_CLR)
			{
				WebCleanup(pMe);
				IMENUCTL_Redraw(pMe->m_pIMainMenu);
				IDISPLAY_Update(pMe->a.m_pIDisplay);
			}
			return TRUE;
		}

		if (pMe->m_pIHelpText)
		{
			if (wParam == AVK_CLR || wParam == AVK_SELECT)
			{
				ISTATIC_Release(pMe->m_pIHelpText);
				pMe->m_pIHelpText = NULL;
				ShowMainMenu(pMe);
				return TRUE;
			}
			if (ISTATIC_HandleEvent(pMe->m_pIHelpText, eCode, wParam, dwParam))
				return TRUE;
		}
		else if (pMe->m_pISpeedMenu && IMENUCTL_IsActive(pMe->m_pISpeedMenu))
		{
			if (IMENUCTL_HandleEvent(pMe->m_pISpeedMenu, eCode, wParam, dwParam))
			{
				// Draw an animation corresponding to the speed.
				if (wParam == AVK_UP || wParam == AVK_DOWN)
					StartTrafficAnim(pMe);
				return TRUE;
			}
		}
		else if (pMe->m_pIMainMenu && IMENUCTL_IsActive(pMe->m_pIMainMenu))
		{
			if (pMe->m_bDownloadErr)
			{
				pMe->m_bDownloadErr = FALSE;
				IMENUCTL_Redraw(pMe->m_pIMainMenu);
				IDISPLAY_Update(pMe->a.m_pIDisplay);
				return TRUE;
			}
			else if (wParam >= AVK_0 && wParam <= AVK_9)
			{
				// If the user hit a number key while in the main menu,
				// do the type-select-behavior
				TypeToSelectFwy(pMe, (AVKType)wParam);
			}
			else if (wParam == AVK_CLR)
			{
				// Hitting the CLR key while in the main menu means we should exit the app.
				// We do this by returning FALSE to a CLR key event.
				return FALSE;
			}
			else if (IMENUCTL_HandleEvent(pMe->m_pIMainMenu, eCode, wParam, dwParam))
				return TRUE;
		}

		if (wParam == AVK_CLR)
		{
			if (pMe->m_pISpeedMenu)
			{
				// Return back to the main menu from the speed menu.
				SaveDefaultRamp(pMe);
				IMENUCTL_Release(pMe->m_pISpeedMenu);
				pMe->m_pISpeedMenu = NULL;

				StopTrafficAnim(pMe);

				ShowMainMenu(pMe);
			}
			return TRUE;
		}

		return FALSE;

	case EVT_COMMAND:
		if (!pMe->m_pISpeedMenu)
		{
			switch (wParam)
			{
			case IDS_MENU_HELP:
				ShowHelp(pMe);
				break;

			case IDS_MENU_ABOUT:
				ShowSplash(pMe);
				break;

			default:
				// User selected a freeway.
				// Invoke function to read from a web page.
				GetFreewaySpeeds(pMe);
				break;
			}
		}
		return TRUE;
	}
   
	return(FALSE);
}


/*===========================================================================

FUNCTION WebStatusNotification()

DESCRIPTION
	Notification function for the status of the web call.

PARAMETERS:
	pNotifyData: User-defined data, in this case it's where we stuff the pointer
	             to the CRoadWarriorApp object.
	ws: What status is happening
	p: Unused

===========================================================================*/
static void WebStatusNotification(void* pNotifyData, WebStatus ws, void* p)
{
	CRoadWarriorApp* pMe = (CRoadWarriorApp*)pNotifyData;
	int StatusLine = 1;
	int16 nResID = 0;

	NO_REF(p)

	switch (ws)
	{
	case WEBS_CANCELLED:
		nResID = IDS_WEBS_CANCELLED;
		break;
	case WEBS_GETHOSTBYNAME:
		nResID = IDS_WEBS_GETHOSTBYNAME;
		StatusLine = 0;
		break;		  
	case WEBS_CONNECT:
		nResID = IDS_WEBS_CONNECT;
		StatusLine = 0;
		break;
	case WEBS_SENDREQUEST:
		xStatus(pMe, 0, IDS_WEBS_CONNECTED);
		nResID = IDS_WEBS_SENDREQUEST;
		break;
	case WEBS_READRESPONSE:
		nResID = IDS_WEBS_READRESPONSE;
		break;
	case WEBS_GOTREDIRECT:
		nResID = IDS_WEBS_GOTREDIRECT;
		break;
	case WEBS_CACHEHIT:
		nResID = IDS_WEBS_CACHEHIT;
		break;
	}

	if (nResID)
		xStatus(pMe, StatusLine, nResID);
}

/*===========================================================================

FUNCTION IsNetAvailable

DESCRIPTION
	Tell whether or not it's even possible to start up the network.
	This helps to get around several bugs:
		- OEM implementations of gethostbyname that hang forever when the
			phone is not in service.
		- OEM implementations that have undetermined beahvior (often crashing)
			when a data channel is brought up when one is being shut down.

RETURN VALUE
	TRUE: If the data channel can be brought up
	FALSE: If the data channel cannot be brought up
===========================================================================*/
void DummyCallback(void* p, int err)
{
	NO_REF(p)
	NO_REF(err)
}

boolean IsNetAvailable(CRoadWarriorApp* pMe)
{
	INetMgr* pINetMgr;
	boolean bResult = FALSE;

	if (ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_NET, (void**)(&pINetMgr)) == SUCCESS)
	{
		NetState ns = INETMGR_NetStatus(pINetMgr, NULL);

		// This gets around a bug in BREW 1.0.x where creating an INetMgr interface and then
		// releasing it without calling INETMGR_SetLinger() causes an existing data
		// connection to be shut down
		INETMGR_SetLinger(pINetMgr, 10);

		switch (ns)
		{
		case NET_PPP_OPEN:
			bResult = TRUE;
			break;

		case NET_PPP_CLOSING:
			// Some OEM implementations have bugs where trying to start up the network layer
			// while its being closed down can cause crashes, so report that the network is
			// unavailable at that time.
			bResult = FALSE;
			break;

		case NET_PPP_OPENING:
		case NET_PPP_CLOSED:
			{
				ISocket* pISocket = INETMGR_OpenSocket(pINetMgr, AEE_SOCK_DGRAM);

				if (pISocket)
				{
					int32 err = ISOCKET_Connect(pISocket, 0, 0, (PFNCONNECTCB)DummyCallback, (void*)1);
					ISOCKET_Release(pISocket);
					if (err != AEE_NET_ERROR)
						bResult = TRUE;
				}
			}
			break;
		}

		INETMGR_Release(pINetMgr);
	}

	return bResult;
}

/*===========================================================================

FUNCTION GetFreewaySpeeds()

DESCRIPTION
	This function is invoked from HandleEvent() in the main app. Its purpose
	is to demonstrate the usage of the IWeb and IWebResp interfaces to
	perform all the steps necessary to read from a web site.

===========================================================================*/
static void GetFreewaySpeeds(CRoadWarriorApp *pMe)
{
	uint16 CurSel = IMENUCTL_GetSel(pMe->m_pIMainMenu);
	char* url;

	// Make sure the progress box is up
	StartProgressDisplay(pMe);

	if (!IsNetAvailable(pMe))
	{
		xStatus(pMe, 1, IDS_ERR_NO_SERVICE);
		pMe->m_bDownloadErr = TRUE;
		WebCleanup(pMe);
		return;
	}

	// Create an instance of IWeb
	if (!pMe->m_pIWeb && ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_WEB, (void**)(&pMe->m_pIWeb)) != SUCCESS) 
	{
		xStatus(pMe, 1, IDS_ERR_IWEB_FAILED);
		pMe->m_bDownloadErr = TRUE;
		WebCleanup(pMe);
		return;
	}

	IMENUCTL_GetItemData(pMe->m_pIMainMenu, CurSel, (uint32*)&url);
	
	if (url && (url = STRCHR(url, '\t')) != NULL)
	{
		url++;

		pMe->m_Prefs.DefaultFwy = CurSel;
		ISHELL_SetPrefs(pMe->a.m_pIShell, AEECLSID_ROADWARRIOR, ROAD_WARRIOR_PREFS_VERSION,
						&pMe->m_Prefs, sizeof(pMe->m_Prefs));

		pMe->m_BodySize = 0;

		CALLBACK_Init(&pMe->m_Callback, WebReadCB, pMe);
		// Note the odd syntax of this call, which is necessary because it's a macro that
		// gets expanded before the actual variable arguments function call is made.
		IWEB_GetResponse(pMe->m_pIWeb, (pMe->m_pIWeb, &pMe->m_pIWebResp, &pMe->m_Callback, url,
										WEBOPT_HANDLERDATA, pMe,
										WEBOPT_STATUSHANDLER, WebStatusNotification,
										WEBOPT_END));
	}
	else
	{
		xStatus(pMe, 0, IDS_ERR_INTERNAL);
		xStatus(pMe, 1, IDS_ERR_NO_URL);
		pMe->m_bDownloadErr = TRUE;
		WebCleanup(pMe);
	}
}



// Simple caching mechanism to improve file reading performance
#define FILE_CACHE_SIZE 128

typedef struct
{
	long	ValidBytes;
	char	Buf[FILE_CACHE_SIZE];
	char*	BufPtr;
} FileCache;

/*===========================================================================

FUNCTION ReadLine()

DESCRIPTION

	This function reads in a line from a file using a cache structure

PARAMETERS:
	pIFile:		Pointer to the IFile object
	fc:			Pointer to a FileCache object
	Buf:		Buffer in which to put file data
	BufSize:	Size of Buf parameter

RETURN VALUE:
	 1: Full line read in
	 0: Partial line read in
	-1: Error reading

===========================================================================*/
static int ReadLine(IFile* pIFile, FileCache* fc, char* Buf, int BufSize)
{
	int BufSpot = 0;
	int iReturn = TRUE;

	while (1)
	{
		char c;

		if (!fc->ValidBytes)
		{
			fc->ValidBytes = IFILE_Read(pIFile, fc->Buf, sizeof(fc->Buf));
			if (!fc->ValidBytes)
			{
				iReturn = -1;
				break;
			}
			fc->BufPtr = fc->Buf;
		}
		c = *fc->BufPtr++;
		fc->ValidBytes--;

		// Will take just LF or CR-LF as terminator
		if (c == '\n')
		{
			if (BufSpot > 0 && Buf[BufSpot - 1] == '\r')
				Buf[--BufSpot] = 0;
			break;
		}

		if (BufSpot < BufSize - 1)
			Buf[BufSpot++] = c;
		else
			iReturn = FALSE;
	}

	Buf[BufSpot] = 0;

	return iReturn;
}

/*===========================================================================

FUNCTION SetMenuStyle()

DESCRIPTION

	This function sets up a menu with the correct style according to the
	color depth of the device.

PARAMETERS:
	pMe:	Pointer to the app object
	pIMenu:	Pointer to the menu to receive the style and colors

===========================================================================*/
static void SetMenuStyle(CRoadWarriorApp* pMe, IMenuCtl* pIMenu)
{
	if (pMe->m_DeviceColorDepth == 1)
	{
		// Get rid of 3D effect on mono screens
		AEEItemStyle Sel,Normal;

		Sel.ft 		= AEE_FT_BOX;
		Sel.xOffset = 0;
		Sel.yOffset = 0;
		Sel.roImage = AEE_RO_NOT;

		Normal.ft 		= AEE_FT_NONE;
		Normal.xOffset = 0;
		Normal.yOffset = 0;
		Normal.roImage = AEE_RO_COPY;

		IMENUCTL_SetStyle(pIMenu, &Normal, &Sel);
	}
}

/*===========================================================================

FUNCTION ShowMainMenu()

DESCRIPTION

	This function creates a menu control, and fills it with the items from the data file list
	that contains freeway numbers (with directions) and corresponding URLs.

PARAMETERS:
	pMe:	Pointer to the app object

===========================================================================*/
static void ShowMainMenu(CRoadWarriorApp *pMe)
{
	HideSplash(pMe);
	DrawLogo(pMe);
	DrawHighway(pMe);

	if (!pMe->m_pIMainMenu)
	{
		if (ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_MENUCTL, (void **)&(pMe->m_pIMainMenu)) == SUCCESS)
		{
			IFile* pIFile = IFILEMGR_OpenFile(pMe->m_pIFileMgr, "roadwarrior.dat", _OFM_READ);

			IMENUCTL_SetRect(pMe->m_pIMainMenu, &(pMe->m_MenuFrame));
			SetMenuStyle(pMe, pMe->m_pIMainMenu);

			// Add "Help" and "About" menu items
			IMENUCTL_AddItem(pMe->m_pIMainMenu, ROADWARRIOR_RES_FILE, IDS_MENU_HELP, IDS_MENU_HELP, NULL, NULL);
			IMENUCTL_AddItem(pMe->m_pIMainMenu, ROADWARRIOR_RES_FILE, IDS_MENU_ABOUT, IDS_MENU_ABOUT, NULL, NULL);

			if (pIFile)
			{
				char FileBuf[128];
				FileCache fc;
				uint16 nID = 0;

				MEMSET(&fc, 0, sizeof(fc));

				// The data file consists of a number of freeways, with a freeway name and
				// a corresponding URL, separated by a tab character.
				while (ReadLine(pIFile, &fc, FileBuf, sizeof(FileBuf)) >= 0)
				{
					char* tab = STRCHR(FileBuf, '\t');

					if (tab)
					{
						AECHAR WideBuf[16];
						const char* DataCopy = STRDUP(FileBuf);
	
						if (DataCopy)
						{
							*tab = 0;
							STR_TO_WSTR(FileBuf, WideBuf, sizeof(WideBuf));

							IMENUCTL_AddItem(pMe->m_pIMainMenu, NULL, 0, nID++, WideBuf, (uint32)DataCopy);
						}
					}
				}

				IFILE_Release(pIFile);
			}

			// Automatically select the last used freeway as a convenience to the user
			IMENUCTL_SetSel(pMe->m_pIMainMenu, (uint16)pMe->m_Prefs.DefaultFwy);
		}
	}

	if (pMe->m_pISpeedMenu)
		IMENUCTL_SetActive(pMe->m_pISpeedMenu, FALSE);
	if (pMe->m_pIMainMenu)
	{
		IMENUCTL_SetActive(pMe->m_pIMainMenu, TRUE);
		IMENUCTL_Redraw(pMe->m_pIMainMenu);
	}
}
	

/*===========================================================================

FUNCTION TagContents()

DESCRIPTION

	This function parses through HTML data searching for a particular tag and
	returns pointers to the beginning and ending of the data within the tag
	if it was found.

PARAMETERS:
	Html:		Pointer to a pointer of HTML data.  Updated to point after tag contents.
	Tag:		Name of tag to search for content
	Start:		Out pointer to where tag contents start
	End:		Out pointer to where tag contents end
	TagStart:	Out pointer to where tag starts (optional)

RETURN VALUE:
	TRUE:	Tag was found.  Start and End valid.
	FALSE:	Tag was not found.  Start and End not valid.

===========================================================================*/
static boolean TagContents(char** Html, const char* Tag, char** Start, char** End, char** TagStart)
{
	const int TagLen = STRLEN(Tag);
	boolean bFound = FALSE;
	char* h = *Html;

	h = STRCHR(h, '<');
	while (h)
	{
		h++;
		if (STRNICMP(h, Tag, TagLen) == 0 &&
			(h[TagLen] == '>' || h[TagLen] == ' '))
		{
			if (TagStart)
				*TagStart = h - 1;
			break;
		}
		h = STRCHR(h, '<');
	}

	if (h)
	{
		h = STRCHR(h, '>');
		if (h)
		{
			h++;
			*Start = h;
			h = STRCHR(h, '<');

			while (h)
			{
				h++;
				if (*h == '/' && STRNICMP(h + 1, Tag, TagLen) == 0 &&
					(h[TagLen + 1] == '>' || h[TagLen + 1] == ' '))
				{
					*End = h - 2;
					bFound = TRUE;
					break;
				}
				h = STRCHR(h, '>');
				if (!h)
					break;
				h = STRCHR(h, '<');
			}
		}
	}

	*Html = h;

	return bFound;
}

/*===========================================================================

FUNCTION GetRawText()

DESCRIPTION

	This function parses through HTML data and removes tags and newlines,
	making the resulting text better suited for display.  Note that the
	stripping occurs inside the source data itself.

PARAMETERS:
	Html:	Pointer to the HTML data.  Can be modified.

RETURN VALUE:
	Pointer to tag-stripped contents, mainly for convenience.

===========================================================================*/
static char* GetRawText(char* Html)
{
	char* Src = Html;
	char* Dest = Html;
	boolean bInTag = FALSE;

	for (; *Src; Src++)
	{
		if (*Src == '<')
			bInTag = TRUE;
		else if (bInTag && *Src == '>')
			bInTag = FALSE;
		else if (!bInTag && *Src != '\r' && *Src != '\n')
			*Dest++ = *Src;
	}

	*Dest = 0;

	return Html;
}

/*===========================================================================

FUNCTION DisplayResults()

DESCRIPTION

	This function creates a menu control to hold the freeway speeds that it
	parses out of the HTML returned from the web server.

PARAMETERS:
	pMe:	Pointer to the app object

===========================================================================*/
static void DisplayResults(CRoadWarriorApp* pMe)
{
	if (pMe->m_pISpeedMenu ||
		ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_MENUCTL, (void **)&(pMe->m_pISpeedMenu)) == SUCCESS)
	{
		char* b = pMe->m_BodyBuffer;
		char* TagStart;
		char* Start;
		char* End;
		uint16 nID = 0;
		CtlAddItem cia;

		IMENUCTL_Reset(pMe->m_pISpeedMenu);

		IMENUCTL_SetRect(pMe->m_pISpeedMenu, &(pMe->m_MenuFrame));

		SetMenuStyle(pMe, pMe->m_pISpeedMenu);
				   
		if (!TagContents(&b, "CAPTION", &Start, &End, NULL))
		{
			b = pMe->m_BodyBuffer;
			while (TagContents(&b, "td", &Start, &End, &TagStart))
			{
				Start[-1] = 0;
				TagStart = STRRCHR(TagStart, '<');
				if (TagStart && STRSTR(TagStart, "class=caltext"))
					break;
			}
		}
		if (!b)
		{
			IMENUCTL_AddItem(pMe->m_pISpeedMenu, ROADWARRIOR_RES_FILE, IDS_ERR_NO_DATA, 0, NULL, 0);
		}
		else
		{
			// The "CAPTION" tag contains the freeway name and the date/time stamp
			// of when the speeds were acquired.  Make it the first item in the
			// menu and in bold so that it stands out, but it can be scrolled out
			// of view so that it doesn't always take up valuable space.
			AECHAR Caption[64];
			const boolean bDoMenuItemBitmaps = (boolean)(pMe->m_DeviceColorDepth >= 2);

			End[1] = 0;
			STR_TO_WSTR(GetRawText(Start), Caption, sizeof(Caption));

			MEMSET(&cia, 0, sizeof(cia));
			cia.pText = Caption;
			cia.wFont = AEE_FONT_BOLD;
			cia.wItemID = nID++;
			IMENUCTL_AddItemEx(pMe->m_pISpeedMenu, &cia);

			MEMSET(&cia, 0, sizeof(cia));
			if (bDoMenuItemBitmaps)
				cia.pszResImage = ROADWARRIOR_RES_FILE;

			while (1)
			{
				AECHAR Item[64];
				char* Location;
				char* Speed;
				int   iSpeed;
				char  Buf[4];
				char* c;
				int   len;

				while (TagContents(&b, "TD", &Location, &End, &TagStart))
				{
					Location[-1] = 0;
					TagStart = STRRCHR(TagStart, '<');
					if (TagStart && STRSTR(TagStart, "class=menuitem"))
						break;
				}
				if (!b)
					break;
				End[1] = 0;

				if (!TagContents(&b, "TD", &Speed, &End, NULL))
					break;
				End[1] = 0;

				GetRawText(Speed);

				// Speed can be "x mph", "xx mph" (x = number), or "not avail".
				MEMSET(Buf, 0, 4);
				c = STRRCHR(Speed, ' ');
				len = (int) (c+1 - Speed);				// len = # characters in the number (1 or 2).
				STRNCPY(Buf, Speed, len);

				// It assumes that if the first character is a number, the 2nd
				// (if any) is also a number.
				if (Buf[0] > '9' || Buf[0] < '0') 		  // Take care of "not avail" case here.
				{
					iSpeed = -1;
					STRCPY(Speed, "NA ");
					len = 3;
				}
				else 
					iSpeed = ATOI(Buf);

				STR_TO_WSTR(Speed, Item, (len+1) * sizeof(AECHAR));
				STR_TO_WSTR(GetRawText(Location), Item + len, sizeof(Item) - len * sizeof(AECHAR));

				cia.pText = Item;
				if (bDoMenuItemBitmaps)
					cia.wImage = (uint16)(IDB_MENU_ITEM_SLOW + SPEED_CATEGORY(iSpeed));
				cia.wItemID = nID++;
				cia.dwData = iSpeed;
				IMENUCTL_AddItemEx(pMe->m_pISpeedMenu, &cia);
			}
			
			IMENUCTL_SetSel(pMe->m_pISpeedMenu, pMe->m_Prefs.DefaultRamp[pMe->m_Prefs.DefaultFwy]);
		}

		StopProgressDisplay(pMe);

		if (!pMe->m_bSuspended)
		{
			IMENUCTL_SetActive(pMe->m_pIMainMenu, FALSE);
			IMENUCTL_SetActive(pMe->m_pISpeedMenu, TRUE);

			DrawLogo(pMe);
			StartTrafficAnim(pMe);
		}
	}
}


/*===========================================================================

FUNCTION WebReadCB()

DESCRIPTION

	This is the function that reads from the web page. It will be called after a
	successful connect, and after some data has been read it may schedule itself to
	be called again.

	It takes a 'void *' so it can be passed to ISOURCE_Readable() without a cast.
	(Safer to cast a simple structure pointer than to cast a function pointer.)

PARAMETERS:
	pMe: Pointer to the AEEApplet structure. 

===========================================================================*/
static void WebReadCB(void* cxt)
{
	CRoadWarriorApp* pMe = (CRoadWarriorApp*)cxt;
	WebRespInfo* pWebRespInfo = IWEBRESP_GetInfo(pMe->m_pIWebResp);
	int ByteCount;
	char buf[128];

	if (pWebRespInfo)
	{
		if (!WEB_ERROR_SUCCEEDED(pWebRespInfo->nCode))
		{
			xStatus(pMe, 1, IDS_ERR_NO_SERVICE);
			pMe->m_bDownloadErr = TRUE;
		}
		else
		{
			ISource* pISource = pWebRespInfo->pisMessage;

			ByteCount = ISOURCE_Read(pISource, (char*)buf, sizeof(buf));

			// Read() return values fall into four categories of interest:
			//
			//   1. ISOURCE_WAIT:  This means data is not yet available
			//   2. ISOURCE_ERROR: An eror occurred
			//   3. ISOURCE_END:   No more data
			//   4. >0 : The actual data that was read
			//

			switch (ByteCount)
			{
			case ISOURCE_WAIT:
				xStatus(pMe, 1, IDS_WAITING);
				ISOURCE_Readable(pISource, &pMe->m_Callback);
				return;

			case ISOURCE_ERROR:
				// Connection hosed.
				xStatus(pMe, 1, IDS_ERR_READ);
				pMe->m_bDownloadErr = TRUE;
				break;

			case ISOURCE_END:
				// No more data, display the results
				if (pMe->m_BodySize < pMe->m_BodyAllocSize)
					pMe->m_BodyBuffer[pMe->m_BodySize] = 0;
				else
					pMe->m_BodyBuffer[pMe->m_BodyAllocSize - 1] = 0;
				
				DisplayResults(pMe);
				break;

			default:
				if (ByteCount)
				{
					if (pMe->m_BodySize + ByteCount > pMe->m_BodyAllocSize)
					{
						const int NewSize = pMe->m_BodyAllocSize + 1024;
						char* NewBuf = (char*)REALLOC(pMe->m_BodyBuffer, NewSize);

						if (NewBuf)
						{
							pMe->m_BodyBuffer = NewBuf;
							pMe->m_BodyAllocSize = NewSize;
						}
					}
					if (pMe->m_BodySize + ByteCount <= pMe->m_BodyAllocSize)
					{
						MEMCPY(pMe->m_BodyBuffer + pMe->m_BodySize, buf, ByteCount);
						pMe->m_BodySize += ByteCount;
					}
				}

				ISOURCE_Readable(pISource, &pMe->m_Callback);
				return;
			}
		}

		WebCleanup(pMe);
	}
}


/*===========================================================================

FUNCTION WebCleanup()

DESCRIPTION
	Free any resources allocated as part of the web retrieval.  This can be
	called before the app exits, or before the web access is repeated.

===========================================================================*/
static void WebCleanup(CRoadWarriorApp *pMe)
{
	CALLBACK_Cancel(&pMe->m_Callback);

	if (pMe->m_pIWebResp)
	{
		IWEBRESP_Release(pMe->m_pIWebResp);
		pMe->m_pIWebResp = NULL;
	}

	if (pMe->m_pIWeb) 
	{
		IWEB_Release(pMe->m_pIWeb);
		pMe->m_pIWeb = NULL;
	}

	if (pMe->m_BodyBuffer)
	{
		FREE(pMe->m_BodyBuffer);
		pMe->m_BodyBuffer = NULL;
	}
	pMe->m_BodySize = 0;
	pMe->m_BodyAllocSize = 0;

	StopProgressDisplay(pMe);
}
