#include <stdio.h>
#include <string.h>
#include "fileio.h"
#include "parser.h"
#include "entities.h"
#include "gameinitexit.h"
#include "gamemem.h"
#include "config.h"
#include "textures.h"

void FailWithErrorString(char *string);

void WriteOutFileName(char *buffer,int *bufferPos,char *label,tFileRef fileID)
{
	char line[256];
	if(fileID!=-1)
		sprintf(line,"%s \"%s\"\n",label,FileGetName(fileID));
	else
		sprintf(line,"%s \"\"\n",label);
	int len=strlen(line);
	if(buffer)
		memcpy(buffer+*bufferPos,line,len);
	*bufferPos+=len;
}

void WriteOutVector3(char *buffer,int *bufferPos,char *label,tVector3 v)
{
	char line[256];
	sprintf(line,"%s {%f,%f,%f}\n",label,v.x,v.y,v.z);
	int len=strlen(line);
	if(buffer)
		memcpy(buffer+*bufferPos,line,len);
	*bufferPos+=len;
}

void WriteOutVector2(char *buffer,int *bufferPos,char *label,tVector2 v)
{
	char line[256];
	sprintf(line,"%s {%f,%f}\n",label,v.x,v.y);
	int len=strlen(line);
	if(buffer)
		memcpy(buffer+*bufferPos,line,len);
	*bufferPos+=len;
}

void WriteOutInt(char *buffer,int *bufferPos,char *label,int arrayPos)
{
	if(arrayPos)
	{
		char line[256];
		sprintf(line,"%s %d\n",label,arrayPos);
		int len=strlen(line);
		if(buffer)
			memcpy(buffer+*bufferPos,line,len);
		*bufferPos+=len;
	}
}

void WriteOutFloat(char *buffer,int *bufferPos,char *label,float x)
{
	if(x!=0.0)
	{
		char line[256];
		sprintf(line,"%s %f\n",label,x);
		int len=strlen(line);
		if(buffer)
			memcpy(buffer+*bufferPos,line,len);
		*bufferPos+=len;
	}
}

void WriteOutHex(char *buffer,int *bufferPos,char *label,int arrayPos)
{
	if(arrayPos)
	{
		char line[256];
		sprintf(line,"%s 0x%x\n",label,arrayPos);
		int len=strlen(line);
		if(buffer)
			memcpy(buffer+*bufferPos,line,len);
		*bufferPos+=len;
	}
}

void WriteOutString(char *buffer,int *bufferPos,char *label,char *str)
{
	if(str[0])
	{
		char line[256];
		sprintf(line,"%s \"%s\"\n",label,str);
		int len=strlen(line);
		if(buffer)
			memcpy(buffer+*bufferPos,line,len);
		*bufferPos+=len;
	}
}

void WriteOutArrayPos(char *buffer,int *bufferPos,int arrayPos)
{
	char line[256];
	sprintf(line,"# %d\n",arrayPos);
	int len=strlen(line);
	if(buffer)
		memcpy(buffer+*bufferPos,line,len);
	*bufferPos+=len;
}

void WriteOutArrayPos2(char *buffer,int *bufferPos,int arrayPos)
{
	char line[256];
	sprintf(line,"## %d\n",arrayPos);
	int len=strlen(line);
	if(buffer)
		memcpy(buffer+*bufferPos,line,len);
	*bufferPos+=len;
}

int WriteOutMapInfo(char *buffer,int *bufferPos,tFileRef fileID,tMapInfo *mapInfo)
{
	WriteOutFileName(buffer,bufferPos,"road",mapInfo->road);
	WriteOutFileName(buffer,bufferPos,"image",mapInfo->image);
	WriteOutVector3(buffer,bufferPos,"startPos",mapInfo->startPos);
	WriteOutVector3(buffer,bufferPos,"finishPos",mapInfo->finishPos);

	WriteOutVector2(buffer,bufferPos,"overviewTopLeft",mapInfo->overviewTopLeft);
	WriteOutVector2(buffer,bufferPos,"overviewBotRight",mapInfo->overviewBotRight);

	WriteOutInt(buffer,bufferPos,"loop",mapInfo->loop);
	WriteOutInt(buffer,bufferPos,"reverse",mapInfo->reverse);
	WriteOutInt(buffer,bufferPos,"useEnts",mapInfo->demoAvailable);
	WriteOutInt(buffer,bufferPos,"hasOverview",mapInfo->hasOverview);
	WriteOutInt(buffer,bufferPos,"hideMap",mapInfo->hideMap);
	WriteOutInt(buffer,bufferPos,"dontDrawRoad",mapInfo->dontDrawRoad);
	WriteOutInt(buffer,bufferPos,"builtIn",mapInfo->builtIn);
	WriteOutInt(buffer,bufferPos,"maxPlayers",mapInfo->maxPlayers);
	WriteOutInt(buffer,bufferPos,"playerPos",mapInfo->playerPos);
	WriteOutInt(buffer,bufferPos,"dirtEnable",mapInfo->dirtEnable);
	WriteOutInt(buffer,bufferPos,"baseSurfaceType",mapInfo->baseSurfaceType);
	WriteOutFloat(buffer,bufferPos,"startLineOffset",mapInfo->startLineOffset);
	WriteOutFloat(buffer,bufferPos,"startCenterOffset",mapInfo->startCenterOffset);
	WriteOutFloat(buffer,bufferPos,"carOffset",mapInfo->carOffset);
	WriteOutFloat(buffer,bufferPos,"speedFactor",mapInfo->speedFactor);
	WriteOutFloat(buffer,bufferPos,"dirtIntensity",mapInfo->dirtIntensity);

	WriteOutFileName(buffer,bufferPos,"roadTypes",mapInfo->roadTypes);
	WriteOutFileName(buffer,bufferPos,"overview",mapInfo->overview);

	WriteOutString(buffer,bufferPos,"name",mapInfo->name);
	WriteOutFileName(buffer,bufferPos,"dirtMap",mapInfo->dirtMap);
	WriteOutInt(buffer,bufferPos,"numObjs",mapInfo->numObjs);
	for(int arrayPos=0;arrayPos<mapInfo->numObjs;arrayPos++)
	{
		WriteOutArrayPos(buffer,bufferPos,arrayPos);
		WriteOutFileName(buffer,bufferPos,"obj.model",mapInfo->obj[arrayPos].model);
		WriteOutVector3(buffer,bufferPos,"obj.pos",mapInfo->obj[arrayPos].pos);
		WriteOutVector3(buffer,bufferPos,"obj.dir",mapInfo->obj[arrayPos].dir);
		WriteOutInt(buffer,bufferPos,"obj.color",mapInfo->obj[arrayPos].color);
		WriteOutInt(buffer,bufferPos,"obj.untouchable",mapInfo->obj[arrayPos].untouchable);
		WriteOutHex(buffer,bufferPos,"obj.envFlags",mapInfo->obj[arrayPos].envFlags);
	}
	WriteOutInt(buffer,bufferPos,"numVisWalls",mapInfo->numVisWalls);
	for(int arrayPos=0;arrayPos<mapInfo->numVisWalls;arrayPos++)
	{
		WriteOutArrayPos(buffer,bufferPos,arrayPos);
		WriteOutVector3(buffer,bufferPos,"a",mapInfo->visWalls[arrayPos].a);
		WriteOutVector3(buffer,bufferPos,"b",mapInfo->visWalls[arrayPos].b);
		WriteOutVector3(buffer,bufferPos,"c",mapInfo->visWalls[arrayPos].c);
	}
	WriteOutInt(buffer,bufferPos,"numMapEnvs",mapInfo->numMapEnvs);
	for(int arrayPos=0;arrayPos<mapInfo->numMapEnvs;arrayPos++)
	{
		WriteOutArrayPos(buffer,bufferPos,arrayPos);	
		WriteOutHex(buffer,bufferPos,"mapEnv.envFlags",mapInfo->mapEnv[arrayPos].envFlags);
		WriteOutInt(buffer,bufferPos,"mapEnv.lightEnable",mapInfo->mapEnv[arrayPos].lightEnable);
		WriteOutFloat(buffer,bufferPos,"mapEnv.fogBegin",mapInfo->mapEnv[arrayPos].fogBegin);
		WriteOutFloat(buffer,bufferPos,"mapEnv.fogEnd",mapInfo->mapEnv[arrayPos].fogEnd);
		WriteOutVector3(buffer,bufferPos,"mapEnv.fogColor",mapInfo->mapEnv[arrayPos].fogColor);
		WriteOutFileName(buffer,bufferPos,"mapEnv.sky0",mapInfo->mapEnv[arrayPos].sky0);
		WriteOutFileName(buffer,bufferPos,"mapEnv.sky90",mapInfo->mapEnv[arrayPos].sky90);
		WriteOutFileName(buffer,bufferPos,"mapEnv.sky180",mapInfo->mapEnv[arrayPos].sky180);
		WriteOutFileName(buffer,bufferPos,"mapEnv.sky270",mapInfo->mapEnv[arrayPos].sky270);
		WriteOutFileName(buffer,bufferPos,"mapEnv.skytop",mapInfo->mapEnv[arrayPos].skytop);
		WriteOutFileName(buffer,bufferPos,"mapEnv.skybot",mapInfo->mapEnv[arrayPos].skybot);
		WriteOutFileName(buffer,bufferPos,"mapEnv.lightMap",mapInfo->mapEnv[arrayPos].lightMap);
		WriteOutFileName(buffer,bufferPos,"mapEnv.spheremap",mapInfo->mapEnv[arrayPos].spheremap);
		WriteOutVector2(buffer,bufferPos,"mapEnv.lightMapTopLeft",mapInfo->mapEnv[arrayPos].lightMapTopLeft);
		WriteOutVector2(buffer,bufferPos,"mapEnv.lightMapBotRight",mapInfo->mapEnv[arrayPos].lightMapBotRight);
		WriteOutVector3(buffer,bufferPos,"mapEnv.lightDir",mapInfo->mapEnv[arrayPos].lightDir);
		WriteOutVector3(buffer,bufferPos,"mapEnv.flareDir",mapInfo->mapEnv[arrayPos].flareDir);
	}
}

int WriteOutConfig(char *buffer,int *bufferPos,tFileRef fileID,tSysConfig *config)
{
	WriteOutInt(buffer,bufferPos,"screenXSize",config->screenXSize);
	WriteOutInt(buffer,bufferPos,"screenYSize",config->screenYSize);
	WriteOutInt(buffer,bufferPos,"windowX",config->windowX);
	WriteOutInt(buffer,bufferPos,"windowY",config->windowY);
	WriteOutInt(buffer,bufferPos,"allCams",config->allCams);
	WriteOutInt(buffer,bufferPos,"useBetaBuilds",config->useBetaBuilds);
	WriteOutInt(buffer,bufferPos,"noGhost",config->noGhost);
	WriteOutInt(buffer,bufferPos,"onlyRegisteredPlayers",config->onlyRegisteredPlayers);
	WriteOutInt(buffer,bufferPos,"cantGoBackwards",config->cantGoBackwards);
	WriteOutInt(buffer,bufferPos,"fullscreen",config->fullscreen);
	WriteOutInt(buffer,bufferPos,"performanceStats",config->performanceStats);
	WriteOutInt(buffer,bufferPos,"stencil",config->stencil);
	WriteOutInt(buffer,bufferPos,"textureQuality",config->textureQuality);
	WriteOutInt(buffer,bufferPos,"textureFilter",config->textureFilter);
	WriteOutInt(buffer,bufferPos,"soundEnable",config->soundEnable);
	WriteOutInt(buffer,bufferPos,"maxCarSources",config->maxCarSources);
	WriteOutInt(buffer,bufferPos,"fsaa",config->fsaa);
	WriteOutInt(buffer,bufferPos,"dbIndex",config->dbIndex);
	WriteOutInt(buffer,bufferPos,"arcade",config->arcade);
	WriteOutInt(buffer,bufferPos,"reverse",config->reverse);
	WriteOutInt(buffer,bufferPos,"ffb",config->ffb);
	WriteOutInt(buffer,bufferPos,"color32Bit",config->color32Bit);
	WriteOutInt(buffer,bufferPos,"metricUnits",config->metricUnits);
	WriteOutInt(buffer,bufferPos,"interiorDisplay",config->interiorDisplay);
	WriteOutInt(buffer,bufferPos,"cameraMode",config->cameraMode);
	WriteOutInt(buffer,bufferPos,"guideSigns",config->guideSigns);
	WriteOutInt(buffer,bufferPos,"carsOnSpeed",config->carsOnSpeed);
	WriteOutInt(buffer,bufferPos,"demolition",config->demolition);
	WriteOutInt(buffer,bufferPos,"trackerEnable",config->trackerEnable);
	WriteOutInt(buffer,bufferPos,"maxPlayers",config->maxPlayers);
	WriteOutInt(buffer,bufferPos,"registerLapTimes",config->registerLapTimes);
	WriteOutInt(buffer,bufferPos,"showPlayerNames",config->showPlayerNames);
	WriteOutInt(buffer,bufferPos,"motionBlur",config->motionBlur);
	WriteOutInt(buffer,bufferPos,"showReplays",config->showReplays);
	WriteOutInt(buffer,bufferPos,"allowHugeGames",config->allowHugeGames);
	WriteOutInt(buffer,bufferPos,"interfaceSounds",config->interfaceSounds);
	WriteOutHex(buffer,bufferPos,"challengeData",config->challengeData);
	WriteOutInt(buffer,bufferPos,"seperateGasBrake",config->seperateGasBrake);
	WriteOutInt(buffer,bufferPos,"disableAnalogueTCS",config->disableAnalogueTCS);
	WriteOutInt(buffer,bufferPos,"reverseGas",config->reverseGas);

	WriteOutString(buffer,bufferPos,"playerName",config->playerName);
	WriteOutString(buffer,bufferPos,"gameName",config->gameName);
	WriteOutString(buffer,bufferPos,"password",config->password);
	WriteOutString(buffer,bufferPos,"confirmedVersion",config->confirmedVersion);

	WriteOutFloat(buffer,bufferPos,"gfxDynamics",config->gfxDynamics);
	WriteOutFloat(buffer,bufferPos,"soundVolume",config->soundVolume);
	WriteOutFloat(buffer,bufferPos,"musicVolume",config->musicVolume);
	WriteOutFloat(buffer,bufferPos,"hudTransparency",config->hudTransparency);
	WriteOutFloat(buffer,bufferPos,"ffbIntensity",config->ffbIntensity);

	WriteOutFileName(buffer,bufferPos,"lastCar",config->lastCar);
	WriteOutFileName(buffer,bufferPos,"lastEnemy",config->lastEnemy);
	WriteOutFileName(buffer,bufferPos,"lastRoad",config->lastRoad);
	WriteOutInt(buffer,bufferPos,"numEnemies",config->numEnemies);
	WriteOutInt(buffer,bufferPos,"automatic",config->automatic);
	WriteOutInt(buffer,bufferPos,"lastLaps",config->lastLaps);
	WriteOutInt(buffer,bufferPos,"lastColor",config->lastColor);
	WriteOutFileName(buffer,bufferPos,"lastEnv",config->lastEnv);

	WriteOutInt(buffer,bufferPos,"numKeys",config->numKeys);
	for(int arrayPos=0;arrayPos<config->numKeys;arrayPos++)
	{
		WriteOutArrayPos(buffer,bufferPos,arrayPos);
		WriteOutHex(buffer,bufferPos,"keyID",config->keys[arrayPos].keyID);
		WriteOutHex(buffer,bufferPos,"controllerID1",config->keys[arrayPos].controllerID1);
		WriteOutHex(buffer,bufferPos,"controllerID2",config->keys[arrayPos].controllerID2);
		WriteOutHex(buffer,bufferPos,"elementID",config->keys[arrayPos].elementID);
		WriteOutString(buffer,bufferPos,"identifier",config->keys[arrayPos].identifier);
		WriteOutString(buffer,bufferPos,"controllerIdentifier",config->keys[arrayPos].controllerIdentifier);
	}

	WriteOutInt(buffer,bufferPos,"numAxis",config->numAxis);
	for(int arrayPos=0;arrayPos<config->numAxis;arrayPos++)
	{
		WriteOutArrayPos(buffer,bufferPos,arrayPos);
		WriteOutHex(buffer,bufferPos,"axisControllerID1",config->axis[arrayPos].axisControllerID1);
		WriteOutHex(buffer,bufferPos,"axisControllerID2",config->axis[arrayPos].axisControllerID2);
		WriteOutHex(buffer,bufferPos,"axisElementID",config->axis[arrayPos].axisElementID);
		WriteOutString(buffer,bufferPos,"axisIdentifier",config->axis[arrayPos].axisIdentifier);
		WriteOutInt(buffer,bufferPos,"min",config->axis[arrayPos].min);
		WriteOutInt(buffer,bufferPos,"mid",config->axis[arrayPos].mid);
		WriteOutInt(buffer,bufferPos,"max",config->axis[arrayPos].max);
		WriteOutFloat(buffer,bufferPos,"deadzone",config->axis[arrayPos].deadzone);
	}
	WriteOutInt(buffer,bufferPos,"numTaunts",config->numTaunts);
	for(int arrayPos=0;arrayPos<config->numTaunts;arrayPos++)
	{
		WriteOutArrayPos(buffer,bufferPos,arrayPos);
		WriteOutString(buffer,bufferPos,"taunts",config->taunts[arrayPos]);
	}
	for(int arrayPos=0;arrayPos<11;arrayPos++)
	{
		WriteOutArrayPos(buffer,bufferPos,arrayPos);
		WriteOutFileName(buffer,bufferPos,"opponentCars",config->opponentCars[arrayPos]);
		WriteOutInt(buffer,bufferPos,"opponentColors",config->opponentColors[arrayPos]);
	}
	for(int arrayPos=0;arrayPos<32;arrayPos++)
		if(config->challengeRecords[arrayPos])
		{
			WriteOutArrayPos(buffer,bufferPos,arrayPos);
			WriteOutInt(buffer,bufferPos,"challengeRecords",config->challengeRecords[arrayPos]);
		}
		
	WriteOutInt(buffer,bufferPos,"numPersonalRecords",config->numPersonalRecords);
	for(int arrayPos=0;arrayPos<config->numPersonalRecords;arrayPos++)
	{
		WriteOutArrayPos(buffer,bufferPos,arrayPos);
		WriteOutFileName(buffer,bufferPos,"records.car",config->records[arrayPos].car);
		WriteOutFileName(buffer,bufferPos,"records.map",config->records[arrayPos].map);
		WriteOutInt(buffer,bufferPos,"records.mode",config->records[arrayPos].mode);
		WriteOutInt(buffer,bufferPos,"records.direction",config->records[arrayPos].direction);
		WriteOutHex(buffer,bufferPos,"records.time",config->records[arrayPos].time);
	}
}


int WriteOutFile(tFileRef fileID, void *dataBuffer,int fileType)
{
	char *buffer=NULL;
	int bufferPos=0;
	switch(fileType)
	{
		case kParserTypeMapInfoDesc:
			WriteOutMapInfo(buffer,&bufferPos,fileID,(tMapInfo*)dataBuffer);
			break;
		case kParserTypeConfigDesc:
			WriteOutConfig(buffer,&bufferPos,fileID,(tSysConfig*)dataBuffer);
			break;
	}
	buffer=(char*)MemoryAllocateBlock(bufferPos);
	bufferPos=0;
	switch(fileType)
	{
		case kParserTypeMapInfoDesc:
			WriteOutMapInfo(buffer,&bufferPos,fileID,(tMapInfo*)dataBuffer);
			break;
		case kParserTypeConfigDesc:
			WriteOutConfig(buffer,&bufferPos,fileID,(tSysConfig*)dataBuffer);
			break;
	}
	if(fileType==kParserTypeCareerDataDesc)
		CryptData(buffer,bufferPos);
	FileSetData(fileID,buffer);
}