#include "stdafx.h"
#include "XMLConverter.h"
#include "IRCLog.h"
#include "IXmlSerializer.h"
#include "XmlBinaryHeaders.h"
#include "XMLBinaryWriter.h"
#include "DbgHelp.h"

bool g_bNeedSwapEndian = false;

const string XMLConverter::sk_sInputExtensions[] = {
	"xml"
};

XMLConverter::XMLConverter(ICryXML* pCryXML)
: pCryXML(pCryXML), m_refCount(1)
{
	pCryXML->AddRef();
}

XMLConverter::~XMLConverter()
{
	pCryXML->Release();
}

void XMLConverter::Release()
{
	if (--m_refCount <= 0)
		delete this;
}

//////////////////////////////////////////////////////////////////////////
class CXmlBinaryDataWriterFile : public XMLBinary::IDataWriter
{
public:
	CXmlBinaryDataWriterFile( const char *file ) { m_file = fopen( file,"wb" ); }
	~CXmlBinaryDataWriterFile() { if (m_file) fclose( m_file ); };
	virtual bool IsOk() { return m_file != 0; };
	virtual void Write(const void* pData, size_t size) { if (m_file) fwrite( pData,size,1,m_file ); }
private:
	FILE *m_file;
};

bool XMLConverter::Process( ConvertContext &cc )
{
	if (cc.platform == PLATFORM_X360 || cc.platform == PLATFORM_PS3)
	{
		g_bNeedSwapEndian = true;
		cc.pLog->Log("Endian conversion specified");
	}

	// Get the files to process.
	string sOutputFile = cc.getOutputPath();
	string sInputFile = cc.getSourcePath();

	// Get the xml serializer.
	IXMLSerializer* pSerializer = pCryXML->GetXMLSerializer();

	// Read in the input file.
	XmlNodeRef root;
	{
		FILE* pSourceFile = fopen(sInputFile.c_str(), "rb");
		if (pSourceFile == 0)
		{
			cc.pLog->LogError("Cannot open file \"%s\": %s\n", sInputFile.c_str(), strerror(errno));
			return false;
		}
		fclose(pSourceFile);
		char szErrorBuffer[1024];
		root = pSerializer->Read(FileXmlBufferSource(sInputFile.c_str()), sizeof(szErrorBuffer), szErrorBuffer);
		if (!root)
		{
			cc.pLog->LogError("Cannot read file \"%s\": %s\n", sInputFile.c_str(), szErrorBuffer);
			return false;
		}
	}

	// Write out the destination file.
	{
		FILE* pDestinationFile = fopen(sOutputFile.c_str(), "wb");
		if (pDestinationFile == 0)
		{
			cc.pLog->LogError("Cannot open file \"%s\": %s\n", sInputFile.c_str(), strerror(errno));
			return false;
		}
		fclose(pDestinationFile);

		CXmlBinaryDataWriterFile outputFile(sOutputFile.c_str());
		XMLBinary::CXMLBinaryWriter xmlBinaryWriter;
		xmlBinaryWriter.WriteNode( &outputFile,root );
	}

#ifdef _DEBUG
	// Verify that the output file is not corrupt.
	{
		FILE* pSourceFile = fopen(sOutputFile.c_str(), "rb");
		if (pSourceFile == 0)
		{
			cc.pLog->LogError("Failed to write file \"%s\": %s\n", sInputFile.c_str(), strerror(errno));
			return false;
		}
		fclose(pSourceFile);

		char szErrorBuffer[1024];
		root = pSerializer->Read( FileXmlBufferSource(sOutputFile.c_str()),sizeof(szErrorBuffer), szErrorBuffer);
		if (!root)
		{
			cc.pLog->LogError("Cannot read file \"%s\": %s\n", sOutputFile.c_str(), szErrorBuffer);
			return false;
		}
	}

	// Write out a file for comparison.
	{
		CXmlBinaryDataWriterFile outputFile((sOutputFile + ".test").c_str());
		XMLBinary::CXMLBinaryWriter xmlBinaryWriter;
		xmlBinaryWriter.WriteNode( &outputFile,root );
	}
#endif //_DEBUG

	return true;
}

bool XMLConverter::GetOutputFile(ConvertContext &cc)
{
	string sExtension = PathHelpers::FindExtension(cc.sourceFileFinal);
	sExtension = "bin" + sExtension;
	cc.outputFile = PathHelpers::ReplaceExtension(cc.sourceFileFinal, sExtension);
	return true;
}

int XMLConverter::GetNumPlatforms() const
{
	return 3;
}

Platform XMLConverter::GetPlatform( int index ) const
{
	switch (index)
	{
	case 0:
		return PLATFORM_PC;
	case 1:
		return PLATFORM_X360;
	case 2:
		return PLATFORM_PS3;
	default:
		return PLATFORM_UNKNOWN;
	}
}

int XMLConverter::GetNumExt() const
{
	return sizeof(sk_sInputExtensions) / sizeof(sk_sInputExtensions[0]);
}

const char* XMLConverter::GetExt( int index ) const
{
	return sk_sInputExtensions[index].c_str();
}

DWORD XMLConverter::GetTimestamp() const
{
	return GetTimestampForLoadedLibrary(g_hInst);
}

//////////////////////////////////////////////////////////////////////////
ICompiler* XMLConverter::CreateCompiler()
{
	// Only ever return one compiler, since we don't support multithreading. Since
	// the compiler is just this object, we can tell whether we have already returned
	// a compiler by checking the ref count.
	if (m_refCount >= 2)
		return 0;

	// Until we support multithreading for this convertor, the compiler and the
	// convertor may as well just be the same object.
	++m_refCount;
	return this;
}

bool XMLConverter::SupportsMultithreading() const
{
	return false;
}
