//log.cpp
//log [network] physics packets in order to replay the games

#include <stdlib.h>
#include <unistd.h>
#include "networkphysics.h"
#include "gamemem.h"
#include "log.h"
#include "gameinitexit.h"
#include "gameframe.h"
#include "network.h"

typedef struct{
	int offset;
	short size;
	short type;
} tLogEntry;

#define kLogSize (20*1024*1024)
#define kGhostLogSize (256*1024)
#define kIndexSize (256*1024)

char* gPacketLog;
char *gGhostLog;
tLogEntry *gPacketLogIndex;
tLogEntry *gGhostIndex;
int gLogPos=0;
int gIndexPos=0;
int gGhostLogPos=0;
int gGhostIndexPos=0;

void LogInit()
{
	gPacketLog=(char*)malloc(kLogSize);
	gGhostLog=(char*)malloc(kGhostLogSize);
	gPacketLogIndex=(tLogEntry*)malloc(kIndexSize*sizeof(tLogEntry));
	gGhostIndex=(tLogEntry*)malloc(kIndexSize*sizeof(tLogEntry));
}

void GetLogs(tFileRef *logs,int *logCount)
{
	*logCount=0;
	for(int i=0;i<gFileTableSize;i++)
		if(char *extension=FileGetExtension(i))
			if(!_stricmp(extension,kFileTypeLog))
				if(*logCount<kMaxLogs)
					logs[(*logCount)++]=i;
}

void LogPacket(int size, void *data,int type)
{
	if(gIndexPos<kIndexSize)
		if(gLogPos+size<kLogSize)
		{
			gPacketLogIndex[gIndexPos].size=size;
			gPacketLogIndex[gIndexPos].offset=gLogPos;
			gPacketLogIndex[gIndexPos].type=type;
			gIndexPos++;
			MemoryMove(gPacketLog+gLogPos,data,size);
			gLogPos+=size;
		}
}

void LogToGhostLog()
{
//kLogSize>kGhostLogSize!!!!
	MemoryMove(gGhostLog,gPacketLog,gLogPos>kGhostLogSize?kGhostLogSize:gLogPos);
	MemoryMove(gGhostIndex,gPacketLogIndex,gIndexPos*sizeof(tLogEntry));
	gGhostLogPos=gLogPos;
	gGhostIndexPos=gIndexPos;
}

int LogCompare(const void *a,const void *b)
{	
	int aFrame=((tPhysicsMessage*)(gPacketLog+*(int*)a))->frame;
	int bFrame=((tPhysicsMessage*)(gPacketLog+*(int*)b))->frame;
	S32Swap(aFrame);
	S32Swap(bFrame);
	return aFrame-bFrame;
}

void LogSort()
{
	qsort(gPacketLogIndex,gIndexPos,sizeof(tLogEntry),LogCompare);
}

void LogReset()
{
	gLogPos=0;
	gIndexPos=0;
}

void GhostLogReset()
{
	gGhostLogPos=0;
	gGhostIndexPos=0;
}


int LogGetPacket(int index,int *type,int *size,int log,void *buffer)
{
	if(log==kLogReplayLog||gGhostLogPos==0){
		if(index<gIndexPos)
		{
			*type=gPacketLogIndex[index].type;
			*size=gPacketLogIndex[index].size;
			memcpy(buffer,gPacketLog+gPacketLogIndex[index].offset,*size);
			return true;
		}
	}
	else
		if(index<gGhostIndexPos)
			if(gGhostIndex[index].offset<kGhostLogSize)
			{
				*type=gGhostIndex[index].type;
				*size=gGhostIndex[index].size;
				memcpy(buffer,gGhostLog+gGhostIndex[index].offset,*size);
				return true;
			}
	return false;
}

void GameInfoSend(tGameInfo *gInfo);
void GameInfoReceive(tGameInfo *gInfo);
void ConvertGInfoSwap(tGameInfo *gInfo);

void LogSaveToDisk()
{
	char ct[255],name[255];
	time_t t;
	time(&t);
	ctime_r(&t,ct);
	ct[19]='\0';
	ct[13]='-';
	ct[16]='-';	
	sprintf(name,"%s.redlog",ct);

	CFBundleRef refMainBundle = CFBundleGetMainBundle(); 
	if (!refMainBundle)
		return;
		
	// create a URL to the app bundle
	CFURLRef refMainBundleURL = CFBundleCopyBundleURL (refMainBundle); 
	if(!refMainBundleURL)
		return;
		
	char path[512];
	if(!CFURLGetFileSystemRepresentation(refMainBundleURL,true,(UInt8*)path,512))
		return;
		
	sprintf(path,"%s/../",path);
	chdir(path);
	sprintf(path,"%s/Plug-Ins/",path);
	chdir(path);
	FILE* f=fopen(name,"w");
	if(!f)
		return;
		
	SysBeep(0);
	GameInfoSend(gGameInfo);
	fwrite(gGameInfo,sizeof(tGameInfo),1,f);
	if(gGameInfo->numLaps==-1)
	{
		int w=gGhostIndexPos;
		S32Swap(w);
		fwrite(&w,sizeof(int),1,f);
				
		w=gGhostLogPos;
		S32Swap(w);
		fwrite(&w,sizeof(int),1,f);
		
		w=gReplayOldFrameCount;
		S32Swap(w);
		fwrite(&w,sizeof(int),1,f);
		
		
		for(int i=0;i<gGhostIndexPos;i++)
		{	
			S32Swap(gGhostIndex[i].offset);
			S16Swap(gGhostIndex[i].type);
			S16Swap(gGhostIndex[i].size);
		}
		fwrite(gGhostIndex,sizeof(tLogEntry),gGhostIndexPos,f);
		for(int i=0;i<gGhostIndexPos;i++)
		{	
			S32Swap(gGhostIndex[i].offset);
			S16Swap(gGhostIndex[i].type);
			S16Swap(gGhostIndex[i].size);
		}
		
		for(int i=0;i<gGhostIndexPos;i++)
		{
			switch(gGhostIndex[i].type)
			{
				case kMessageTypePhysics:
					{
						tPhysicsMessage *physMessage=(tPhysicsMessage*)(gGhostLog+gGhostIndex[i].offset);
						physMessage->frame-=(gBestLapTime!=0?gBestLapStart+5:gCurrentLapStart+5);
					}
			}
		}
		fwrite(gGhostLog,sizeof(char),gGhostLogPos,f);
		for(int i=0;i<gGhostIndexPos;i++)
		{
			switch(gGhostIndex[i].type)
			{
				case kMessageTypePhysics:
					{
						tPhysicsMessage *physMessage=(tPhysicsMessage*)(gGhostLog+gGhostIndex[i].offset);
						physMessage->frame+=(gBestLapTime!=0?gBestLapStart+5:gCurrentLapStart+5);
					}
			}
		}
	}
	else
	{
		int w=gIndexPos;
		S32Swap(w);
		fwrite(&w,sizeof(int),1,f);
				
		w=gLogPos;
		S32Swap(w);
		fwrite(&w,sizeof(int),1,f);
		
		w=gReplayOldFrameCount;
		S32Swap(w);
		fwrite(&w,sizeof(int),1,f);

		for(int i=0;i<gIndexPos;i++)
		{	
			S32Swap(gPacketLogIndex[i].offset);
			S16Swap(gPacketLogIndex[i].type);
			S16Swap(gPacketLogIndex[i].size);
		}
		fwrite(gPacketLogIndex,sizeof(tLogEntry),gIndexPos,f);
		for(int i=0;i<gIndexPos;i++)
		{	
			S32Swap(gPacketLogIndex[i].offset);
			S16Swap(gPacketLogIndex[i].type);
			S16Swap(gPacketLogIndex[i].size);
		}

		fwrite(gPacketLog,sizeof(char),gLogPos,f);
	}
	fclose(f);
	
	FileRescanDirectory();
}

int LogLoad(tFileRef f,tGameInfo* gInfo)
{
	char *ch=(char*)FileGetDataPtr(f);
	
	*gInfo=*(tGameInfo*)ch;
	ConvertGInfoSwap(gInfo);
	GameInfoReceive(gInfo);
	ch+=sizeof(tGameInfo);//-sizeof(UInt32)*kMaxPlayers;

	for(int i=0;i<gInfo->numPlayers;i++)
		if(gInfo->playerCars[i]==-1)
			return false;
	if(gInfo->map==-1)
		return false;
	if(gInfo->environment==-1)
		return false;

	gIndexPos=*(int*)ch;
	S32Swap(gIndexPos);
	gGhostIndexPos=gIndexPos;
	ch+=sizeof(int);

	gLogPos=*(int*)ch;
	S32Swap(gLogPos);
	ch+=sizeof(int);

	gReplayOldFrameCount=*(int*)ch;
	S32Swap(gReplayOldFrameCount);
	ch+=sizeof(int);

	MemoryMove(gPacketLogIndex,ch,sizeof(tLogEntry)*gIndexPos);
	for(int i=0;i<gIndexPos;i++)
	{	
		S32Swap(gPacketLogIndex[i].offset);
		S16Swap(gPacketLogIndex[i].type);
		S16Swap(gPacketLogIndex[i].size);
	}
	ch+=sizeof(tLogEntry)*gIndexPos;

	MemoryMove(gPacketLog,ch,sizeof(char)*gLogPos);
	return true;
}
