/****************************************************************************************/
/*  SHADER_FUNCTIONS.C                                                                  */
/*                                                                                      */
/*  Author: Timothy Roff	                                                            */
/*  Description:  Shader implementation                                                 */
/*                                                                                      */
/*  The contents of this file are subject to the Jet3D Public License                   */
/*  Version 1.02 (the "License"); you may not use this file except in                   */
/*  compliance with the License. You may obtain a copy of the License at                */
/*  http://www.jet3d.com                                                                */
/*                                                                                      */
/*  Software distributed under the License is distributed on an "AS IS"                 */
/*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */
/*  the License for the specific language governing rights and limitations              */
/*  under the License.                                                                  */
/*                                                                                      */
/*  This file was not part of the original Jet3D, released December 12, 1999.           */
/*                                                                                      */
/****************************************************************************************/
#include "shader_functions.h"


//=====================================================================================
//	ParseWaveForm
//=====================================================================================
int ParseWaveForm(const char* str)
{
   if (stricmp(str, "sin") == 0) {
      return SH_WAVE_SIN;
   }
   else if (stricmp(str, "triangle") == 0) {
      return SH_WAVE_TRIANGLE;
   }
   else if (stricmp(str, "square") == 0) {
      return SH_WAVE_SQUARE;
   }
   else if (stricmp(str, "sawtooth") == 0) {
      return SH_WAVE_SAWTOOTH;
   }
   else if (stricmp(str, "inversesawtooth") == 0) {
      return SH_WAVE_INVSAWTOOTH;
   }
   else if (stricmp(str, "noise") == 0) {
      jeShader_Error("waveform noise is not documented by Id");
      return SH_WAVE_NOISE;
   }
   else {
      jeShader_Error("unknown waveform '%s'", str);
      return -1;
   }
}

//=====================================================================================
//	ParseColor
//=====================================================================================
void shader_number_parse(const char* string, sh_num_t* number)
{
   char* place;
   int   len;

   len = strlen(string);

   if (len > JE_SHADER_NUMBER_LEN) {
	   jeErrorLog_AddString(JE_ERR_SHADER_SCRIPT, "number string length exceeds JE_SHADER_NUMBER_LEN in shader", NULL);
   }

   number->value = (float)strtod(string, &place);

   if ((place - string) != len) {
      expected("number", string);
   }

   strncpy(number->string, string, JE_SHADER_NUMBER_LEN);
   number->string[JE_SHADER_NUMBER_LEN] = '\0';
}

void parse_clamped_number(const char* string, sh_num_t* number, const char* name, float low, float high)
{
	char * temp=NULL;
	shader_number_parse(string, number);

	if (number->value < low) {
#ifdef _DEBUG
      sprintf(temp, "%s is not normalized. clamping to %f", name, low);
	  jeErrorLog_AddString(JE_ERR_SHADER_SCRIPT, "SH_PARSE: ", temp);
#endif
      number->value = low;
	}

	else if (number->value > high) {
#ifdef _DEBUG
      sprintf(temp, "%s is not normalized, clamping to %f", name, high);
	  jeErrorLog_AddString(JE_ERR_SHADER_SCRIPT, "SH_PARSE: ", temp);
#endif
      number->value = high;
	}
}

void ParseColor(sh_num_t color[3], const char* red, const char* green, const char* blue)
{
   parse_clamped_number(red,   &color[0], "red color component",   0, 1);
   parse_clamped_number(green, &color[1], "green color component", 0, 1);
   parse_clamped_number(blue,  &color[2], "blue color component",  0, 1);
}

//=====================================================================================
//	cullParseFunc
//=====================================================================================
jeBoolean cullParseFunc(int argc, char* argv[], shader_info_t *info)
{
	assert(info != NULL);

   if (argc == 1) {
      // if there are no arguments, it defaults to 'cull front'
      info->shader->cull_face = SH_CULL_FRONT;
   }
   else {
      if (stricmp(argv[1], "front") == 0) {
         info->shader->cull_face = SH_CULL_FRONT;
      }
      else if (stricmp(argv[1], "back") == 0) {
         info->shader->cull_face = SH_CULL_BACK;
      }
      else if ((stricmp(argv[1], "disable") == 0) ||
               (stricmp(argv[1], "none") == 0)) {
         info->shader->cull_face = SH_CULL_DISABLE;
      }

     else if (stricmp(argv[1], "_both") == 0) {
         info->shader->cull_face = SH_CULL_BOTH_EXT;
      }

      else {
         jeShader_Error("argument '%s' is invalid, it should be one of 'front', 'back', '_both', 'disable', or 'none'", argv[1]);
         return JE_TRUE;
      }
   }

   return JE_TRUE;
}


//=====================================================================================
//	alphaFuncParseFunc
//=====================================================================================
jeBoolean alphaFuncParseFunc(int unused, char* argv[], shader_info_t *info)
{
	assert(info != NULL);

   (void)unused;

   if (stricmp(argv[1], "GT0") == 0) {
      info->stage.alphafunc = SH_ALPHAFUNC_GT0;
   }
   else if (stricmp(argv[1], "LT128") == 0) {
      info->stage.alphafunc = SH_ALPHAFUNC_LT128;
   }
   else if (stricmp(argv[1], "GE128") == 0) {
      info->stage.alphafunc = SH_ALPHAFUNC_GE128;
   }
   else {
      jeShader_Error("unknown alphaFunc '%s'", argv[1]);
      return JE_TRUE;
   }

   info->stage.flags |= SH_STAGE_MAP_NEEDS_ALPHA;
   info->stage.flags |= SH_STAGE_ALPHAFUNC;

   return JE_TRUE;
}

//=====================================================================================
//	Function Table
//=====================================================================================
command_parse_table_t command_table[] = 
{
	/*general notes by Timothy Roff (cyrius):
	1 min and 1 max parms mean that the name is the only param
	for example:
	{ "detail",               1,  1, JE_FALSE, detailParseFunc     },
	"detail" is the only thing passed to detailParseFunc

	in the case of
	{ "map",                  2,  2,JE_FALSE, mapParseFunc        },
	when say (for example!) map GlobalMaterials/Jet3d.bmp is parsed in a shader file,
	"map" and "GlobalMaterials/Jet3d.bmp"
	are BOTH passed to the mapParseFunc
	*/

    // global commands
	//name					min	  max	is_stage, is_ignored, c_func
    { "skyParms",             4,  4,	JE_FALSE, skyParmsParseFunc       },
    { "cull",                 1,  2,	JE_FALSE, cullParseFunc           },
    { "deformVerts",	       2, 10,	JE_FALSE, deformVertexesParseFunc },
    //{ "fogparms",             5,  7, JE_FALSE, JE_FALSE, fogparmsParseFunc       },
    //{ "nopicmip",             1,  1, JE_FALSE, JE_FALSE, nopicmipParseFunc       },
    //{ "nomipmaps",            1,  1, JE_FALSE, JE_FALSE, nomipmapsParseFunc      },
    //{ "polygonOffset",        1,  1, JE_FALSE, JE_FALSE, polygonOffsetParseFunc  },
	{ "map",                  2,  2, JE_FALSE, mapParseFunc        },
    
    // non-implemented and un-tested (cyrius)
    //{ "entityMergable",       1,  1, JE_FALSE, JE_FALSE, entityMergableParseFunc },
    { "light",                2,  2,	JE_TRUE,  NULL }, //specifies additive light level
    { "lightning",            1,  1,	JE_TRUE,  NULL },
    { "cloudparms",           3,  3,	JE_TRUE,  NULL },
    { "sky",                  2,  2,	JE_TRUE,  NULL },

    // extensions
    { "_skyRotate",           4,  4,	JE_TRUE,  NULL              },
    { "_skyBoxFaces",         1,  7,	JE_TRUE,  NULL              },

    // Jet3d keywords (ignored by the interpreter) //THESE SHOULD NOT BE IGNORED!
    { "jemap_tessSize",       1,  1,	JE_TRUE,  NULL },
    { "jemap_sun",            1,  1,	JE_TRUE,  NULL },
    { "surfaceparm",          1,  1,	JE_TRUE,  NULL },
	{ "type",					2,	2,	JE_FALSE,	typeParse		},	
	    

    // editor commands (ignored by the interpreter) //THESE SHOULD NOT BE IGNORED!!!!
	{ "jwe_save_into_object",	1,	1,	JE_TRUE,	NULL },
    { "jwe_trans",				1,  1,	JE_TRUE,	NULL },

    //actor commands
	{ "actor",					2,	2,	JE_FALSE,	actorParseFunc },
	{ "bone",					2,	JE_SHADER_MAX_BONES,	JE_FALSE,	actorBoneParse }, //min 1 bone max 5 bones
	{ "actor_section",			2,	2,	JE_FALSE,	actorSectionParse},
	
	// level commands
    { "clampmap",             2,  2, JE_FALSE, mapParseFunc        },
    { "animMap",              3, 12, JE_FALSE, animMapParseFunc    },
    { "blendFunc",            2,  3, JE_FALSE, blendFuncParseFunc  },
    { "rgbGen",               2,  7, JE_FALSE, rgbaGenParseFunc    },
    { "alphaGen",             2,  7, JE_FALSE, rgbaGenParseFunc    },
    { "tcGen",                2, 12, JE_FALSE, tcGenParseFunc      },
    { "tcMod",                3,  8, JE_FALSE, tcModParseFunc      },
    { "alphaFunc",            2,  2, JE_FALSE, alphaFuncParseFunc  },
	{ "procTexture",				2,	20,	JE_FALSE,	procTexture		},

	//actor AND level commands
	{ "particle",				2,	14,	JE_FALSE,	particleParse},

    // extensions
    { "_jeSkyMap",            2,  2, JE_FALSE, jeSkyMapParseFunc },
    { "alphaMap",				2,	2,	JE_TRUE,	NULL },
	//{ "_filter",				2,	2,	JE_FALSE,	filterParse	},

    { 0 }
};