/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Cyril Concolato
 *			Copyright (c) Telecom ParisTech 2004-2012
 *					All rights reserved
 *
 *  This file is part of GPAC / SVG Scene Graph Generator sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <gpac/tools.h>
#include <gpac/list.h>

static void start_ctx(char *ctx_name, FILE **ifce_c, FILE **fun_c)
{
	char szFile[GF_MAX_PATH];
	sprintf(szFile, "../../../src/jsmods/%s.c", ctx_name);
	FILE *res = gf_fopen(szFile, "wt");
	*ifce_c = res;
	*fun_c = gf_file_temp(NULL);

	fprintf(res, "/* !! AUTOGENERATED FILE - DO NOT MODIFY !! \n");
	fprintf(res, "\n%s implementation for QJS\n*/\n\n", ctx_name);
}


static void end_ctx(FILE *ifce_c, FILE *func_c, char *ctx_name)
{

	fprintf(ifce_c, "static const JSCFunctionListEntry %s_funcs[] =\n\{\n", ctx_name);
	gf_fseek(func_c, 0, SEEK_SET);
	while (!feof(func_c)) {
		char szLine[4001];
		szLine[0] = 0;
		gf_fgets(szLine, 4000, func_c);
		gf_fputs(szLine, ifce_c);
	}
	gf_fclose(func_c);
	fprintf(ifce_c, "};\n");
	gf_fclose(ifce_c);
	gf_free(ctx_name);

}

static void put_const(FILE *fun_defs, char *c_name, char *c_val)
{
	fprintf(fun_defs, "\tJS_PROP_INT32_DEF(\"%s\", %s, JS_PROP_CONFIGURABLE),\n", c_name, c_val);
}
static void put_fun(FILE *fun_defs, char *f_name)
{
	fprintf(fun_defs, "\tJS_CFUNC_DEF(\"%s\", 0, wgl_%s),\n", f_name, f_name);
}
static void put_prop(FILE *fun_defs, char *ctx_name, char *pname, Bool is_readonly)
{
	if (is_readonly) {
		fprintf(fun_defs, "\tJS_CGETSET_MAGIC_DEF(\"%s\", %s_getProperty, NULL, %s_PROP_%s),\n", pname, ctx_name, ctx_name, pname);
	} else {
		fprintf(fun_defs, "\tJS_CGETSET_MAGIC_DEF(\"%s\", %s_getProperty, %s_setProperty, %s_PROP_%s),\n", pname, ctx_name, ctx_name, ctx_name, pname);
	}
}

static const char *get_arg_type(char *arg_type, Bool *is_array)
{
	*is_array = GF_FALSE;
	if (!strcmp(arg_type, "GLenum")) return "u32";
	if (!strcmp(arg_type, "DOMString"))  return "const char *";
	if (!strcmp(arg_type, "DOMString?"))  return "const char *";
	if (!strcmp(arg_type, "GLuint"))  return "u32";
	if (!strcmp(arg_type, "GLint"))  return "s32";
	if (!strcmp(arg_type, "GLuint"))  return "u32";
	if (!strcmp(arg_type, "GLclampf"))  return "Float";
	if (!strcmp(arg_type, "GLfloat"))  return "Float";
	if (!strcmp(arg_type, "GLboolean"))  return "Bool";
	if (!strcmp(arg_type, "boolean"))  return "Bool";
	if (!strcmp(arg_type, "GLsizei"))  return "u32";
	if (!strcmp(arg_type, "GLbitfield"))  return "u32";
	if (!strcmp(arg_type, "GLshort"))  return "s16";
	if (!strcmp(arg_type, "GLushort"))  return "u16";
	if (!strcmp(arg_type, "GLsizeiptr"))  return "u64";
	if (!strcmp(arg_type, "GLintptr"))  return "u64";
	if (!strcmp(arg_type, "GLbyte"))  return "s8";
	if (!strcmp(arg_type, "GLubyte"))  return "u8";
	if (!strcmp(arg_type, "WebGLProgram?")) return "WebGLProgram";
	if (!strcmp(arg_type, "WebGLShader?")) return "WebGLShader";
	if (!strcmp(arg_type, "WebGLBuffer?")) return "WebGLBuffer";
	if (!strcmp(arg_type, "WebGLFramebuffer?")) return "WebGLFramebuffer";
	if (!strcmp(arg_type, "WebGLRenderbuffer?")) return "WebGLRenderbuffer";
	if (!strcmp(arg_type, "WebGLTexture?")) return "WebGLTexture";
	if (!strcmp(arg_type, "WebGLUniformLocation?")) return "WebGLUniformLocation";
	if (!strcmp(arg_type, "TexImageSource?")) return "TexImageSource";
	if (!strcmp(arg_type, "WebGLContextAttributes?")) return "WebGLContextAttributes";
	if (!strcmp(arg_type, "boolean?")) return "Bool";
	if (!strcmp(arg_type, "WebGLActiveInfo?")) return "JSValue";

	if (!strcmp(arg_type, "Float32Array")) {
		*is_array = GF_TRUE;
		return "Float *";
	}
	if (!strcmp(arg_type, "Int32Array")) {
		*is_array = GF_TRUE;
		return "s32 *";
	}
	if (!strcmp(arg_type, "sequence<DOMString>?")) {
		return "JSValue";
	}
	if (!strcmp(arg_type, "sequence<WebGLShader>?")) {
		return "JSValue";
	}
	if (!strcmp(arg_type, "ArrayBufferView")
		|| !strcmp(arg_type, "ArrayBufferView?")
		|| !strcmp(arg_type, "BufferDataSource?")
	) {
		*is_array = GF_TRUE;
		return "u8 *";
	}

	if (!strcmp(arg_type, "object?")
		|| !strcmp(arg_type, "WebGLShaderPrecisionFormat?")
		|| !strcmp(arg_type, "any")
		) {
		return NULL;
	}

	fprintf(stderr, "arg type %s not supported\n", arg_type);
	return "JSValue";
}

static void put_arg_get(FILE *ifce_c, char *fname, int idx, char *arg_type, char *arg_name)
{
	Bool is_array;
	const char *native_type = get_arg_type(arg_type, &is_array);
	if (!native_type) return;

	if (!strcmp(native_type, "Bool")) {
		fprintf(ifce_c, "\tWGL_GET_BOOL(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "s32")) {
		fprintf(ifce_c, "\tWGL_GET_S32(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "u32")) {
		fprintf(ifce_c, "\tWGL_GET_U32(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "u64")) {
		fprintf(ifce_c, "\tWGL_GET_U64(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "s64")) {
		fprintf(ifce_c, "\tWGL_GET_S64(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "s16")) {
		fprintf(ifce_c, "\tWGL_GET_S16(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "u16")) {
		fprintf(ifce_c, "\tWGL_GET_U16(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(arg_type, "GLclampf")) {
		fprintf(ifce_c, "\tWGL_GET_FLOAT_CLAMP(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "Float")) {
		fprintf(ifce_c, "\tWGL_GET_FLOAT(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "const char *")) {
		fprintf(ifce_c, "\tWGL_GET_STRING(%s, argv[%d]);\n", arg_name, idx);
	}
	else if (!strcmp(native_type, "WebGLProgram")
	 	|| !strcmp(native_type, "WebGLShader")
	 	|| !strcmp(native_type, "WebGLBuffer")
	 	|| !strcmp(native_type, "WebGLFramebuffer")
	 	|| !strcmp(native_type, "WebGLRenderbuffer")
	 	|| !strcmp(native_type, "WebGLTexture")
	 	|| !strcmp(native_type, "WebGLUniformLocation")
	) {
		fprintf(ifce_c, "\tWGL_GET_GLID(%s, argv[%d], %s_class_id);\n", arg_name, idx, native_type);
	}

	else if (!strcmp(arg_type, "BufferDataSource?")) {
		fprintf(ifce_c, "\tif(!JS_IsObject(argv[%d])) {\n", idx);
		fprintf(ifce_c, "\t\tWGL_GET_U32(%s_size, argv[%d]);\n", arg_name, idx);
		fprintf(ifce_c, "\t} else {\n");
		fprintf(ifce_c, "\t\t%s = wgl_GetArrayBuffer(ctx, &%s_size, argv[%d]);\n", arg_name, arg_name, idx);
		fprintf(ifce_c, "\t}\n");
	}
	else if (is_array && !strcmp(native_type, "u8 *")) {
		fprintf(ifce_c, "\tif(!JS_IsArray(ctx, argv[%d])) return js_throw_err(ctx, WGL_INVALID_VALUE);\n", idx);
		fprintf(ifce_c, "\t%s = wgl_GetArrayBuffer(ctx, &%s_size, argv[%d]);\n", arg_name, arg_name, idx);
	}
	else if (is_array && (!strcmp(native_type, "Float *") || !strcmp(native_type, "s32 *")) ) {
		u32 vlen = 1;
		if (strstr(fname, "2fv")) vlen = 2;
		else if (strstr(fname, "3fv")) vlen = 3;
		else if (strstr(fname, "4fv")) vlen = 4;

		if (!strcmp(native_type, "s32 *"))
			fprintf(ifce_c, "\tif (!WGL_LOAD_INT32_VEC(ctx, argv[%d], (s32 **) &%s, &%s_size, %d)) return js_throw_err(ctx, WGL_INVALID_VALUE);\n", idx, arg_name, arg_name, vlen);
		else {
			Bool is_matrix = GF_FALSE;
			if (strstr(fname, "Matrix")) is_matrix = GF_TRUE;
			if (strstr(fname, "vertexAttrib")) {
				fprintf(ifce_c, "\tif (!WGL_LOAD_FLOAT_VEC(ctx, argv[%d], (Float **) &%s, NULL, %d, %d)) return js_throw_err(ctx, WGL_INVALID_VALUE);\n", idx, arg_name, vlen, is_matrix);
			} else {
				fprintf(ifce_c, "\tif (!WGL_LOAD_FLOAT_VEC(ctx, argv[%d], (Float **) &%s, &%s_size, %d, %d)) return js_throw_err(ctx, WGL_INVALID_VALUE);\n", idx, arg_name, arg_name, vlen, is_matrix);
			}
		}
	}
	else {
		fprintf(stderr, "arg type %s not supported\n", arg_type);
	}

}

static void put_arg_decl(FILE *ifce_c, char *fname, char *arg_type, char *arg_name)
{
	Bool is_array = GF_FALSE;
	const char *native_type = get_arg_type(arg_type, &is_array);
	if (!native_type)
		return;
		
	if (is_array) {
		if (strstr(fname, "vertexAttrib1fv")) {
			fprintf(ifce_c, "\tFloat %s[1];\n", arg_name);
		} else if (strstr(fname, "vertexAttrib2fv")) {
			fprintf(ifce_c, "\tFloat %s[2];\n", arg_name);
		} else if (strstr(fname, "vertexAttrib3fv")) {
			fprintf(ifce_c, "\tFloat %s[3];\n", arg_name);
		} else if (strstr(fname, "vertexAttrib4fv")) {
			fprintf(ifce_c, "\tFloat %s[4];\n", arg_name);
		} else {
			fprintf(ifce_c, "\t%s %s = NULL;\n", native_type, arg_name);
			fprintf(ifce_c, "\tu32 %s_size = 0;\n", arg_name);
		}
	} else {
		if (strstr(arg_name, " *"))
			fprintf(ifce_c, "\t%s %s = NULL;\n", native_type, arg_name);
		else if (!strcmp(native_type, "JSValue"))
			fprintf(ifce_c, "\t%s %s = JS_UNDEFINED;\n", native_type, arg_name);
		else if (!strncmp(native_type, "WebGL", 5))
			fprintf(ifce_c, "\tGLuint %s = 0;\n", arg_name);
		else
			fprintf(ifce_c, "\t%s %s = 0;\n", native_type, arg_name);
	}
}
/*all functions that cannot be mapped directly due to to heavy syntax pacthing or our own hacking*/
static const char *not_autogen_funcs = "getContextAttributes,isContextLost,getSupportedExtensions,getExtension,getBufferParameter,getParameter,getFramebufferAttachmentParameter,getProgramParameter,getRenderbufferParameter,getShaderParameter,getShaderPrecisionFormat,getProgramInfoLog,getShaderParameter,getShaderInfoLog,getShaderSource,getTexParameter,getUniform,getVertexAttrib,getVertexAttribOffset,readPixels,shaderSource,texImage2D,texSubImage2D,useProgram,activeTexture,bindTexture,getUniformLocation,createTexture,bindFramebuffer,pixelStorei";

static void wrap_func(FILE *ifce_c, char *fname, char *fun_def)
{
	char *sep, *nsep;
	u32 len, i, nb_args;
	u32 first_char;
	u32 ret_type=0;
	Bool is_create=GF_FALSE;
	Bool is_delete_fun=GF_FALSE;
	char *ret_val=NULL, *exc_value=NULL, *arg_type[50], *arg_name[50];

	if (strstr(not_autogen_funcs, fname)) {
		fprintf(ifce_c, "\nstatic JSValue wgl_%s(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);\n", fname);
		return;
	}

	fprintf(ifce_c, "\nstatic JSValue wgl_%s(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)\n{\n", fname);

	nb_args = 0;
	memset(arg_type, 0, sizeof(char *)*50);
	memset(arg_name, 0, sizeof(char *)*50);

	sep = fun_def;
	while (sep[0]==' ') sep++;
	/*parse exception*/
	if (sep[0]=='[') {
		nsep = strchr(sep, ']');
		assert(nsep);
		nsep[0]=0;
		exc_value = gf_strdup(sep);
		nsep[0]=']';
		sep = nsep+1;
		while (sep[0]==' ') sep++;
	}
	/*parse return value*/
	nsep = strchr(sep, ' ');
	assert(nsep);
	nsep[0] = 0;
	ret_val = strdup(sep);
	sep = nsep+1;
	len = strlen(fname);
	assert(!strncmp(sep, fname, len) );
	sep += len+1;
	while (sep) {
		char *next_arg;
		nsep = strchr(sep, ',');
		if (!nsep) {
			nsep = strchr(sep, ')');
			if (!nsep) break;
		}
		nsep[0]=0;
		next_arg = nsep+1;

		if (nb_args>=50) {
			assert(0);
		}
		nsep = strchr(sep, ' ');
		if (!nsep)
			break;
		assert(nsep);
		nsep[0] = 0;
		arg_type[nb_args] = gf_strdup(sep);
		arg_name[nb_args] = gf_strdup(nsep+1);
		nb_args++;
		sep = next_arg;
		while (sep[0]==' ') sep++;
	}

	fprintf(ifce_c, "\tJSValue ret_val_js = JS_UNDEFINED;\n");

	for (i=0; i<nb_args; i++) {
		put_arg_decl(ifce_c, fname, arg_type[i], arg_name[i]);
	}
	fprintf(ifce_c, "\tWGL_CHECK_CONTEXT\n");

	if (nb_args) {
		fprintf(ifce_c, "\tif (argc<%d) return js_throw_err(ctx, WGL_INVALID_VALUE);\n", nb_args);
		for (i=0; i<nb_args; i++) {
			put_arg_get(ifce_c, fname, i, arg_type[i], arg_name[i]);
		}
	}

	/*hack a few funcs with different signatures*/
	if (!strcmp(fname, "getActiveAttrib")) {
		fprintf(ifce_c, "\tret_val_js = webgl_getActiveAttrib(ctx, program, index);\n\treturn ret_val_js;\n");
		goto fun_done;
	}
	if (!strcmp(fname, "getActiveUniform")) {
		fprintf(ifce_c, "\tret_val_js = webgl_getActiveUniform(ctx, program, index);\n\treturn ret_val_js;\n");
		goto fun_done;
	}
	if (!strcmp(fname, "getAttachedShaders")) {
		fprintf(ifce_c, "\tret_val_js = webgl_getAttachedShaders(ctx, JS_GetOpaque(this_val, WebGLRenderingContextBase_class_id), program);\n\treturn ret_val_js;\n");
		goto fun_done;
	}
	if (!strcmp(fname, "getBufferParameter")) {
		fprintf(ifce_c, "\tret_val_js = webgl_getBufferParameter(ctx, target, pname);\n\treturn ret_val_js;\n");
		goto fun_done;
	}

	if (ret_val && strcmp(ret_val, "void")) {
		if (!strcmp(fname, "createBuffer") || !strcmp(fname, "createFramebuffer") || !strcmp(fname, "createTexture") || !strcmp(fname, "createRenderbuffer")) {
			fprintf(ifce_c, "\tGF_WebGLObject *wglo;\n");
			is_create = GF_TRUE;
			ret_type = 2;

		} else if (!strcmp(fname, "createProgram") || !strcmp(fname, "createShader")) {
			fprintf(ifce_c, "\tGF_WebGLObject *wglo;\n");
			ret_type = 2;
		} else {
			put_arg_decl(ifce_c, fname, ret_val, "ret_val");
			ret_type = 1;
		}
	}

	first_char = fname[0];
	if ((first_char < 'A') || (first_char > 'Z')) {
		first_char += 'A' - 'a';
	}

	if (is_create) {
		fprintf(ifce_c, "\tGF_SAFEALLOC(wglo, GF_WebGLObject);\n");
		fprintf(ifce_c, "\tif (!wglo) return js_throw_err(ctx, WGL_OUT_OF_MEMORY);\n");
		fprintf(ifce_c, "\twglo->par_ctx = JS_GetOpaque(this_val, WebGLRenderingContextBase_class_id);\n");
		fprintf(ifce_c, "\tglGen%ss(1, &wglo->gl_id", fname+6);
	} else {
		if (!strcmp(fname, "deleteShader") || !strcmp(fname, "deleteProgram")) {
			is_delete_fun=GF_TRUE;
		}
		if (!strcmp(fname, "deleteBuffer") || !strcmp(fname, "deleteTexture") || !strcmp(fname, "deleteFramebuffer") || !strcmp(fname, "deleteRenderbuffer")) {
			fprintf(ifce_c, "\tgl%c%ss(1, &", first_char, fname+1);
			is_delete_fun=GF_TRUE;
		}
		else if (!strcmp(fname, "createProgram") || !strcmp(fname, "createShader")) {
			fprintf(ifce_c, "\tGF_SAFEALLOC(wglo, GF_WebGLObject);\n");
			fprintf(ifce_c, "\tif (!wglo) return js_throw_err(ctx, WGL_OUT_OF_MEMORY);\n");
			fprintf(ifce_c, "\twglo->par_ctx = JS_GetOpaque(this_val, WebGLRenderingContextBase_class_id);\n");
			fprintf(ifce_c, "\twglo->gl_id = gl%c%s(", first_char, fname+1);
		} else if (!strcmp(fname, "clearDepth") || !strcmp(fname, "depthRange")) {
			fprintf(ifce_c, "#if defined(GPAC_USE_GLES2)\n");
			fprintf(ifce_c, "\tgl%c%sf(\n", first_char, fname+1);
			fprintf(ifce_c, "#else\n");
			fprintf(ifce_c, "\tgl%c%s(\n", first_char, fname+1);
			fprintf(ifce_c, "#endif\n\t\t");
		} else {
			fprintf(ifce_c, "\t");
			if (ret_type==1) {
				fprintf(ifce_c, "ret_val = ");
			}
			if (!strncmp(fname, "uniform", 7)) {

				fprintf(ifce_c, "/*hack for GPAC textures, we create uniforms with loc -2 for such textures*/\n\tif ((s32) location>=0)\n\t\t");
			}
			fprintf(ifce_c, "gl%c%s(", first_char, fname+1);
		}
	}

	Bool prev_is_transpose = GF_FALSE;
	for (i=0; i<nb_args; i++) {
		Bool is_array;
		get_arg_type(arg_type[i], &is_array);

		if (!strcmp(arg_name[i], "transpose")) {
			fprintf(ifce_c, "%s_size, ", arg_name[i+1]);
			prev_is_transpose = GF_TRUE;
		}
		else if (prev_is_transpose) {
			is_array = GF_FALSE;
		}

		if (is_array && (
			!strcmp(fname, "bufferData") || !strcmp(fname, "bufferSubData")
			|| !strcmp(fname, "compressedTexImage2D") || !strcmp(fname, "compressedTexSubImage2D")
			|| !strncmp(fname, "uniform", 7)

		)) {
			is_array = GF_FALSE;
			fprintf(ifce_c, "%s_size, ", arg_name[i]);
		}
		if (!strcmp(arg_type[i], "GLintptr") && (!strcmp(fname, "vertexAttribPointer") || !strcmp(fname, "drawElements")) ) {
			fprintf(ifce_c, "(void *)");
		}

		fprintf(ifce_c, "%s", arg_name[i]);

		if (is_array) {
			if (strstr(fname, "vertexAttrib1fv")) {}
			else if (strstr(fname, "vertexAttrib2fv")) {}
			else if (strstr(fname, "vertexAttrib3fv")) {}
			else if (strstr(fname, "vertexAttrib4fv")) {}
			else {
				fprintf(ifce_c, ", %s_size", arg_name[i]);
			}
		}
		if (i+1<nb_args)
			fprintf(ifce_c, ", ");
	}

	fprintf(ifce_c, ");\n");
	if (is_delete_fun) {
		Bool is_array;
		const char *nat_type = get_arg_type(arg_type[0], &is_array);
		fprintf(ifce_c, "\t{\n\tGF_WebGLObject *glo = JS_GetOpaque(argv[0], %s_class_id);\n\tif (glo) {\n\tglo->gl_id=0;\n", nat_type);
		fprintf(ifce_c, "\tJS_FreeValue(ctx, glo->obj);\n\tglo->obj = JS_UNDEFINED;\n\tgf_list_del_item(glo->par_ctx->all_objects, glo);\n");
		fprintf(ifce_c, "\t}\n\t}\n");
	}

	if (ret_type==1) {

		if (!strcmp(ret_val, "GLenum") || !strcmp(ret_val, "GLint")) {
			fprintf(ifce_c, "\tret_val_js = JS_NewInt32(ctx, ret_val);\n");
		} else if (!strcmp(ret_val, "GLboolean") ) {
			fprintf(ifce_c, "\tret_val_js = JS_NewBool(ctx, ret_val);\n");
		} else if (!strcmp(ret_val, "WebGLProgram") ) {
			fprintf(ifce_c, "\tret_val_js = JS_NewObjectClass(ctx, WebGLProgram_class_id);\n");
			fprintf(ifce_c, "\tJS_SetOpaque(ret_val_js, wglo);\n");
		} else {
			fprintf(stderr, "retval %s not supported in %s\n", ret_val, fname);
		}
	} else if (ret_type==2) {
		Bool is_array;
		const char *native_type = get_arg_type(ret_val, &is_array);
		fprintf(ifce_c, "\tret_val_js = JS_NewObjectClass(ctx, %s_class_id);\n", native_type);
		fprintf(ifce_c, "\tJS_SetOpaque(ret_val_js, wglo);\n");
		fprintf(ifce_c, "\twglo->obj = JS_DupValue(ctx, ret_val_js);\n");
		fprintf(ifce_c, "\twglo->class_id = %s_class_id;\n", native_type);
		fprintf(ifce_c, "\tgf_list_add(wglo->par_ctx->all_objects, wglo);\n");
	}

	for (i=0; i<nb_args; i++) {
		Bool is_array;
		const char *native_type = get_arg_type(arg_type[i], &is_array);
		if (!native_type) continue;
		if (!strcmp(native_type, "const char *")) {
			fprintf(ifce_c, "\tJS_FreeCString(ctx, %s);\n", arg_name[i]);
		}

		if (is_array && strcmp(native_type, "u8 *")) {
			if (strstr(fname, "vertexAttrib1fv")) {
			} else if (strstr(fname, "vertexAttrib2fv")) {
			} else if (strstr(fname, "vertexAttrib3fv")) {
			} else if (strstr(fname, "vertexAttrib4fv")) {
			} else {
				fprintf(ifce_c, "\tif (%s) gf_free(%s);\n", arg_name[i], arg_name[i]);
			}
		}
	}

	fprintf(ifce_c, "\treturn ret_val_js;\n");

fun_done:
	while (nb_args>1) {
		gf_free(arg_type[nb_args-1]);
		gf_free(arg_name[nb_args-1]);
		nb_args--;
	}
	gf_free(ret_val);
	fprintf(ifce_c, "}\n");
}


static void load_idl(const char *file)
{
	Bool in_ctxbase = GF_FALSE;
	char szLine[4001], *sep;
	char *curCtx = NULL;
	u32 len;
	FILE * idl = gf_fopen(file, "rt");
	FILE *ifce_c=NULL;
	FILE *fun_defs=NULL;

	while (!feof(idl)) {
		gf_fgets(szLine, 4000, idl);
		if (strstr(szLine, "implements"))
			continue;
		len = strlen(szLine);
		while (len && strchr(" \n", szLine[len])) {
			szLine[len] = 0;
			len--;
		}
		if (!len) continue;

		if (!in_ctxbase) {
			if (!strstr(szLine, "RenderingContextBase"))
				continue;
			in_ctxbase = GF_TRUE;
			sep = strchr(szLine, ' ');
			assert(sep);
			curCtx = gf_strdup(sep+1);
			start_ctx(curCtx, &ifce_c, &fun_defs);
			continue;
		} else {
			if (strstr(szLine, "};")){
				in_ctxbase = GF_FALSE;
				end_ctx(ifce_c, fun_defs, curCtx);
				ifce_c = NULL;
				continue;
			}
		}

		if (strstr(szLine, "/*")) continue;
		sep = strstr(szLine, "const ");
		if (sep) {
			char *cname, *nsep;
			sep = strchr(sep+7, ' ');
			assert(sep);
			sep++;
			nsep = strchr(sep, ' ');
			assert(nsep);
			nsep[0] = 0;
			cname = gf_strdup(sep);
			sep = strchr(nsep+1, '=');
			assert(sep);
			sep++;
			while (sep[0]==' ') sep++;
			nsep = strchr(sep, ';');
			assert(nsep);
			nsep[0]=0;
			put_const(fun_defs, cname, sep);
			gf_free(cname);
			continue;
		}
		sep = strstr(szLine, "typedef");
		if (sep) continue;

		sep = strstr(szLine, "attribute");
		if (sep) {
			char *nsep;
			Bool is_readonly = GF_FALSE;
			sep = szLine;
			while (sep[0]==' ') sep++;
			if (!strncmp(sep, "readonly", 8)) {
				is_readonly = GF_TRUE;
				sep+=9;
			}
			sep += 10;
			sep = strchr(sep, ' ');
			assert(sep);
			sep+=1;
			nsep = strchr(sep, ';');
			assert(nsep);
			nsep[0] = 0;
			put_prop(fun_defs, curCtx, sep, is_readonly);
			continue;
		}
		/*double type declaration for glXXXifv*/
		if (strstr(szLine, ", sequence<"))
			continue;
		/*double type declaration for bufferData*/
		if (strstr(szLine, ", GLsizeiptr size"))
			continue;
		/*double type declaration for texImage2D and texSubImage2D*/
		if (strstr(szLine, ", TexImageSource"))
			continue;

		sep = strstr(szLine, "(");
		/*this is a function*/
		if (sep) {
			char *nsep, *fname;
			sep = strchr(szLine, ']');
			if (sep) sep+=1;
			else sep = szLine;
			while (sep[0] == ' ') sep++;
			sep = strchr(sep, ' ');
			assert(sep);
			sep++;
			nsep = strchr(sep, '(');
			assert(nsep);
			nsep[0] = 0;
			fname = gf_strdup(sep);
			put_fun(fun_defs, sep);
			nsep[0] = '(';

			wrap_func(ifce_c, fname, szLine);
			gf_free(fname);
			continue;
		}
		fprintf(stderr, "instruction %s not handled\n", szLine);
	}


	gf_fclose(idl);
}

int main(int argc, char **argv)
{
	u32 i=0;
	for (i=1; i<argc; i++) {
		load_idl(argv[i]);
	}

	return 0;
}


