/****************************************************************************************/
/*  JEPARSE.C                                                                           */
/*                                                                                      */
/*  Author: Timothy Roff	                                                            */
/*  Description:  The parsing engine                                                    */
/*                                                                                      */
/*  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 "jet.h"
#include "jeParse.h"
#include "jeParse_h.h"
#include "jeParse_cmdtable.h"
#include "errorlog.h"
#include <Assert.h>

void jeParse_Error(const char * error, ...);

void expected(const char* expected, const char* got)
{
   char * temp=NULL;
   sprintf(temp, "expected %s but found '%s'", expected, got);

   jeErrorLog_AddString(JE_ERR_PARSE_FAILURE, "JET_PARSE: ", temp);
}

jeBoolean jeParse_Match(JE_PARSE_TOKEN id, scriptData *info)
{
	assert(info != NULL);
	
	if (id == info->lookahead)
	{
		info->lookahead = jeParse_Scan(info);
		return JE_TRUE;
	}
	else
	{
		switch(id)
		{
			case JE_SHADER_TOKEN_STRING:
				expected("string", info->token.string);
				break;
			case JE_SHADER_TOKEN_EOF:
				expected("<EOF>", info->token.string);
				break;
			case '{':
				expected("{", info->token.string);
				break;
			case '}':
	            expected("}", info->token.string);
		        break;
			default:
				// assert: this should never happen
				expected("ERROR 404: ", info->token.string);
				break;
		}

		return JE_FALSE;
	}
}

//=====================================================================================
//	jeParse_DispatchCommandParser
//=====================================================================================
void jeParse_DispatchCommandParser(int argc, char* argv[SCRIPT_MAX_ARGS], scriptData *info)
{
	jeCommandTable* command;
	assert(info != NULL);

   for (command = info->command_table; ; command++) {
      if (!command->name) {
         jeParse_Error("unrecognized command '%s'", argv[0]);
         return;
      }

      if (stricmp(command->name, argv[0]) == 0) {
         if (command->is_ignored) {
         }
         else if (argc > command->max) {
            jeParse_Error("too many arguments for '%s'", argv[0]);
         }
         else if (argc < command->min) {
            jeParse_Error("too few arguments for '%s'", argv[0]);
         }
         else {
            command->Parse(argc, argv);
         }

         return;
      }
   }
}

//=====================================================================================
//	jeParse_ParseArguments
//=====================================================================================
jeBoolean jeParse_ParseArguments(int* pargc, char* pargv[SCRIPT_MAX_ARGS], scriptData *info)
{
	assert(info != NULL);

   *pargc = 1;
   strncpy(pargv[0], info->token.string, JE_PARSE_TOKEN_LEN);

   jeParse_Match(JE_PARSE_TOKEN_STRING, info);

   while (!info->token.is_newline) {
      if (*pargc < SCRIPT_MAX_ARGS) {
         strncpy(pargv[*pargc], info->token.string, JE_PARSE_TOKEN_LEN);
		 (*pargc)++;
      }

      if (!jeParse_Match(JE_PARSE_TOKEN_STRING, info)) return JE_FALSE;
   }

   return JE_TRUE;
}


typedef struct arg_s {
   char string[JE_PARSE_TOKEN_LEN+1];
} arg_t;


//=====================================================================================
//	jeParse_parse_command
//=====================================================================================
jeBoolean jeParse_parse_command(scriptData *info)
{
	int argc;
	arg_t argv_str[SCRIPT_MAX_ARGS];
	
	char* argv[SCRIPT_MAX_ARGS] = {
      argv_str[0].string,
      argv_str[1].string,
      argv_str[2].string,
      argv_str[3].string,
      argv_str[4].string,
      argv_str[5].string,
      argv_str[6].string,
      argv_str[7].string,
      argv_str[8].string,
      argv_str[9].string,
      argv_str[10].string,
	  argv_str[11].string,
      argv_str[12].string,
	  argv_str[13].string,
	  argv_str[14].string,
	  argv_str[15].string,
	  argv_str[16].string,
	  argv_str[17].string,
	  argv_str[18].string,
	  argv_str[19].string,
   };

	assert(info != NULL);

	if (jeParse_ParseArguments(&argc, argv, info)) {
      jeParse_DispatchCommandParser(argc, argv, info);
      return JE_TRUE;
   }
   else {
      return JE_FALSE;
   }
}

//=====================================================================================
//	initialize_shader
//=====================================================================================
/*jeBoolean initialize_shader(const char* name, scriptData *info)
{

	shader_t *shader;

	assert(info != NULL);
   
	shader = sh_alloc();
	
	if (shader == NULL) return JE_FALSE;

	memset(shader, 0, sizeof(shader_t));
	strcpy(shader->name, name);

	info->shader = shader;

   return JE_TRUE;
}*/

//=====================================================================================
//	parse_script
//=====================================================================================
jeBoolean parse_script(jeParse_ApproveFunc approve, scriptData *info)
{
	char name[JE_PARSE_TOKEN_LEN+1];

	assert(info != NULL);
   
   strncpy(name, info->token.string, JE_PARSE_TOKEN_LEN);
   name[JE_PARSE_TOKEN_LEN] = '\0';

   jeParse_Match(JE_PARSE_TOKEN_STRING, info);

   if (strlen(name) <= JE_PARSE_NAME_LEN) {
      if (approve && !approve(name)) return JE_FALSE;

      //if (!initialize_shader(name, info)) return JE_FALSE;

	  
   }
   else {
	   //jeParse_Error("shader name too long", name);
	   return JE_FALSE;
   }

   if (info->lookahead == '{')
   {
      jeParse_Match('{', info);
	  while (info->lookahead != '}')
	  {
         if (info->lookahead == '{')
		 {
            //if (!parse_shader_stage(info)) return JE_FALSE;
         }
         else if (info->lookahead == JE_PARSE_TOKEN_STRING)
		 {
            //if (!parse_shader_command(info)) return JE_FALSE;
         }
         else
		 {
            expected("global command", info->token.string);
            return JE_FALSE;
         }
      }

      jeParse_Match('}', info);
      //finalize_shader(info);
      return JE_TRUE;
   }
   else {
      expected("beginning of body", info->token.string);
      return JE_FALSE;
   }
}


//=====================================================================================
//	jeParse_Parse
//=====================================================================================
size_t jeParse_Parse(jeParse_CompileFunc compile, jeParse_ApproveFunc approve, scriptData *info)
{

   int count = 0;

   	assert(info != NULL);

	jeParse_BeginScan(info);
   info->parse_stop = JE_FALSE;

   info->lookahead  = jeParse_Scan(info);

   while (info->lookahead != JE_PARSE_TOKEN_EOF) {
      if (info->lookahead == JE_PARSE_TOKEN_STRING) {

		  if (parse_script(approve, info))
		  {
			  if (compile)
				  compile(info);

			  //jeParse_AllocKeep();
			  count++;
			  
			  if (info->parse_stop)
			  {
				  return count;
			  }
			  else
			  {
				  continue;
			  }

		  }

	  }
            
      else {
         expected("script name", info->token.string);
      }

      jeParse_Skip(info);
   }

   jeParse_Match(JE_PARSE_TOKEN_EOF, info);

   jeParse_EndScan(info);
   info->lookahead = 0;

   return count;
}

//=====================================================================================
//	jeParse_stop
//=====================================================================================
void jeParse_stop( scriptData *info)
{
	assert(info != NULL);

   info->parse_stop = JE_TRUE;
}
