/* Copyright (C) 2006 Charlie C
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
*    claim that you wrote the original software. If you use this software
*    in a product, an acknowledgment in the product documentation would be
*    appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
*    misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include <sstream>
#include "bDNA.h"
#include "bBlenderFile.h"
#include "btBulletFile.h"
#include "LinearMath/btSerializer.h"
#include "bCommon.h"
#include <map>
#include <vector>
#include <string.h>

bool isBulletFile = true;

using namespace bParse;
typedef std::string bString;

///////////////////////////////////////////////////////////////////////////////
typedef std::map<bString, bString> bStringMap;
typedef std::vector<class bVariable> bVariableList;
typedef std::vector<bString> bStringList;

///////////////////////////////////////////////////////////////////////////////
static FILE *dump = 0;
static bDNA *mDNA = 0;
static bStringMap mStructs;

///////////////////////////////////////////////////////////////////////////////
class bVariable
{
public:
	bVariable();
	~bVariable();

	bString dataType;
	bString variableName;

	bString functionName;
	bString classCtor;

	bString memberVariable;
	bString memberDataType;
	bString functionArgs;

	void initialize(bString dataType, bString variable, bStringMap refDataTable);

	bool isPtr;
	bool isFunctionPtr;
	bool isPtrToPtr;
	bool isArray;
	bool isCharArray;
	bool isListBase;
	bool isPadding;
	bool isCommentedOut;
	bool isGeneratedType;
	bool isbString;
};

///////////////////////////////////////////////////////////////////////////////
bool dataTypeStandard(bString dataType)
{
	if (dataType == "char")
		return true;
	if (dataType == "short")
		return true;
	if (dataType == "int")
		return true;
	if (dataType == "long")
		return true;
	if (dataType == "float")
		return true;
	if (dataType == "double")
		return true;
	if (dataType == "void")
		return true;
	if (dataType == "btScalar")
		return true;
	return false;
}

///////////////////////////////////////////////////////////////////////////////
void writeTemplate(short *structData)
{
	bString type = mDNA->getType(structData[0]);
	bString className = type;
	bString prefix = isBulletFile ? "bullet_" : "blender_";

	int thisLen = structData[1];
	structData += 2;

	bString fileName = prefix + type;

	bVariableList dataTypes;
	bStringMap includeFiles;

	for (int dataVal = 0; dataVal < thisLen; dataVal++, structData += 2)
	{
		bString dataType = mDNA->getType(structData[0]);
		bString dataName = mDNA->getName(structData[1]);
		{
			bString newDataType = "";
			bString newDataName = "";

			bStringMap::iterator addB = mStructs.find(dataType);
			if (addB != mStructs.end())
			{
				newDataType = addB->second;
				newDataName = dataName;
			}

			else
			{
				if (dataTypeStandard(dataType))
				{
					newDataType = dataType;
					newDataName = dataName;
				}
				else
				{
					// Unresolved
					// set it to an empty struct
					// if it's not a ptr generate an error
					newDataType = "bInvalidHandle";
					newDataName = dataName;

					if (dataName[0] != '*')
					{
					}
				}
			}

			if (!newDataType.empty() && !newDataName.empty())
			{
				bVariable var = bVariable();
				var.initialize(newDataType, newDataName, mStructs);
				dataTypes.push_back(var);
			}
		}

		bStringMap::iterator include = mStructs.find(dataType);
		if (include != mStructs.end())
		{
			if (dataName[0] != '*')
			{
				if (includeFiles.find(dataType) == includeFiles.end())
				{
					includeFiles[dataType] = prefix + dataType;
				}
			}
		}
	}

	fprintf(dump, "###############################################################\n");
	fprintf(dump, "%s = bStructClass()\n", fileName.c_str());
	fprintf(dump, "%s.name = '%s'\n", fileName.c_str(), className.c_str());
	fprintf(dump, "%s.filename = '%s'\n", fileName.c_str(), fileName.c_str());

	bVariableList::iterator vars = dataTypes.begin();
	while (vars != dataTypes.end())
	{
		fprintf(dump, "%s.dataTypes.append('%s %s')\n", fileName.c_str(), vars->dataType.c_str(), vars->variableName.c_str());
		vars++;
	}

	bStringMap::iterator inc = includeFiles.begin();
	while (inc != includeFiles.end())
	{
		fprintf(dump, "%s.includes.append('%s.h')\n", fileName.c_str(), inc->second.c_str());
		inc++;
	}
	fprintf(dump, "DataTypeList.append(%s)\n", fileName.c_str());
}

///////////////////////////////////////////////////////////////////////////////
char data[] = {
	"\n"
	"class bStructClass:\n"
	"    def __init__(self):\n"
	"       self.name = \"\";\n"
	"       self.filename = \"\";\n"
	"       self.includes = []\n"
	"       self.dataTypes = []\n"
	"\n\n"
	"DataTypeList = []\n"};

///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
	using namespace bParse;
	dump = fopen("dump.py", "w");

	if (!dump) return 0;
	fprintf(dump, "%s\n", data);

#if 0
	char* filename = "../../../../data/r2d2_multibody.bullet";
	
	if (argc==2)
		filename = argv[1];

	bString fileStr(filename);
	bString extension(".bullet");

	int index2 = fileStr.find(extension);
	if (index2>=0)
		isBulletFile=true;

	
	FILE* fp = fopen (filename,"rb");

	if (!fp)
	{
		printf("error: file not found %s\n",filename);
		exit(0);
	}

	char* memBuf = 0;
	int len = 0;

	long currentpos = ftell(fp); /* save current cursor position */
	long newpos;
	int bytesRead;

	fseek(fp, 0, SEEK_END); /* seek to end */
	newpos = ftell(fp); /* find position of end -- this is the length */
	fseek(fp, currentpos, SEEK_SET); /* restore previous cursor position */
	
	len = newpos;
	
	memBuf = (char*)malloc(len);
	bytesRead = fread(memBuf,len,1,fp);

	bool swap = false;

	
	if (isBulletFile)
	{
		btBulletFile f(memBuf,len);
		swap = (f.getFlags() & FD_ENDIAN_SWAP)!=0;
	} else
	{
		bBlenderFile	f(memBuf,len);
		swap = (f.getFlags() & FD_ENDIAN_SWAP)!=0;
	}
#else
	isBulletFile = true;
	bool swap = false;
	char *memBuf = sBulletDNAstr;
	int len = sBulletDNAlen;
#endif

	char *blenderData = memBuf;
	int sdnaPos = 0;
	int mDataStart = 12;

	char *tempBuffer = blenderData;
	for (int i = 0; i < len; i++)
	{
		// looking for the data's starting position
		// and the start of SDNA decls

		if (!mDataStart && strncmp(tempBuffer, "REND", 4) == 0)
			mDataStart = i;
		if (!sdnaPos && strncmp(tempBuffer, "SDNA", 4) == 0)
			sdnaPos = i;

		if (mDataStart && sdnaPos) break;
		tempBuffer++;
	}

	FILE *fpdna = fopen("dnaString.txt", "w");
	char buf[1024];

	for (int i = 0; i < len - sdnaPos; i++)
	{
		int dnaval = (memBuf + sdnaPos)[i];

		if ((i % 32) == 0)
		{
			sprintf(buf, "%d,\n", dnaval);
		}
		else
		{
			sprintf(buf, "%d,", dnaval);
		}

		fwrite(buf, strlen(buf), 1, fpdna);
	}

	fclose(fpdna);

	mDNA = new bDNA();
	//mDNA->initMemory();

	mDNA->init(memBuf + sdnaPos, len - sdnaPos, swap);

	for (int i = 0; i < mDNA->getNumStructs(); i++)
	{
		short *structData = mDNA->getStruct(i);
		bString type = mDNA->getType(structData[0]);

		bString className = type;
		mStructs[type] = className;
	}

	for (int i = 0; i < mDNA->getNumStructs(); i++)
	{
		short *structData = mDNA->getStruct(i);
		writeTemplate(structData);
	}

	delete mDNA;
	fclose(dump);
	return 0;
}
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
int _getArraySize(char *str)
{
	int a, mul = 1;
	char stri[100], *cp = 0;
	int len = (int)strlen(str);

	memcpy(stri, str, len + 1);
	for (a = 0; a < len; a++)
	{
		if (str[a] == '[')
			cp = &(stri[a + 1]);
		else if (str[a] == ']' && cp)
		{
			stri[a] = 0;
			mul *= atoi(cp);
		}
	}
	return mul;
}

///////////////////////////////////////////////////////////////////////////////
bVariable::bVariable()
	: dataType("invalid"),
	  variableName("invalid"),
	  functionName(""),
	  classCtor(""),
	  memberVariable(""),
	  memberDataType(""),
	  functionArgs(""),
	  isPtr(false),
	  isFunctionPtr(false),
	  isPtrToPtr(false),
	  isArray(false),
	  isCharArray(false),
	  isListBase(false),
	  isPadding(false),
	  isCommentedOut(false),
	  isGeneratedType(false),
	  isbString(false)
{
}

///////////////////////////////////////////////////////////////////////////////
bVariable::~bVariable()
{
	dataType.clear();
	variableName.clear();
}

///////////////////////////////////////////////////////////////////////////////
void bVariable::initialize(bString type, bString variable, bStringMap refDataTable)
{
	dataType = type;
	variableName = variable;

	if (variableName[0] == '*')
	{
		isPtr = true;
		if (variableName[1] == '*')
			isPtrToPtr = true;
	}
	if (variableName[0] == '(')
		if (variableName[1] == '*')
			isFunctionPtr = true;

	if (variableName[variableName.size() - 1] == ']')
	{
		isArray = true;
		if (type == "char")
			isCharArray = true;
	}

	if (type == "ListBase")
		isListBase = true;

	if (variableName[0] == 'p')
	{
		bString sub = variableName.substr(0, 3);
		if (sub == "pad")
			isPadding = true;
	}
	if (dataType[0] == '/' && dataType[1] == '/')
		isCommentedOut = true;

	if (refDataTable.find(dataType) != refDataTable.end())
		isGeneratedType = true;

	if (!isBulletFile)
	{
		// replace valid float arrays
		if (dataType == "float" && isArray)
		{
			int size = _getArraySize((char *)variableName.c_str());
			if (size == 3)
			{
				dataType = "vec3f";
				variableName = variableName.substr(0, variableName.find_first_of("["));
			}
			if (size == 4)
			{
				dataType = "vec4f";
				variableName = variableName.substr(0, variableName.find_first_of("["));
			}
		}
	}
	memberDataType = dataType;
	functionArgs = variableName;
}

// eof
