///////////////////////////////////////////////////////////////////////////////
/// @file Shader.cpp
///
/// @brief Class for shaders
///
/// @author JesusFreak
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date May 2002
/// @author JesusFreak
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include <cstdlib>
#include "deShader_priv.hpp"
#include "deString.hpp"

IdeShader * IdeShader_Create()
{
	return new deShader;
}

deShader::deShader():
m_Instructions(0),
m_Constants(0)
{
	// Set our member variables
	m_ShaderType = SHADER_INVALID;
	m_CurrentLine = 1;
	m_InstructionCount = 0;
	m_ConstantCount = 0;
	m_DriverShader = NULL;
}

deShader::~deShader()
{
}

deBoolean deShader::Assemble(char * Code,void (*ErrorCallback)(long LineNumber,char * Error))
{
	deBoolean Ret = deTRUE;
	m_InstructionCount = 0;
	m_ConstantCount = 0;
	m_Instructions.Resize(128);
	m_Constants.Resize(128);
	while(Ret || !Ret) // silly thing just to make a warning go away
	{
		char Word[50] = "";
		GetNextWord(&Code,Word);
		if(strcmp(Word,"") == 0)
		{
			break;
		}
		
		if(strncmp(Word,"//",2) == 0)
		{
			GotoNextLine(&Code);
		}
		else if(!ParseInstruction(ErrorCallback,&Code,Word))
		{
			GotoNextLine(&Code);
			Ret = deFALSE;
		}
	}

	// Shrink the instruction array to the number of instructions we have
	if(m_InstructionCount == 0)
	{
		m_Instructions.Resize(1);
	}
	else
	{
		m_Instructions.Resize(m_InstructionCount);
	}

	// Shrink the constant array to the number of constants we have
	if(m_ConstantCount == 0)
	{
		m_Constants.Resize(1);
	}
	else
	{
		m_Constants.Resize(m_ConstantCount);
	}

	if(!SetShaderType())
		Ret = deFALSE;
	return Ret;
}

deBoolean deShader::ParseInstruction(void (*ErrorCallback)(long LineNumber,char * Error),char ** Code,char * deShaderInstructionName)
{
	// Create a new instruction
	int ArgumentCount = 0;
	deShaderInstruction NewInstruction;
	NewInstruction.Arguments[0] = (unsigned long)ARGUMENT_INVALID;
	NewInstruction.Arguments[1] = (unsigned long)ARGUMENT_INVALID;
	NewInstruction.Arguments[2] = (unsigned long)ARGUMENT_INVALID;
	
	// Parse the instruction name
	if(strcmp(deShaderInstructionName,"DE_VERTEX_SHADER") == 0)
	{
		NewInstruction.Type = INSTRUCTION_VERSION_VERTEX;
		ArgumentCount = 1;
	}
	else if(strcmp(deShaderInstructionName,"DE_PIXEL_SHADER") == 0)
	{
		NewInstruction.Type = INSTRUCTION_VERSION_PIXEL;
		ArgumentCount = 1;
	}
	else if(strcmp(deShaderInstructionName,"add") == 0)
	{
		NewInstruction.Type = INSTRUCTION_ADD;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"dp3") == 0)
	{
		NewInstruction.Type = INSTRUCTION_DP3;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"dp4") == 0)
	{
		NewInstruction.Type = INSTRUCTION_DP4;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"dst") == 0)
	{
		NewInstruction.Type = INSTRUCTION_DST;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"expp") == 0)
	{
		NewInstruction.Type = INSTRUCTION_EXPP;
		ArgumentCount = 2;
	}
	else if(strcmp(deShaderInstructionName,"lit") == 0)
	{
		NewInstruction.Type = INSTRUCTION_LIT;
		ArgumentCount = 2;
	}
	else if(strcmp(deShaderInstructionName,"logp") == 0)
	{
		NewInstruction.Type = INSTRUCTION_LOGP;
		ArgumentCount = 2;
	}
	else if(strcmp(deShaderInstructionName,"mad") == 0)
	{
		NewInstruction.Type = INSTRUCTION_MAD;
		ArgumentCount = 4;
	}
	else if(strcmp(deShaderInstructionName,"max") == 0)
	{
		NewInstruction.Type = INSTRUCTION_MAX;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"min") == 0)
	{
		NewInstruction.Type = INSTRUCTION_MIN;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"mov") == 0)
	{
		NewInstruction.Type = INSTRUCTION_MOV;
		ArgumentCount = 2;
	}
	else if(strcmp(deShaderInstructionName,"mul") == 0)
	{
		NewInstruction.Type = INSTRUCTION_MUL;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"rcp") == 0)
	{
		NewInstruction.Type = INSTRUCTION_RCP;
		ArgumentCount = 2;
	}
	else if(strcmp(deShaderInstructionName,"rsq") == 0)
	{
		NewInstruction.Type = INSTRUCTION_RSQ;
		ArgumentCount = 2;
	}
	else if(strcmp(deShaderInstructionName,"sge") == 0)
	{
		NewInstruction.Type = INSTRUCTION_SGE;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"slt") == 0)
	{
		NewInstruction.Type = INSTRUCTION_SLT;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"sub") == 0)
	{
		NewInstruction.Type = INSTRUCTION_SUB;
		ArgumentCount = 3;
	}
	else if(strcmp(deShaderInstructionName,"DECLARE_CONSTANT") == 0)
	{
		if(!ParseConstantDeclaration(ErrorCallback,Code,INSTRUCTION_DECLARE_CONSTANT))
		{
			Error(ErrorCallback,"Error parsing shader constant.");
			return deFALSE;
		}
		return deTRUE;
	}
	else if(strcmp(deShaderInstructionName,"DECLARE_MATRIX") == 0)
	{
		if(!ParseConstantDeclaration(ErrorCallback,Code,INSTRUCTION_DECLARE_MATRIX))
		{
			Error(ErrorCallback,"Error parsing shader constant.");
			return deFALSE;
		}
		return deTRUE;
	}
	else
	{
		Error(ErrorCallback,"Unrecognized instruction.");
		return deFALSE;
	}

	// Parse our arguments
	deString Argument;
	for(int i = 0;i < ArgumentCount;++i)
	{
		// Reset our argument variable
		Argument = "";

		// If we are parsing our last argument
		if(i == (ArgumentCount-1))
		{
			if(!GetLastArgument(Code,&Argument))
			{
				Error(ErrorCallback,"Error parsing last argument.");
				return deFALSE;
			}
		}
		else
		{
			if(!GetArgument(Code,&Argument))
			{
				Error(ErrorCallback,"Error parsing argument.");
				return deFALSE;
			}
		}

		if(NewInstruction.Type == INSTRUCTION_VERSION_VERTEX || NewInstruction.Type == INSTRUCTION_VERSION_PIXEL)
		{
			if(strcmp(Argument.const_str(),"1.0") == 0)
			{
				NewInstruction.Arguments[i] = ARGUMENT_VERSION_1;
			}
			else
			{
				Error(ErrorCallback,"Unrecognized or Invalid argument.");
				return deFALSE;
			}
		}
		else
		{
			long Index = 0;
			long RegisterStart = -1;
			long Offset = 0;
			long Flags = 0;

			if(Argument[0] == '-')
			{
				// Set the negate flag
				Flags |= IdeShader_Argument_Negate;

				// Set the argument string to the text to the right of the negate modifier
				deTArray<deString> ResultStrings;
				Argument.Split('-',ResultStrings);
				if(ResultStrings.Length() == 2)
				{
					Argument = ResultStrings[1];
				}				
			}

			if(strchr(Argument.const_str(),'['))
			{
				Index = GetArrayArgumentIndex(ErrorCallback,&Argument);
				if(Index < 0)
				{
					Error(ErrorCallback,"Invalid array index.");
					return deFALSE;
				}
			}

			deString OutArgument;
			Offset = GetArgumentOffset(i,&Argument,&OutArgument);
			if(Offset == -1)
			{
				Error(ErrorCallback,"Invalid argument offset.");
				return deFALSE;
			}
					
			// Write only registers
			if(i == 0)
			{
				// the oPos register
				if((strcmp(OutArgument.const_str(),"oPos") == 0) || (strncmp(OutArgument.const_str(),"oPos[",5) == 0))
				{
					RegisterStart = ARGUMENT_OUTPOS;
				}
				else if((strcmp(OutArgument.const_str(),"oColor") == 0) || (strncmp(OutArgument.const_str(),"oColor[",7) == 0))
				{
					RegisterStart = ARGUMENT_OUTCOLOR;
				} 
				else if((strcmp(OutArgument.const_str(),"oFog") == 0) || (strncmp(OutArgument.const_str(),"oFog[",5) == 0))
				{
					RegisterStart = ARGUMENT_OUTFOG;
					if(Offset == 0)
					{
						Offset = 1;
					}
				} 
				else if((strcmp(OutArgument.const_str(),"oPts") == 0) || (strncmp(OutArgument.const_str(),"oPts[",5) == 0))
				{
					RegisterStart = ARGUMENT_OUTPTS;
					if(Offset == 0)
					{
						Offset = 1;
					}
				} 
				else if((strcmp(OutArgument.const_str(),"oTex") == 0) || (strncmp(OutArgument.const_str(),"oTex[",5) == 0))
				{
					RegisterStart = ARGUMENT_OUTTEX;
				} 

			}
			// Read only registers
			else
			{
				if((strncmp(OutArgument.const_str(),"c[",2) == 0) || (strcmp(OutArgument.const_str(),"c") == 0))
				{
					RegisterStart = ARGUMENT_CONSTANT;
				}
			}

			// Read and write registers
			if(RegisterStart == -1)
			{
				if((strncmp(OutArgument.const_str(),"v[",2) == 0) || (strcmp(OutArgument.const_str(),"v") == 0))
				{
					RegisterStart = ARGUMENT_VERTEX;
				}
			
				if((strncmp(OutArgument.const_str(),"t[",2) == 0) || (strcmp(OutArgument.const_str(),"t") == 0))
				{
					RegisterStart = ARGUMENT_TEMP;
				}

				if(RegisterStart == -1)
				{
					Error(ErrorCallback,"Unrecognized argument.");
					return deFALSE;
				}			
			}

			NewInstruction.Arguments[i] = (RegisterStart + Index)|Offset|Flags;			
		}
	}

	// Add the instruction to the instruction array
	m_Instructions[m_InstructionCount] = NewInstruction;
	++m_InstructionCount;

	return deTRUE;
}

long deShader::GetArrayArgumentIndex(void (* /*ErrorCallback*/)(long LineNumber,char * Error),deString * Argument)
{
	int Offset = 0;

	// Split the string until we have a string containing only the array index
	deTArray<deString> Strings;
	Argument->Split('[',Strings);
	if(Strings.Length() != 2)
	{
		return -1;
	}

	deTArray<deString> Strings2;
	Strings[1].Split(']',Strings2);
	if(Strings2.Length() != 2)
	{
		return -1;
	}

	// If we have something after the ']'
	if(strcmp(Strings2[1].const_str(),"") != 0)
	{
		deTArray<deString> Strings3;
		Strings2[1].Split('+',Strings3);
		int Length = Strings3.Length();
		if(Length >= 2)
		{
			for(int i = 1;i < Length;++i)
			{
				Offset += atoi(Strings3[i].const_str());
			}
		}

		Strings2[1].Split('-',Strings3);
		Length = Strings3.Length();
		if(Length >= 2)
		{
			for(int i = 1;i < Length;++i)
			{
				Offset -= atoi(Strings3[i].const_str());
			}
		}
	}

	// Conver our deString to a integer
	return (atoi(Strings2[0].const_str()) + Offset)*0x1000000;
}

long deShader::GetArgumentOffset(long ArgumentNumber,deString * Argument,deString * OutArgument)
{
	deTArray<deString> Strings;
	long Offset = 0;

	Argument->Split('.',Strings);
	int NumOfStrings = Strings.Length();
	if(NumOfStrings == 1)
	{
		*OutArgument = *Argument;
		return 0;
	}
	if(NumOfStrings == 2)
	{
		*OutArgument = Strings[0];

		if(ArgumentNumber == 0)
		{
			if(strchr(Strings[1].const_str(),'x'))
			{
				Offset |= IdeShader_Argument_X;
			}
			if(strchr(Strings[1].const_str(),'y'))
			{
				Offset |= IdeShader_Argument_Y;
			}
			if(strchr(Strings[1].const_str(),'z'))
			{
				Offset |= IdeShader_Argument_Z;
			}
			if(strchr(Strings[1].const_str(),'w'))
			{
				Offset |= IdeShader_Argument_W;
			}
		}
		else
		{
			// The first character
			if(Strings[1][0] == 'x')
			{
				Offset |= IdeShader_Argument_Swizzle_1X;
			}
			else if(Strings[1][0] == 'y')
			{
				Offset |= IdeShader_Argument_Swizzle_1Y;
			}
			else if(Strings[1][0] == 'z')
			{
				Offset |= IdeShader_Argument_Swizzle_1Z;
			}
			else if(Strings[1][0] == 'w')
			{
				Offset |= IdeShader_Argument_Swizzle_1W;
			}
			else
			{
				return deFALSE;
			}

			// The second character
			if(Strings[1][1] == 'x')
			{
				Offset |= IdeShader_Argument_Swizzle_2X;
			}
			else if(Strings[1][1] == 'y')
			{
				Offset |= IdeShader_Argument_Swizzle_2Y;
			}
			else if(Strings[1][1] == 'z')
			{
				Offset |= IdeShader_Argument_Swizzle_2Z;
			}
			else if(Strings[1][1] == 'w')
			{
				Offset |= IdeShader_Argument_Swizzle_2W;
			}
			else
			{
				return deFALSE;
			}

			// The third character
			if(Strings[1][2] == 'x')
			{
				Offset |= IdeShader_Argument_Swizzle_3X;
			}
			else if(Strings[1][2] == 'y')
			{
				Offset |= IdeShader_Argument_Swizzle_3Y;
			}
			else if(Strings[1][2] == 'z')
			{
				Offset |= IdeShader_Argument_Swizzle_3Z;
			}
			else if(Strings[1][2] == 'w')
			{
				Offset |= IdeShader_Argument_Swizzle_3W;
			}
			else
			{
				return deFALSE;
			}

			// The fourth character
			if(Strings[1][3] == 'x')
			{
				Offset |= IdeShader_Argument_Swizzle_4X;
			}
			else if(Strings[1][3] == 'y')
			{
				Offset |= IdeShader_Argument_Swizzle_4Y;
			}
			else if(Strings[1][3] == 'z')
			{
				Offset |= IdeShader_Argument_Swizzle_4Z;
			}
			else if(Strings[1][3] == 'w')
			{
				Offset |= IdeShader_Argument_Swizzle_4W;
			}
			else
			{
				return deFALSE;
			}
		}
		if(Offset == 0)
			return -1;
		else return Offset;
	}
	else
	{
		OutArgument = Argument;
		return -1;
	}
}

deBoolean deShader::Disassemble(deString * Code)
{
	size_t i;
	for(i = 0;i < m_Instructions.Length();++i)
	{
		if(!DisassembleInstruction(&m_Instructions[i],Code))
		{
			return deFALSE;
		}
	}

	for(i = 0;i < m_Constants.Length();++i)
	{
		if(!DisassembleConstant(&m_Constants[i],Code))
		{
			return deFALSE;
		}
	}
	return deTRUE;
}

deBoolean deShader::DisassembleArgument(long ArgumentNumber,DWORD vArgument,deString * Code)
{
	if(vArgument & IdeShader_Argument_Negate)
	{
		*Code += "-";
	}

	if(vArgument == ARGUMENT_VERSION_1)
	{
		*Code += " 1.0";
	}
	else if((ARGUMENT_CONSTANT <= vArgument) && (vArgument < ARGUMENT_TEMP))
	{
		*Code += " c";
		DisassembleIndexAndOffset(ArgumentNumber,ARGUMENT_CONSTANT,vArgument,Code);	
	}
	else if((ARGUMENT_TEMP <= vArgument) && (vArgument < ARGUMENT_VERTEX))
	{
		*Code += " t";
		DisassembleIndexAndOffset(ArgumentNumber,ARGUMENT_TEMP,vArgument,Code);	
	}
	else if((ARGUMENT_VERTEX <= vArgument) && (vArgument < ARGUMENT_OUTPOS))
	{
		*Code += " v";
		DisassembleIndexAndOffset(ArgumentNumber,ARGUMENT_VERTEX,vArgument,Code);	
	}
	else if((ARGUMENT_OUTPOS <= vArgument) && (vArgument < ARGUMENT_OUTCOLOR))
	{
		*Code += " oPos";
		DisassembleIndexAndOffset(ArgumentNumber,ARGUMENT_OUTPOS,vArgument,Code);	
	}
	else if((ARGUMENT_OUTCOLOR <= vArgument) && (vArgument <= ARGUMENT_OUTFOG))
	{
		*Code += " oColor";
		DisassembleIndexAndOffset(ArgumentNumber,ARGUMENT_OUTCOLOR,vArgument,Code);	
	}
	else if((ARGUMENT_OUTFOG <= vArgument) && (vArgument < ARGUMENT_OUTPTS))
	{
		*Code += " oFog";
		DisassembleIndexAndOffset(ArgumentNumber,ARGUMENT_OUTFOG,vArgument,Code);	
	}
	else if((ARGUMENT_OUTPTS <= vArgument) && (vArgument < ARGUMENT_OUTTEX))
	{
		*Code += " oPts";
		DisassembleIndexAndOffset(ArgumentNumber,ARGUMENT_OUTPTS,vArgument,Code);	
	}
	else if((ARGUMENT_OUTTEX <= vArgument) && (vArgument < ARGUMENT_OUTTEX_END))
	{
		*Code += " oTex";
		DisassembleIndexAndOffset(ArgumentNumber,ARGUMENT_OUTTEX,vArgument,Code);	
	}
	else
	{
		return deFALSE;
	}

	return deTRUE;
}

void deShader::DisassembleIndexAndOffset(long ArgumentNumber,DWORD IndexBase,DWORD Argument,deString * Code)
{
	*Code += "[";
	int Index = (Argument-IndexBase)/0x1000000;
	char cIndex[3];
	itoa(Index,cIndex,10);
	*Code += cIndex;
	*Code += "]";
	
	if(ArgumentNumber == 0)
	{
		if((Argument & IdeShader_Argument_X) || (Argument & IdeShader_Argument_Y) || (Argument & IdeShader_Argument_Z) || (Argument & IdeShader_Argument_W))
		{
			*Code += ".";
			if(Argument & IdeShader_Argument_X)
			{
				*Code += "x";
			}
		
			if(Argument & IdeShader_Argument_Y)
			{
				*Code += "y";
			}

			if(Argument & IdeShader_Argument_Z)
			{
				*Code += "z";
			}
		
			if(Argument & IdeShader_Argument_W)
			{
				*Code += "w";
			}
		}
	}
	else
	{
		if(Argument & IdeShader_Argument_Swizzle_1X)
		{
			*Code += ".x";
		}
		else if(Argument & IdeShader_Argument_Swizzle_1Y)
		{
			*Code += ".y";
		}
		else if(Argument & IdeShader_Argument_Swizzle_1Z)
		{
			*Code += ".z";
		}
		else if(Argument & IdeShader_Argument_Swizzle_1W)
		{
			*Code += ".w";
		}
		
		if(Argument & IdeShader_Argument_Swizzle_2X)
		{
			*Code += "x";
		}
		else if(Argument & IdeShader_Argument_Swizzle_2Y)
		{
			*Code += "y";
		}
		else if(Argument & IdeShader_Argument_Swizzle_2Z)
		{
			*Code += "z";
		}
		else if(Argument & IdeShader_Argument_Swizzle_2W)
		{
			*Code += "w";
		}

		if(Argument & IdeShader_Argument_Swizzle_3X)
		{
			*Code += "x";
		}
		else if(Argument & IdeShader_Argument_Swizzle_3Y)
		{
			*Code += "y";
		}

		if(Argument & IdeShader_Argument_Swizzle_3Z)
		{
			*Code += "z";
		}
		else if(Argument & IdeShader_Argument_Swizzle_3W)
		{
			*Code += "w";
		}
			
			
		if(Argument & IdeShader_Argument_Swizzle_4X)
		{
			*Code += "x";
		}
		else if(Argument & IdeShader_Argument_Swizzle_4Y)
		{
			*Code += "y";
		}
		else if(Argument & IdeShader_Argument_Swizzle_4Z)
		{
			*Code += "z";
		}
		else if(Argument & IdeShader_Argument_Swizzle_4W)
		{
			*Code += "w";
		}
	}

}

deBoolean deShader::DisassembleInstruction(IdeShader::deShaderInstruction * deShaderInstruction,deString * Code)
{
	// Find out what type of instruction it is
	switch(deShaderInstruction->Type)
	{
		case INSTRUCTION_VERSION_VERTEX:
			*Code += "DE_VERTEX_SHADER";
			break;

		case INSTRUCTION_VERSION_PIXEL:
			*Code += "DE_PIXEL_SHADER";
			break;

		case INSTRUCTION_ADD:
			*Code += "add";
			break;

		case INSTRUCTION_DP3:
			*Code += "dp3";
			break;

		case INSTRUCTION_DP4:
			*Code += "dp4";
			break;

		case INSTRUCTION_DST:
			*Code += "dst";
			break;

		case INSTRUCTION_EXPP:
			*Code += "expp";
			break;

		case INSTRUCTION_LIT:
			*Code += "lit";
			break;
						
		case INSTRUCTION_LOGP:
			*Code += "logp";
			break;

		case INSTRUCTION_MAD:
			*Code += "mad";
			break;

		case INSTRUCTION_MAX:
			*Code += "max";
			break;

		case INSTRUCTION_MIN:
			*Code += "min";
			break;

		case INSTRUCTION_MOV:
			*Code += "mov";
			break;

		case INSTRUCTION_MUL:
			*Code += "mul";
			break;

		case INSTRUCTION_RCP:
			*Code += "rcp";
			break;

		case INSTRUCTION_RSQ:
			*Code += "rsq";
			break;

		case INSTRUCTION_SGE:
			*Code += "sge";
			break;

		case INSTRUCTION_SLT:
			*Code += "slt";
			break;

		case INSTRUCTION_SUB:
			*Code += "sub";
			break;

		default:
			return deFALSE;
	}

	// Disassemble the instruction's arguments
	for(int i = 0;i < 3 && deShaderInstruction->Arguments[i] != ARGUMENT_INVALID;++i)
	{
		if(i != 0)
			*Code += ",";

		if(!DisassembleArgument(i,deShaderInstruction->Arguments[i],Code))
			return deFALSE;
	}

	*Code += ";\n";
	return deTRUE;
}

deBoolean deShader::DisassembleConstant(IdeShader::deShaderConstant * Constant,deString * Code)
{
	if(Constant->Type == INSTRUCTION_DECLARE_CONSTANT)
	{
		*Code += "DECLARE_CONSTANT ";

		for(int i = 0;i < 5;++i)
		{		
			if(i == 0)
			{
				//itoa((int)Constant->Arguments[i],Argument,10);
				*Code << ((int)Constant->Arguments[i]);
			}
			else
			{
				//sprintf(Argument,"%f",Constant->Arguments[i]);
				*Code << Constant->Arguments[i];
			}

			//*Code += Argument;

			if(i != 4)
			{
				*Code += ",";
			}
		}

		*Code += ";\n";
	}
	else if(Constant->Type == INSTRUCTION_DECLARE_MATRIX)
	{
		*Code += "DECLARE_MATRIX ";

		// Disassemble the index argument
		char Index[5];
		itoa((int)Constant->Arguments[0],Index,10);
		*Code += Index;
		*Code += ",";

		switch((int)Constant->Arguments[1])
		{
			case ARGUMENT_DE_TRANSFORM:
				*Code += "DE_TRANSFORM,";
				break;

			case ARGUMENT_DE_PROJECTION:
				*Code += "DE_PROJECTION,";
				break;

			case ARGUMENT_DE_TRANSFORM_PROJECTION:
				*Code += "DE_TRANSFORM_PROJECTION,";
				break;

			default:
				return deFALSE;
				break;
		}

		
		switch((int)Constant->Arguments[2])
		{
			case ARGUMENT_DE_IDENTITY:
				*Code += "DE_IDENTITY;\n";
				break;

			case ARGUMENT_DE_INVERSE:
				*Code += "DE_INVERSE;\n";
				break;

			case ARGUMENT_DE_TRANSPOSE:
				*Code += "DE_TRANSPOSE;\n";
				break;

			case ARGUMENT_DE_INVERSE_TRANSPOSE:
				*Code += "DE_INVERSE_TRANSPOSE;\n";
				break;

			default:
				return deFALSE;
				break;
		}

	}
	else
	{
		return deFALSE;
	}

	return deTRUE;
}

deShader::ShaderType deShader::GetShaderType() const
{
	// Return the shader type
	return m_ShaderType;
}

long deShader::GetInstructionCount() const
{
	return m_InstructionCount;
}

const deTArray<IdeShader::deShaderInstruction> * deShader::GetInstructions() const
{
	return &m_Instructions;
}

long deShader::GetConstantCount() const
{
	return m_ConstantCount;
}

const deTArray<IdeShader::deShaderConstant> * deShader::GetConstants() const
{
	return &m_Constants;
}

deBoolean deShader::Write(IdeFile * File) const
{
	int i;

	// Write the number of shader constants to the file
	if(File->Write((void*)&m_ConstantCount,sizeof(m_ConstantCount)) != sizeof(m_ConstantCount))
			return deFALSE;

	// Write the shader constants to the file
	for(i = 0;i < m_ConstantCount;++i)
	{
		if(File->Write((void*)&m_Constants[i],sizeof(deShaderConstant)) != sizeof(deShaderConstant))
			return deFALSE;
	}

	// Write the shader instructions to the file
	for(i = 0;i < m_InstructionCount;++i)
	{
		if(File->Write((void*)&m_Instructions[i],sizeof(deShaderInstruction)) != sizeof(deShaderInstruction))
			return deFALSE;
	}

	// Return Success
	return deTRUE;
}

deBoolean deShader::Read(IdeFile * File)
{
	// Get the file size
	int FileSize = File->GetSize();
	
	// Make sure we have a valid file
	if(FileSize < 0)
		return deFALSE;

	// Set the instruction count
	m_InstructionCount = 0;
	m_ConstantCount = 0;

	// Resize our instruction array to hold the shader instructions
	m_Instructions.Resize(128);
	
	// Read the number of constants from the file
	if(File->Read(&m_ConstantCount,sizeof(m_ConstantCount)) != sizeof(m_ConstantCount))
	{
		m_ShaderType = SHADER_INVALID;
		m_ConstantCount = 0;
		m_InstructionCount = 0;
		m_Instructions.Resize(0);
		m_Constants.Resize(0);
		return deFALSE;
	}

	m_Constants.Resize(m_ConstantCount);

	// Read the shader's constants from the file
	deShaderConstant NewConstant;
	for(int i = 0;i < m_ConstantCount;++i)
	{
		if(File->Read(&NewConstant,sizeof(NewConstant)) != sizeof(NewConstant))
		{
			m_ShaderType = SHADER_INVALID;
			m_ConstantCount = 0;
			m_Constants.Resize(m_ConstantCount);
			return deFALSE;
		}

		m_Constants[i] = NewConstant;
	}


	// Read the shader's instructions from the file
	deShaderInstruction NewInstruction;
	while(File->GetPosition() < FileSize)
	{
		if(File->Read(&NewInstruction,sizeof(NewInstruction)) != sizeof(NewInstruction))
		{
			m_ShaderType = SHADER_INVALID;
			m_InstructionCount = 0;
			m_Instructions.Resize(m_InstructionCount);
			return deFALSE;
		}

		m_Instructions[m_InstructionCount] = NewInstruction;
		++m_InstructionCount;
	}
		
	m_Instructions.Resize(m_InstructionCount);

	SetShaderType();

	// Return Success
	return deTRUE;
}

// Get the next "word" in a string
void deShader::GetNextWord(char ** String,char * Word)
{
	// Create a buffer to hold th next word
	int Pos = 0;
	char LastChar = 0;

	// Get the next word
	while(((*String[0] > 0) && (*String[0] != ' ') && (*String[0] != '\n') && ((*String[0] != 13))) ||
		(((LastChar == 0) && (*String[0] > 0)) || (LastChar == ' ') || (LastChar == '\n') ))
	{
		if(*String[0] == 13)
		{
			++*String;
			continue;
		}

		if(*String[0] == '\n')
			++m_CurrentLine;

		if(*String[0] == 0)
			break;

		if((*String[0] != ' ') && (*String[0] != '\n'))
		{
			Word[Pos] = *String[0];
			LastChar = *String[0];
			++Pos;
		}

		if(*String[0] == ';')
		{
			++*String;
			break;
		}

		++*String;
	}

	if(*String[0] == '\n')
		++m_CurrentLine;

	// NULL terminate the word
	Word[Pos] = 0;
}

deBoolean deShader::GetArgument(char ** String,deString * Argument)
{
	// Create a buffer to hold the next Argument
	char LastChar = 0;

	// Get the next Argument
	while(((*String[0] != 0) && (*String[0] != ' ') && (*String[0] != '\n') && (*String[0] != 13)) ||
		(((LastChar == 0) && (*String[0] != 13)) || (LastChar == ' ') || (LastChar == '\n') ))
	{
		if(*String[0] == '\n')
			++m_CurrentLine;

		if(*String[0] == 0)
			break;

		if(*String[0] == ',')
		{
			++*String;
			return deTRUE;
			break;
		}

		if((*String[0] != ' ') && (*String[0] != '\n'))
		{
			char Copy[2];
			Copy[0] = *String[0];
			Copy[1] = 0;
			*Argument += Copy;
			LastChar = *String[0];
		}

		if(*String[0] == ';')
		{
			++*String;
			break;
		}

		++*String;
	}

	if(*String[0] == '\n')
		++m_CurrentLine;

	return deFALSE;
}

deBoolean deShader::GetLastArgument(char ** String,deString * Argument)
{
	// Create a buffer to hold the next Argument
	char LastChar = 0;

	// Get the next Argument
	while(((*String[0] != 0) && (*String[0] != ' ') && (*String[0] != '\n') && (*String[0] != 13)) ||
		(((LastChar == 0) && (*String[0] != 13)) || (LastChar == ' ') || (LastChar == '\n') ))
	{
		if(*String[0] == '\n')
			++m_CurrentLine;

		if(*String[0] == 0)
			break;

		if(*String[0] == ';')
		{
			// Move to the next character
			++*String;
			return deTRUE;
			break;
		}

		if((*String[0] != ' ') && (*String[0] != '\n'))
		{
			char Copy[2];
			Copy[0] = *String[0];
			Copy[1] = 0;
			*Argument += Copy;
			LastChar = *String[0];
		}

		if(*String[0] == ';')
		{
			++*String;
			break;
		}

		++*String;
	}

	if(*String[0] == '\n')
		++m_CurrentLine;

	return deFALSE;
}

// Go to the next line in a string
void deShader::GotoNextLine(char ** String)
{
	while((*String[0] != '\n') && (*String[0] != 0))
	{
		++*String;
	}

	if(*String[0] == '\n')
	{
		++*String;
		++m_CurrentLine;
	}
}

void deShader::Error(void (*ErrorCallback)(long LineNumber,char * Error),char * Error)
{
	if(ErrorCallback)
		ErrorCallback(m_CurrentLine,Error);
}

deBoolean deShader::SetDriverShader(void * Shader)
{
	m_DriverShader = Shader;
	return deTRUE;
}

void * deShader::GetDriverShader() const
{
	return m_DriverShader;
}

deBoolean deShader::SetShaderType()
{
	// If we have jsut loaded a vertex shader
	if(m_Instructions[0].Type == INSTRUCTION_VERSION_VERTEX)
	{
		m_ShaderType = SHADER_VERTEX;
	}
	// If we have jsut loaded a pixel shader
	else if(m_Instructions[0].Type == INSTRUCTION_VERSION_PIXEL)
	{
		m_ShaderType = SHADER_PIXEL;
	}
	// If the file we just read wasn't a valid shader
	else
	{
		m_ShaderType = SHADER_INVALID;
		m_InstructionCount = 0;
		m_Instructions.Resize(m_InstructionCount);
		return deFALSE;
	}

	return deTRUE;

}

deBoolean deShader::ParseConstantDeclaration(void (*ErrorCallback)(long LineNumber,char * Error),char ** Code,deShaderInstructionType Instruction)
{
	deShaderConstant NewConstant;
	NewConstant.Type = Instruction;
	NewConstant.Arguments[0] = ARGUMENT_INVALID;
	NewConstant.Arguments[1] = ARGUMENT_INVALID;
	NewConstant.Arguments[2] = ARGUMENT_INVALID;
	NewConstant.Arguments[3] = ARGUMENT_INVALID;
	NewConstant.Arguments[4] = ARGUMENT_INVALID;

	deString Argument;
	int ArgumentCount = 0;
	if(Instruction == INSTRUCTION_DECLARE_CONSTANT)
	{
		ArgumentCount = 5;
	}
	else if(Instruction == INSTRUCTION_DECLARE_MATRIX)
	{
		ArgumentCount = 3;
	}

	for(int i = 0;i < ArgumentCount;++i)
	{
		// Reset our argument variable
		Argument = "";

		// If we are parsing our last argument
		if(i == (ArgumentCount-1))
		{
			if(!GetLastArgument(Code,&Argument))
			{
				Error(ErrorCallback,"Error parsing last argument.");
				return deFALSE;
			}
		}
		else
		{
			if(!GetArgument(Code,&Argument))
			{
				Error(ErrorCallback,"Error parsing argument.");
				return deFALSE;
			}
		}

		if(Instruction == INSTRUCTION_DECLARE_CONSTANT || i == 0)
		{
			NewConstant.Arguments[i] = (float)atof(Argument.const_str());
		}
		else if(Instruction == INSTRUCTION_DECLARE_MATRIX)
		{
			if(i == 0)
			{
				NewConstant.Arguments[i] = (float)atof(Argument.const_str());
			}

			if(i == 1)
			{
				if(strcmp(Argument.const_str(),"DE_TRANSFORM") == 0)
				{
					NewConstant.Arguments[i] = ARGUMENT_DE_TRANSFORM;
				}
				else if(strcmp(Argument.const_str(),"DE_PROJECTION") == 0)
				{
					NewConstant.Arguments[i] = ARGUMENT_DE_PROJECTION;
				}
				else if(strcmp(Argument.const_str(),"DE_TRANSFORM_PROJECTION") == 0)
				{
					NewConstant.Arguments[i] = ARGUMENT_DE_TRANSFORM_PROJECTION;
				}
				else
				{
					return deFALSE;
				}
			}

			if(i == 2)
			{
				if(strcmp(Argument.const_str(),"DE_IDENTITY") == 0)
				{
					NewConstant.Arguments[i] = ARGUMENT_DE_IDENTITY;
				}
				else if(strcmp(Argument.const_str(),"DE_INVERSE") == 0)
				{
					NewConstant.Arguments[i] = ARGUMENT_DE_INVERSE;
				}
				else if(strcmp(Argument.const_str(),"DE_TRANSPOSE") == 0)
				{
					NewConstant.Arguments[i] = ARGUMENT_DE_TRANSPOSE;
				}
				else if(strcmp(Argument.const_str(),"DE_INVERSE_TRANSPOSE") == 0)
				{
					NewConstant.Arguments[i] = ARGUMENT_DE_INVERSE_TRANSPOSE;
				}
				else
				{
					return deFALSE;
				}
			}

		}

	}

	// Add the constant to the constant array
	m_Constants[m_ConstantCount] = NewConstant;
	++m_ConstantCount;

	return deTRUE;
}