#include <reggie.h>
#include <network_tool.h>
#include <platform.h>
#include <lsockets.h>
#include <stdio.h>
#include <string.h>
#include "tracker.h"
#include "gameinitexit.h"
#include "gametime.h"
#include "carphysics.h"
#include "config.h"
#include "text.h"
#include "gameframe.h"
#include "error.h"
#include "initexit.h"
#include "network.h"
#include "interfaceutil.h"
#include "reg_tool_3.h"
#include "gamesystem.h"
#include "screen.h"

ReggieRef gReggieRef=NULL;
int gReggieConnected=false;
int gReggieSearching=false;
int gReggieLastUpdateGInfoVersion;
int gHasCheckedLonelyServer;
int gReggiePortMapped=false;
int gReggieCounting=false;
float gLastQuery=-1000;
float gConnectionTime;
float gReportedIdle=false;
char gMOTD[1024];

extern NT_SessionRef gSessionRef;
extern NetworkDatagramRef NT_GetSessionEndpoint(NT_SessionRef ref);

#define kReggieUpdateInterval 1

int CouldMyNATCauseMyServerToBeLonely() {
	NetworkAddress local;
	DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local);
	ReggieNetworkType network = ReggieProbeCheck(gReggieRef,false,&local,nil);
	return((network != kReggieNetworkOffline) && (network <=kReggieNetworkProtective) ? true : false);
}

#define kMessageInputBufferSize 8
#define kMaxMessageSize 1024
char gMessageInputBuffer[kMessageInputBufferSize][kMaxMessageSize];
int gMessageInputBufferPos=0;
int gCheckingVersion=false;
char *gRedlineURL=NULL;
char *gAutoUpdateURL=NULL;

void TrackerStartVersionCheck()
{
    if(NetworkStackGetActive(eNetworkStackTCPIP))
	{
		ReggieVersionBegin(nil,"REDLINE");
		gCheckingVersion = true;
	}
}

int TrackerVersionCheck(char **newVersion)
{
	if(gCheckingVersion)
	{
		HashRef hashRef=ReggieVersionCheck(nil,"REDLINE");
		if(hashRef)
		{
			if(gConfig->useBetaBuilds)
			{
				gRedlineURL=HashLookup(hashRef, "$BETAURL", nil);
				gAutoUpdateURL=HashLookup(hashRef, "$BETAAUTOUPDATEURL", nil);
				Char8 *hashLookupResult;
				int versionNum=0;
				if(hashLookupResult=HashLookup(hashRef, "$BETAVERSIONNUM", nil))
					sscanf(hashLookupResult,"%d",&versionNum);
				if(*newVersion=HashLookup(hashRef, "$BETAVERSION", nil))
				{
					gCheckingVersion = false; /* Don't check again */
					if(versionNum)
						return versionNum>kVersion;
					else
						return _stricmp(*newVersion,kVersionString);
				}
			}
			else
			{
				gRedlineURL=HashLookup(hashRef, "$URL", nil);
				gAutoUpdateURL=HashLookup(hashRef, "$AUTOUPDATEURL", nil);
				Char8 *hashLookupResult;
				int versionNum=0;
				if(hashLookupResult=HashLookup(hashRef, "$VERSIONNUM", nil))
					sscanf(hashLookupResult,"%d",&versionNum);
				if(*newVersion=HashLookup(hashRef, "$VERSION", nil))
				{
					gCheckingVersion = false; /* Don't check again */
					if(versionNum)
						return versionNum>kVersion;
					else
						return _stricmp(*newVersion,kVersionString);
				}
			}
		}
	}
	return false;
}

int TrackerConnect()
{
	gStartIdleTime=TimeGetSeconds();
	if(!gSessionRef)
		NetworkPreSession();
	if(!ReggieCreateShared(&gReggieRef,NULL,NULL,NT_GetSessionEndpoint(gSessionRef),NULL,"redline",gConfig->playerName,""))
	{
		gReggieLastUpdateGInfoVersion=-1;
		gHasCheckedLonelyServer=false;
		gConnectionTime=0;
		gReggiePortMapped=false;
		gReggieConnected=true;
		gReggieSearching=false;
		gReggieCounting=false;
		return true;
	}
	return false;
}

int ValidateUpdateURL()
{
	if(!gAutoUpdateURL)
		return false;
	char *pos=strstr(gAutoUpdateURL,"://");
	if(!pos)return false;
	pos+=3;
	char *dash=strstr(pos,"/");
	if(!dash)return false;
	char validurl[]=".ambrosiasw.com";
	char test[256];
	memcpy(test,dash-strlen(validurl),strlen(validurl));
	test[strlen(validurl)]='\0';
	if(_stricmp(test,validurl)) {printf("update URL invaild\n");return false;}
	return true;
}

void TrackerGetNewVersion()
{
	if(ValidateUpdateURL())
	{
		if(!ScreenNoWindow())
		{
			if(gConfig->fullscreen)
			{
				gConfig->fullscreen=false;
				ScreenReInit();
				gConfig->fullscreen=true;
			}
			InterfaceDrawStrings("Trying automatic update","Please wait a moment...",-1);
			char updateURL[1024];
			char failStr[1024]="";
			sprintf(updateURL,"%s%s",gAutoUpdateURL,kVersionString);
			if(AutoUpdateRedline(updateURL,failStr))
		//	if(AutoUpdateRedline(gAutoUpdateURL))
			{
				InterfaceDisplayMessage(-1,"Update successful!","You will have to relaunch Redline now.");
				return;
			}
			else
				InterfaceDisplayMessage(-1,"Automatic update failed.",failStr);		
		}
	}
	if(gRedlineURL)
	{
		CFStringRef str=CFStringCreateWithCString(kCFAllocatorDefault,gRedlineURL, kCFStringEncodingASCII);
		CFURLRef url=CFURLCreateWithString(kCFAllocatorDefault,str,NULL);
		//LSOpenCFURLRef(url,NULL);
		LSLaunchURLSpec inLaunchSpec;
		inLaunchSpec.appURL=NULL;
		inLaunchSpec.itemURLs=CFArrayCreate(kCFAllocatorDefault,(const void**)&url,1,NULL);
		inLaunchSpec.passThruParams=NULL;
		inLaunchSpec.launchFlags=kLSLaunchNoParams;
		inLaunchSpec.asyncRefCon=NULL;
		LSOpenFromURLSpec(&inLaunchSpec,NULL);
	}
	else
	{
		CFURLRef url=CFURLCreateWithString(kCFAllocatorDefault,CFSTR("http://www.AmbrosiaSW.com/"),NULL);
		//LSOpenCFURLRef(url,NULL);
		LSLaunchURLSpec inLaunchSpec;
		inLaunchSpec.appURL=NULL;
		inLaunchSpec.itemURLs=CFArrayCreate(kCFAllocatorDefault,(const void**)&url,1,NULL);
		inLaunchSpec.passThruParams=NULL;
		inLaunchSpec.launchFlags=kLSLaunchNoParams;
		inLaunchSpec.asyncRefCon=NULL;
		LSOpenFromURLSpec(&inLaunchSpec,NULL);
	}	
}

enum{
	kReggieAdvGameChannel=1,
	kReggieSearchGameChannel,
	kReggieSearchPlayersChannel=4,
	kReggiePortMapChannel,
	kReggieInsertRecordChannel,
	kReggieRegistryChannel,
	kReggieRecordChannel
};

void TrackerMessageSend(char *message)
{
	if(gReggieConnected)
	{
		HashRef hashRef = HashCreate(kHashFlagReggieKeys);
		HashAppend(hashRef, "$KIND", "REDLINE");
		HashAppend(hashRef, "$MESSAGE", message);
		
		ReggieMessageSend(gReggieRef,nil,hashRef);
		HashDispose(hashRef); 
		if(gMessageInputBufferPos<kMessageInputBufferSize)
			strcpy(gMessageInputBuffer[gMessageInputBufferPos++],message);
	}
}

int TrackerMessageReceive(tChatMessage *msg)
{
	static char motd[1024]="";
	static char lastMessage[1024]="";
	
	if(gReggieConnected)
	{
		if(strcmp(motd,gMOTD))
		{
			strcpy(motd,gMOTD);
			sprintf(msg->str,"### %s",motd);
			msg->flags=kChatFlagSystem|kChatFlagSilent;
			return true;
		}
		HashRef hash=ReggieMessageCheck(gReggieRef);
		if(hash)
		{
			char *hashResult=nil;
			do{
				hashResult=HashLookup(hash,"$MESSAGE",hashResult);
				if(hashResult)
					if(gMessageInputBufferPos<kMessageInputBufferSize&&strlen(hashResult)<kMaxMessageSize)
					{
						int dupe=false;
						for(int i=0;i<gMessageInputBufferPos;i++)
							if(strcmp(gMessageInputBuffer[i],hashResult)==0)
								dupe=true;
						if(!dupe)
							strcpy(gMessageInputBuffer[gMessageInputBufferPos++],hashResult);
					}
			}while(hashResult);
		}
		if(gMessageInputBufferPos)
		{
			strcpy(msg->str,gMessageInputBuffer[--gMessageInputBufferPos]);
			msg->flags=0;
			if(strcmp(lastMessage,msg->str))
			{
				strcpy(lastMessage,msg->str);
				return true;
			}
			else
				return false;
		}
	}
	return false;
}

void NetworkAdvertiseIdle(tGameInfo *gInfo,int *showLonelyMessage,int locked,int forceUpdate)
{
	if(showLonelyMessage)
		*showLonelyMessage=false;
	if(gReggieConnected)
	{
		UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL);
		if(state==kReggieStateOnline)
		{
			if(gConnectionTime==0)
				gConnectionTime=TimeGetSeconds();
			if((gReggieLastUpdateGInfoVersion!=gInfo->version||forceUpdate)&&gConfig->trackerEnable)
			{
				char hashString[1024];		
				if(gInfo->map==kFileErr)		
					return;
				tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(gInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo));
				while(char *ch=strchr(gConfig->gameName,'\''))
					*ch='`';
				sprintf(hashString,"$REDLINE$HOSTING=1 $REDLINE$PASSWORD='%d' $REDLINE$NUMPLAYERS=%d $REDLINE$NUMNETPLAYERS=%d $REDLINE$MAXPLAYERS=%d $REDLINE$ARCADE=%d $REDLINE$LOCKED=%d $REDLINE$ONLYREGISTERED=%d $REDLINE$LAPS=%d",strlen(gConfig->password)?1:0,gInfo->numPlayers,gInfo->numNetPlayers,gConfig->maxPlayers,gInfo->arcade,locked,gConfig->onlyRegisteredPlayers,gInfo->numLaps);
				HashRef hashRef = HashInitialize(kHashFlagReggieKeys,hashString);
				HashAppend(hashRef, "$MESSAGE", gConfig->gameName);
				HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name);
				HashAppend(hashRef, "$REDLINE$MAPFILE",FileGetName(gInfo->map));

				for(int i=0;i<gInfo->numPlayers;i++)
				{
					NT_MemberInfo memberInfo;
					char key[256];
					sprintf(key,"$REDLINE$PLAYER%dNAME",i);
					HashAppend(hashRef, key, gInfo->playerNames[i]);
					if(gInfo->playerCars[i]!=-1)
					{
						tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gInfo->playerCars[i],kParserTypeCarDesc,sizeof(tCarDefinition));
						sprintf(key,"$REDLINE$PLAYER%dCAR",i);
						HashAppend(hashRef, key, car->carName);
					}
					if(i>0&&i<gInfo->numNetPlayers)
					{
						if(NT_GetMemberInfo(gSessionRef,gInfo->netID[i],&memberInfo))
						{
							char address[256];
							AddressTranscribe(&memberInfo.address,address);
							sprintf(key,"$ADDRESS$PLAYER%d",i);						
							HashAppend(hashRef, key, address);
						}
					}
					else if(i==0)
					{
						NetworkAddress local,publicAdd;
						char address[256];
						DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local);
						ReggieProbeCheck(gReggieRef,false,&local,&publicAdd);
						if(!AddressIsEmpty(&publicAdd))
						{
							AddressTranscribe(&publicAdd,address);	
							HashAppend(hashRef, "$ADDRESS$PLAYER0", address);
						}
					}
				}
				
				ReggieInsertBegin(gReggieRef,kReggieAdvGameChannel, kReggieRecordAdvertise ,hashRef);/* Makes own copy of hash */
				HashDispose(hashRef); /* Not needed any more */
				gReggieLastUpdateGInfoVersion=gInfo->version;
			}
			if(!gHasCheckedLonelyServer)
				if(TimeGetSeconds()>gConnectionTime+3)
					if(showLonelyMessage)
					{
						*showLonelyMessage=CouldMyNATCauseMyServerToBeLonely();
						//printf("CouldMyNATCauseMyServerToBeLonely sez %d\n",*showLonelyMessage);
						gHasCheckedLonelyServer=true;
					}
			if(!gReggiePortMapped)
				if(TimeGetSeconds()>gConnectionTime+1)
				{
					NetworkAddress local;
					UInt16 localPort;
					DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local);
					AddressDecomposeTCPIP(&local,&localPort,nil);
					U16Swap(localPort);
					//printf("PORTMAP: %d\n",ReggiePortmapStatus(gReggieRef, nil, nil, &local));
					//if (ReggiePortmapStatus(gReggieRef, nil, nil, &local) > kReggiePortmapNone)
						ReggiePortmapBegin(gReggieRef,kReggiePortMapChannel,false,localPort,localPort,"Redline");
					gReggiePortMapped=true;
				}
		}
		else if(state!=kReggieStatePending)
		{
			NetworkStopAdvertising();
			ReggieDispose(gReggieRef,true);
			gReggieConnected=false;
		}
	}
	else
		TrackerConnect();
}

void NetworkStopAdvertising()
{
	if(gReggieConnected)
	{
		ReggieChannelClose(gReggieRef,kReggieAdvGameChannel);
//		ReggieDispose(gReggieRef,true);
//		gReggieConnected=false;
	}
}


void QueryGameSearch()
{
	HashRef hashRef = HashInitialize(kHashFlagReggieKeys, "$KIND=redline $REDLINE$HOSTING=1");
	ReggieSearchBegin(gReggieRef,kReggieSearchGameChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); /* Makes own copy of hash */
	HashDispose(hashRef); /* Not needed any more */
}

void QueryPlayerSearch()
{
	HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0");
	ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); 
	HashDispose(hashRef); /* Not needed any more */
}

void AdvertisePlayer()
{
	char hashString[1024];	
	float idleAdd=TimeGetSeconds()-gStartIdleTime;	
	int reportIdle=(idleAdd>300);
	gReportedIdle=reportIdle;
	UInt32 startIdleLocalTime=TimerGetLocalSeconds()-idleAdd;
	if(reportIdle)
		sprintf(hashString,"$REDLINE$HOSTING=0 $TIME$IDLE=%d",startIdleLocalTime);
	else
		sprintf(hashString,"$REDLINE$HOSTING=0");	
	HashRef hashRef = HashInitialize(kHashFlagReggieKeys,hashString);
	char name[1024];
	sprintf(name,"%s%s",RT3_IsRegistered()?"":"\255demo.png\255 ",gConfig->playerName);
	HashAppend(hashRef, "$NAME", name);
	ReggieInsertBegin(gReggieRef,kReggieAdvGameChannel,kReggieRecordAdvertise,hashRef);
	HashDispose(hashRef);
}

void NetworkSearchGamesInit()
{
	gLastQuery=0;
	QueryGameSearch();
	QueryPlayerSearch();
	AdvertisePlayer();
	//ReggieMessageBlock(gReggieRef,NULL,false);
	gReggieSearching=true;
}

int NetworkCountPlayers()
{
	static UInt32 lastPlayerStamp=-1;
	static int lastCount=0;
	if(gReggieConnected)
	{
		UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL);
		if(!(state==kReggieStateRefused||state==kReggieStateOffline))
		{
			if(!gReggieCounting)
			{
				HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0 COUNT");
				ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); 
				HashDispose(hashRef); /* Not needed any more */
				gReggieCounting=true;
			}

	        SInt32 reggieCount,totalCount;
			UInt32 reggieStamp;
	        Bool8 outdated;
			reggieStamp=ReggieSearchStatus(gReggieRef,kReggieSearchPlayersChannel,&reggieCount,&totalCount,&outdated);
			if(reggieStamp!=lastPlayerStamp)
			{
		        if(outdated)
				{
					HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0 COUNT");
					ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); 
					HashDispose(hashRef); /* Not needed any more */
				}
				lastPlayerStamp=reggieStamp;
	        }
			if(reggieCount!=-1)
				lastCount=totalCount;
			return lastCount;
		}
		else
		{
			ReggieDispose(gReggieRef,true);
			gReggieConnected=false;
		}
	}
	else
		TrackerConnect();
	return 0;
}

void NetworkSearchGames(int *updated,tGameListEntry *games,int *numGames,int maxGames,tGameListPlayerEntry *players,int *numPlayers,int maxPlayers)
{
	static UInt32 lastGameStamp=-1,lastPlayerStamp=-1;
	static float lastIdleAdd;
	*updated=false;
	
	if(gReggieConnected)
	{
		UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL);
		if(!(state==kReggieStateRefused||state==kReggieStateOffline))
		{
			if(!gReggieSearching)
			{
				NetworkSearchGamesInit();
				lastGameStamp=-1;
				lastPlayerStamp=-1;
				gReportedIdle=0;
			}
			float idleAdd=TimeGetSeconds()-gStartIdleTime;	
			if(idleAdd>lastIdleAdd+15)
				AdvertisePlayer();
				
			lastIdleAdd=idleAdd;
			int reportIdle=(idleAdd>300);
			
			if(reportIdle!=gReportedIdle)
				AdvertisePlayer();
	        SInt32 reggieCount,totalCount;
			UInt32 reggieStamp;
	        Bool8 outdated;
	        reggieStamp=ReggieSearchStatus(gReggieRef,kReggieSearchGameChannel,&reggieCount,&totalCount,&outdated);
			if(reggieStamp!=lastGameStamp)
			{
				if(reggieCount==totalCount||reggieCount>*numGames)
//				if(reggieCount>-1)
				{
					int oldNumGames=*numGames;
					*numGames=0;
					lastGameStamp=reggieStamp;
					*updated=true;
					NetworkAddress local;
					DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local);
				
					int index=0;
					for(;index<reggieCount;index++) 
					{
						HashRef hashRef = ReggieSearchIndex(gReggieRef, kReggieSearchGameChannel ,index);
						if(hashRef&&*numGames<maxGames) 
						{
							hashRef=HashClone(hashRef);
							if(HashLookup(hashRef, "$UNIQUEID", nil))
							{
								Char8 *hashLookupResult;
								
								strcpy(games[*numGames].host,"127.0.0.1");	
								strcpy(games[*numGames].alias,"");	
								strcpy(games[*numGames].map,"");
								strcpy(games[*numGames].name,"");
								games[*numGames].numPlayers=0;
								games[*numGames].locked=false;
								games[*numGames].numNetPlayers=0;
								games[*numGames].arcade=false;
								games[*numGames].password=false;
								games[*numGames].loaded=true;
								games[*numGames].mapRef=kFileErr;
								games[*numGames].onlyRegistered=false;

								NetworkAddress addy;
								ReggieNetworkType type;
								ReggieSearchAddress(gReggieRef,kReggieSearchGameChannel,hashRef,&addy,&type);
								ReggiePrejoinType prejoin = ReggieProbePrejoin(gReggieRef,false,&local,&addy);
								if(prejoin==kReggiePrejoinSorry)
									games[*numGames].joinState=kJoinStateFail;
								else if(prejoin!=kReggiePrejoinNormal)
									games[*numGames].joinState=kJoinStateReggie;
								else 
									games[*numGames].joinState=kJoinStateOK;
								AddressTranscribe(&addy,games[*numGames].host);
									
								
								if(hashLookupResult=HashLookup(hashRef, "$ALIASES", nil))
								{
									char aliases[256];
									strcpy(aliases,hashLookupResult);
									if(strchr(aliases,','))	
										*strchr(aliases,',')='\0';
									strcpy(games[*numGames].alias,aliases);
								}												
						
								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$MAP", nil))
									strcpy(games[*numGames].map,hashLookupResult);

								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$MAPFILE",nil))
									games[*numGames].mapRef=FileGetReference(hashLookupResult);
									
								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$NUMPLAYERS", nil))
									sscanf(hashLookupResult,"%d",&games[*numGames].numPlayers);
									
								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$NUMNETPLAYERS", nil))
									sscanf(hashLookupResult,"%d",&games[*numGames].numNetPlayers);

								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$MAXPLAYERS", nil))
									sscanf(hashLookupResult,"%d",&games[*numGames].maxPlayers);

								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$ARCADE", nil))
									sscanf(hashLookupResult,"%d",&games[*numGames].arcade);
									
								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$PASSWORD", nil))
									sscanf(hashLookupResult,"%d",&games[*numGames].password);
								
								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$LOCKED", nil))
									sscanf(hashLookupResult,"%d",&games[*numGames].locked);

								if(hashLookupResult=HashLookup(hashRef, "$REDLINE$ONLYREGISTERED", nil))
									sscanf(hashLookupResult,"%d",&games[*numGames].onlyRegistered);

								if(hashLookupResult=HashLookup(hashRef, "$MESSAGE", nil))	
									strcpy(games[*numGames].name,hashLookupResult);
								
								for(int i=0;i<games[*numGames].numPlayers;i++)
								{
									strcpy(games[*numGames].players[i].name,"");
									strcpy(games[*numGames].players[i].car,"");
									strcpy(games[*numGames].players[i].location,"");

									char hashString[32];
									sprintf(hashString,"$REDLINE$PLAYER%dNAME",i);
									if(hashLookupResult=HashLookup(hashRef,hashString,nil))
										strcpy(games[*numGames].players[i].name,hashLookupResult);
										
									sprintf(hashString,"$REDLINE$PLAYER%dCAR",i);
									if(hashLookupResult=HashLookup(hashRef,hashString,nil))
										strcpy(games[*numGames].players[i].car,hashLookupResult);	      					      			

									sprintf(hashString,"$LOCATION$PLAYER%d",i);
									if(hashLookupResult=HashLookup(hashRef,hashString,nil))
										strcpy(games[*numGames].players[i].location,hashLookupResult);
									else
									{
										sprintf(hashString,"$DOMAIN$PLAYER%d",i);
										if(hashLookupResult=HashLookup(hashRef,hashString,nil))
											strcpy(games[*numGames].players[i].location,hashLookupResult);
										else
										{
											sprintf(hashString,"$HOSTNAMEPLAYER%d",i);
											if(hashLookupResult=HashLookup(hashRef,hashString,nil))
												strcpy(games[*numGames].players[i].location,hashLookupResult);
										}
									}
									if(i>0)
										if(strcmp(games[*numGames].players[i].location,"Reserved Location")==0)
											strcpy(games[*numGames].players[i].location,games[*numGames].players[0].location);
									if(char *loc=strrchr(games[*numGames].players[i].location,','))
										memmove(games[*numGames].players[i].location,loc+1,strlen(loc)+1);
								}

								

								(*numGames)++;
							}
							HashDispose(hashRef);
						}
					}
					for(;index<totalCount;index++)
						if(*numGames<maxGames)
						{
							if(oldNumGames<*numGames)		        	
								games[*numGames].loaded=false;						
/*							games[*numGames].loaded=true;
							strcpy(games[*numGames].name,"Test");						
							strcpy(games[*numGames].host,"127.0.0.1");	
							strcpy(games[*numGames].map,"");
							strcpy(games[*numGames].players[0].name,"kalle");
							strcpy(games[*numGames].players[0].location,"unna");
							games[*numGames].numPlayers=1;
							games[*numGames].locked=false;
							games[*numGames].numNetPlayers=0;
							games[*numGames].arcade=false;
							games[*numGames].password=false;
							games[*numGames].loaded=true;
							games[*numGames].mapRef=kFileErr;*/
							(*numGames)++;
						}
		        }
		        if(outdated&&reggieCount==totalCount)
		        	QueryGameSearch();
	        }
			
			reggieStamp=ReggieSearchStatus(gReggieRef,kReggieSearchPlayersChannel,&reggieCount,&totalCount,&outdated);
			if(reggieStamp!=lastPlayerStamp)
			{
				if(reggieCount==totalCount||reggieCount>*numPlayers)
				//if(reggieCount>-1)
				{
					int oldNumPlayers=*numPlayers;
					*numPlayers=0;
					lastPlayerStamp=reggieStamp;
					*updated=true;
					int index=0;
					for(;index<reggieCount;index++) 
					{
						HashRef hashRef = ReggieSearchIndex(gReggieRef, kReggieSearchPlayersChannel ,index);
						if(hashRef&&*numPlayers<maxPlayers) 
						if(HashLookup(hashRef, "$UNIQUEID", nil))
						{
							Char8 *hashLookupResult;
							
							strcpy(players[*numPlayers].location,"");
							strcpy(players[*numPlayers].name,"");
							players[*numPlayers].startIdleTime=-1;
							
							if(hashLookupResult=HashLookup(hashRef, "$NAME", nil))
								strcpy(players[*numPlayers].name,hashLookupResult);

							if(hashLookupResult=HashLookup(hashRef, "$LOCATION", nil))
								strcpy(players[*numPlayers].location,hashLookupResult);
							if(char *loc=strrchr(players[*numPlayers].location,','))
								memmove(players[*numPlayers].location,loc+1,strlen(loc)+1);
							
							if(hashLookupResult=HashLookup(hashRef, "$TIME$IDLE", nil))
								sscanf(hashLookupResult,"%d",&players[*numPlayers].startIdleTime);
							(*numPlayers)++;
						}
					}
					for(;index<totalCount;index++)
						if(*numPlayers<maxPlayers)
						{
							if(oldNumPlayers<*numPlayers)		        	
							{
								strcpy(players[*numPlayers].location,"");
								strcpy(players[*numPlayers].name,"");	
								players[*numPlayers].startIdleTime=-1;					
								//strcpy(players[*numPlayers].location,"area 51");
								//sprintf(players[*numPlayers].name,"joe %d",*numPlayers);						
							}
							(*numPlayers)++;
						}
					//printf("%d,%d=>%d\n",reggieCount,totalCount,*numPlayers);
		        }
		        if(outdated&&reggieCount==totalCount)
		        	QueryPlayerSearch();
	        }
		}
		else
		{
			NetworkSearchGamesStop();
			ReggieDispose(gReggieRef,true);
			gReggieConnected=false;
		}
	}
	else
	{
		*numGames=0;
		TrackerConnect();
	}
}

void NetworkSearchGamesStop()
{
	if(gReggieConnected)
	{
		//ReggieMessageBlock(gReggieRef,NULL,true);
		ReggieChannelClose(gReggieRef,kReggieAdvGameChannel);
		ReggieChannelClose(gReggieRef, kReggieSearchGameChannel);
		ReggieChannelClose(gReggieRef, kReggieSearchPlayersChannel);
	}
	gReggieSearching=false;
}

void NetworkCountPlayersStop()
{
	if(gReggieConnected)
	{
		ReggieChannelClose(gReggieRef, kReggieSearchPlayersChannel);
	}
	gReggieCounting=false;
}


int gWaitingForRegistry=false;
int gWaitingForRecord=false;

void TrackerFetchRecord(tFileRef track,tFileRef car,int arcade,int reverse)
{
	if(gConfig->registerLapTimes)
	{
		if(!gReggieConnected)
			if(!TrackerConnect())
			{
				TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Unable to Connect lap time server");		
				return;
			}

		char hashString[512];
		HashRef hashRef;				
		tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(track,kParserTypeMapInfoDesc,sizeof(tMapInfo));
		if(!mapInfo->builtIn)return;
		tCarDefinition *carDef=(tCarDefinition*)FileGetParsedDataPtr(car,kParserTypeCarDesc,sizeof(tCarDefinition));
		if(!carDef->builtIn)return;
				
		sprintf(hashString,"$REDLINE$MODE='%d' $REDLINE$REVERSE='%d'",arcade,reverse);		

		hashRef = HashInitialize(kHashFlagReggieKeys, hashString);
		HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name);
		HashAppend(hashRef, "$REDLINE$CAR", carDef->carName);
		HashAppend(hashRef, "SORT", ">$REDLINE$TIME,>$TIME");
		HashAppend(hashRef, "LIMIT", "1");

		//printf("hash:%s\n",(char*)(*HashSave(hashRef)));
		ReggieSearchBegin(gReggieRef,kReggieRecordChannel,kReggieRecordHighScore,0,hashRef); 
		HashDispose(hashRef);			
		gWaitingForRecord=true;

	}
}

void TrackerRegisterLapTime(tFileRef track,tFileRef car,int time,int arcade,int reverse)
{
	if(gConfig->registerLapTimes)
	{
		if(!gReggieConnected)
			if(!TrackerConnect())
			{
				TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Unable to Connect to lap time server");		
				return;
			}
		char hashString[512];
		HashRef hashRef;				
		tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(track,kParserTypeMapInfoDesc,sizeof(tMapInfo));
		if(!mapInfo->builtIn)return;
		tCarDefinition *carDef=(tCarDefinition*)FileGetParsedDataPtr(car,kParserTypeCarDesc,sizeof(tCarDefinition));
		if(!carDef->builtIn)return;
				
		sprintf(hashString,"$REDLINE$TIME='%d' $REDLINE$MODE='%d' $REDLINE$REVERSE='%d' $REDLINE$MAPCHECK='%8X' $REDLINE$CARCHECK='%8X'",time,arcade,reverse,FileGetChecksum(track),FileGetChecksum(car));		
		hashRef = HashInitialize(kHashFlagReggieKeys, hashString);
		HashAppend(hashRef, "$REDLINE$MAPFILE", FileGetName(track));
		HashAppend(hashRef, "$REDLINE$CARFILE", FileGetName(car));
		HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name);
		HashAppend(hashRef, "$REDLINE$CAR", carDef->carName);
		HashAppend(hashRef, "$REDLINE$NAME", gConfig->playerName);

		ReggieInsertBegin(gReggieRef, kReggieInsertRecordChannel, kReggieRecordHighScore ,hashRef);
		HashDispose(hashRef);

		sprintf(hashString,"$REDLINE$TIME<='%d' $REDLINE$TIME!='%d' $REDLINE$MODE='%d' $REDLINE$REVERSE='%d' COUNT",time,time,arcade,reverse);		
		hashRef = HashInitialize(kHashFlagReggieKeys, hashString);
		HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name);
		HashAppend(hashRef, "$REDLINE$CAR", carDef->carName);
		ReggieSearchBegin(gReggieRef,kReggieRegistryChannel,kReggieRecordHighScore,kReggieSearchFlagCount,hashRef); 
		HashDispose(hashRef);
		gWaitingForRegistry=true;
	}
}

void TrackerLapTimeRegistryClose()
{
	if(gWaitingForRegistry||gWaitingForRecord)
	{
		ReggieChannelClose(gReggieRef,kReggieInsertRecordChannel);
		ReggieChannelClose(gReggieRef,kReggieRegistryChannel);
		ReggieChannelClose(gReggieRef,kReggieRecordChannel);
		gWaitingForRegistry=gWaitingForRecord=false;
	}
}

void TrackerWaitForLapTimeRegistry()
{
	if(gWaitingForRegistry||gWaitingForRecord)
	{
		if(gWaitingForRegistry)
			TextPrintfToBufferFormated(Vector(0,-0.2),0.05,kTextAlignMiddle,"Registering Lap Time");

		UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL);
		if(state==kReggieStateRefused||state==kReggieStateOffline)
		{
			if(state==kReggieStateRefused)
				TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Connection Refused by lap time server");
			else
				TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"No response from lap time server");
			gWaitingForRegistry=false;
			gWaitingForRecord=false;
			gReggieConnected=false;
			ReggieDispose(gReggieRef,false);
		}
		else
		{
			if(gWaitingForRegistry)
			{
		        SInt32 reggieCount,totalCount;
		        ReggieSearchStatus(gReggieRef,kReggieRegistryChannel,&reggieCount,&totalCount,NULL);		
		        if(reggieCount!=-1)
				{
					if(totalCount==0)
						TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.1,kTextAlignMiddle,1,2,"World Record!!!");
					else if(totalCount<=25)	
						TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.1,kTextAlignMiddle,1,2,"Place %d on World List",totalCount+1);
					gWaitingForRegistry=false;
					ReggieChannelClose(gReggieRef,kReggieRegistryChannel);
					TrackerFetchRecord(gGameInfo->map,gGameInfo->playerCars[0],gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse);
				}    
			} 
			if(gWaitingForRecord)
			{
		        SInt32 reggieCount,totalCount;
		        ReggieSearchStatus(gReggieRef,kReggieRecordChannel,&reggieCount,&totalCount,NULL);		
		        if(reggieCount!=-1)
				{
					if(totalCount==0)
					{
						gWorldRecord=0;
						gWaitingForRecord=false;
					}
					else if(reggieCount>0){
						HashRef hashRef = ReggieSearchIndex(gReggieRef,kReggieRecordChannel,0);
						//printf("$REDLINE$TIME%s\n",HashLookup(hashRef,"$REDLINE$TIME",nil));
						//printf("$REDLINE$NAME%s\n",HashLookup(hashRef,"$REDLINE$NAME",nil));
						//printf("hash:%s\n",(char*)(*HashSave(hashRef)));
		      			sscanf(HashLookup(hashRef,"$REDLINE$TIME",nil),"%d",&gWorldRecord);
		      			strcpy(gRecordName,HashLookup(hashRef,"$REDLINE$NAME",nil));
						
						int numChars=0,index=0;
						while(numChars<12)
						{
							if(gRecordName[index]=='\255')
								while(gRecordName[++index]!='\255');
							index++;
							numChars++;
						}
						gRecordName[index]='\0';
						gWaitingForRecord=false;
						ReggieChannelClose(gReggieRef,kReggieRecordChannel);

					}	
				}    
			}
		}
	}
}