/****************************/
/*      INTERNET ROUTINES   */
/* (c)2004 Pangea Software  */
/* By Brian Greenstone      */
/****************************/


/***************/
/* EXTERNALS   */
/***************/



extern	PrefsType	gGamePrefs;
extern	unsigned char	gRegInfo[64];
extern	short	gPrefsFolderVRefNum;
extern	long	gPrefsFolderDirID;
extern	Str255  gSerialFileName;
extern	FSSpec				gDataSpec;
extern	Boolean	gLittleSnitch;
extern	CFBundleRef 		gBundle;


/****************************/
/*    PROTOTYPES            */
/****************************/

static OSStatus DownloadURL(const char *urlString, Handle buffer);

static OSErr SkipToPound(void);
static OSErr FindBufferStart(void);
static Boolean InterpretTag(void);
static void ParseNextWord(Str255 s);
static void InterpretVersionName(void);
static void InterpretNoteName(void);
static void InterpretBadSerialsName(void);
static void DoVersionDialog(Str255 s);


/****************************/
/*    CONSTANTS             */
/****************************/

#define	SERIAL_LENGTH		12			// # of characters in our serial #'s

#define	CHAR_CR				0x0d		// ASCII code for Carriage Return
#define	CHAR_LF				0x0a		// ASCII code for Line Feed


/**********************/
/*     VARIABLES      */
/**********************/

Handle				gHTTPDataHandle = nil;
static	int			gHTTPDataSize = 0;
static	int			gHTTPDataIndex = 0;



/************************** READ HTTP DATA **********************************/

void ReadAndParseHTTPMetaFile(void)
{
OSStatus err;
const char *urlString = "http://www.pangeasoft.net/book/updatedata.txt";
	
	
				/* DONT DO IT UNLESS THERE'S A TCP/IP CONNECTION */
				
	if (!IsInternetAvailable())
		return;
	
	
			/* ALLOCATE BUFFER TO STORE DATA */
			
	if (gHTTPDataHandle == nil)
	{
		gHTTPDataSize = 40000;
		gHTTPDataHandle = AllocHandle(gHTTPDataSize);		// alloc 40k buffer
	}			
		
			/***************************/
			/* DOWNLOAD FILE TO BUFFER */
			/***************************/
			
	gHTTPDataIndex = 0;

	err = DownloadURL(urlString, gHTTPDataHandle);		
	if (err)
		return;
		
		
			/****************/
			/* PARSE BUFFER */
			/****************/

			/* FIND BUFFER START */
	
	if (FindBufferStart() != noErr)					// if buffer is bad (probably 404 File Not Found)
		return;

			/* READ AND HANDLE ALL TAGS */
			
	while (!InterpretTag())
	{
	
	};							
}


/****************** INTERPRET TAG **********************/
//
// Returns true when done or error
//

static Boolean InterpretTag(void)
{
u_long	name, *np;
char	*c;

	SkipToPound();													// skip to next tag

	c = *gHTTPDataHandle;											// point to buffer
	np = (u_long *)(c + gHTTPDataIndex);							// point to tag name
	name = *np;														// get tag name
	gHTTPDataIndex += 4;											// skip over it

	switch(name)
	{	
		case	'BVER':												// BEST VERSION name
				InterpretVersionName();
				break;

		case	'NOTE':												// NOTE name
				InterpretNoteName();
				break;
				
		case	'BSER':												// BAD SERIALS name
				InterpretBadSerialsName();
				break;

		case	'TEND':												// END name
				return(true);
	}
	
	return(false);
}			


/******************* INTERPRET VERSION NAME ************************/
//
// We got a VERS tag, so let's read the version and see if our app's verion is this recent.
//

static void InterpretVersionName(void)
{
Str255		s;
NumVersion	*fileVers;
NumVersion	bestVers;
UInt32		vers;

			/* READ VERSION STRING */
			
	ParseNextWord(s);
	
		
		/* CONVERT VERSION STRING TO NUMVERSION */
		
	bestVers.majorRev		=	s[1] - '0';					// convert character to number
	bestVers.minorAndBugRev =	s[3] - '0';
	bestVers.minorAndBugRev <<= 4;
	bestVers.minorAndBugRev |=	s[5] - '0';
		
	
			/* GET OUR VERSION */

	vers = CFBundleGetVersionNumber(gBundle);
	fileVers =  (NumVersion *)&vers;
			


		/* SEE IF THERE'S AN UPDATE */
			
	if (bestVers.majorRev > fileVers->majorRev)		// see if there's a major rev better
	{
		goto update;	
	}
	else
	if ((bestVers.majorRev == fileVers->majorRev) &&		// see if there's a minor rev better
		(bestVers.minorAndBugRev > fileVers->minorAndBugRev))
	{
update:
		DoVersionDialog(s);
	}

}


/****************** DO VERSION DIALOG *************************/

static void DoVersionDialog(Str255 s)
{
Str255			v = "\pVersion               ";
SInt16      	alertItemHit;

	BlockMove(&s[1], &v[9], s[0]);				// copy version number string into full string


	Enter2D();
	
	StandardAlert(kAlertPlainAlert, "\pThere is an update for this game available at www.yoursite.com/downloads :", v, NULL, &alertItemHit);
	
	Exit2D();
}


/********************* INTERPRET NOTE NAME ***************************/
//
// We got a NOTE tag, so let's interpret it and display the note in a dialog.
//

static void InterpretNoteName(void)
{
int		noteID,i;
char	*c = *gHTTPDataHandle;											// point to buffer
Str255	s;
SInt16      alertItemHit;

		/****************************/
		/* READ NOTE'S 4-DIGIT CODE */
		/****************************/

	noteID = (c[gHTTPDataIndex++] - '0') * 1000;
	noteID += (c[gHTTPDataIndex++] - '0') * 100;
	noteID += (c[gHTTPDataIndex++] - '0') * 10;
	noteID += (c[gHTTPDataIndex++] - '0');
	
		
		/* SEE IF THIS NOTE HAS ALREADY BEEN SEEN */

					// note:  for the purposes of this sample code we're not going to actually check this
					
//	if (gGamePrefs.didThisNote[noteID])
//		return;


			/********************/
			/* READ NOTE STRING */
			/********************/
			
	i = 1;
	s[0] = 0;
	while(c[gHTTPDataIndex] != '#')							// scan until next # marker
	{
		s[i++] = c[gHTTPDataIndex++];
		s[0]++;
	}

			/* DO ALERT WITH NOTE */
			
	Enter2D();
	StandardAlert(kAlertStopAlert, s, NULL, NULL, &alertItemHit);
	Exit2D();
	
					// note:  for the purposes of this sample code we're not going to actually do this
//	gGamePrefs.didThisNote[noteID] = 0xff;
//	SavePrefs();
}


/********************* INTERPRET BAD SERIALS NAME ***************************/
//
// We got a BSER tag, so let's read each serial and see if it's ours.
//

static void InterpretBadSerialsName(void)
{
char	serial[SERIAL_LENGTH+1];
Str255	s;

	printf("Here's the list of pirate serial numbers from the file:\n");


	serial[SERIAL_LENGTH] = 0x00;										// put NULL mark at end of C string
	
	while(true)
	{
		ParseNextWord(s);												// read next word
		if (s[1] == '*')												// see if this is the end marker
			break;
			
			/* SAVE SERIAL INTO SOME FILE */
			//
			// NOTE:  We're not actually saving this into anything in this sample app.
			//		In your application you would want to save the list of pirate serials into
			//		a hidden file.  Then, each time the game is launched the user's serial #
			//		will be verified against this list.  Here we're just copying the serial #
			//		into a string and printing it to the Console.
			//
			
		BlockMove(&s[1], &serial, SERIAL_LENGTH);					// copy serial into string
		printf(serial);
		printf("\n");
	}
}



/********************* PARSE NEXT WORD ****************************/
//
// Reads the next "word' from the buffer.  A word is text between
// any spaces or returns.
// 

static void ParseNextWord(Str255 s)
{
char 	*c = *gHTTPDataHandle;														// point to buffer
int		i = 1;

		/* SKIP BLANKS AT FRONT */
		
	while((c[gHTTPDataIndex] == ' ') || (c[gHTTPDataIndex] == CHAR_CR) || (c[gHTTPDataIndex] == CHAR_LF))
	{
		gHTTPDataIndex++;
	}


		/* READ THE GOOD CHARS */
		
	do
	{
		s[i] = c[gHTTPDataIndex];												// copy from buffer to string
		gHTTPDataIndex++;
		s[0] = i;
		i++;
	}while((c[gHTTPDataIndex] != ' ') && (c[gHTTPDataIndex] != CHAR_CR) && (c[gHTTPDataIndex] != CHAR_LF));	// stop when reach space or CR
}


/********************* FIND BUFFER START ***************************/
//
// Skips to the #$#$ tag in the buffer which denotes the beginning of our data
//

static OSErr FindBufferStart(void)
{
	gHTTPDataIndex = 0; 						// start @ beginning of file		
	
	while(true)
	{
		char *c = *gHTTPDataHandle;				// point to buffer
		
		if (SkipToPound() != noErr)
			return(1);
		
		if ((c[gHTTPDataIndex] == '$') &&		// see if $#$ 
			(c[gHTTPDataIndex+1] == '#') &&
			(c[gHTTPDataIndex+2] == '$'))
		{
			if (SkipToPound() != noErr)						// skip to next tag
				return(1);
			return(noErr);		
		}
		
		if (gHTTPDataIndex > gHTTPDataSize)		// see if out of buffer range
			return(1);
	};
	
}



/********************** SKIP TO POUND ************************/
//
// Skips to the next # character in the buffer
//

static OSErr SkipToPound(void)
{
char	*c;

	c = *gHTTPDataHandle;						// point to buffer

	while(c[gHTTPDataIndex] != '#')				// scan for #
	{
		gHTTPDataIndex++;
		if (gHTTPDataIndex >= gHTTPDataSize)	// see if out of buffer range
			return(1);
	}

	gHTTPDataIndex++;							// skip past the #

	return(noErr);
}


#pragma mark -

/************************** DOWNLOAD URL **********************************/
//
// Downloads the file indicated by the URL into the input buffer.
//

static OSStatus DownloadURL(const char *urlString, Handle buffer)
{
OSStatus 	err;

			/* MAKE SURE THERE'S A BUFFER TO LOAD INTO */
			
	if (buffer == nil)
		DoFatalAlert("\pDownloadURL: gHTTPDataHandle == nil");


			/* DOWNLOAD DATA */

	err = URLSimpleDownload(
						urlString,					// pass the URL string
						nil,						// download to buffer, not FSSpec
						buffer,						// buffer
						0, 							// no flags
						nil,						// no callback eventProc
						nil);						// no userContex

	return(err);
	

}


#pragma mark -

/************************ IS INTERNET AVAILABLE ****************************/

Boolean IsInternetAvailable(void)
{
OSErr				iErr;
InetInterfaceInfo 	info;

	iErr = OTInetGetInterfaceInfo(&info, kDefaultInetInterface);

	if (iErr)
		return(false);

	return(true);
}


/************************ LAUNCH URL *****************************/

OSStatus LaunchURL(ConstStr255Param urlStr)
{
OSStatus err;
ICInstance inst;
long startSel;
long endSel;

    err = ICStart(&inst, kGameID);     
    if (err == noErr)
    {
	    startSel = 0;
	    endSel = urlStr[0];
	    err = ICLaunchURL(inst, "\p", (char *) &urlStr[1], urlStr[0], &startSel, &endSel);
        ICStop(inst);
    }
    return (err);
}


