var CodingModule = //do not change
{
	name: "Code",
	bigicon: "imgs/tabicon-code.png",

	show_sceneview: true, //3d view
	show_panel: false, //side panel

	is_sceneview_visible: true, 

	APIs: {}, //here you can register function calls of the API
	windows: [], //external windows

	component_help_url: "http://webglstudio.org/doc/litescene/classes/LS.Components.",

	init: function()
	{
		this.tab = LiteGUI.main_tabs.addTab( this.name, {
			id:"codingtab",
			bigicon: this.bigicon,
			size: "full", 
			callback: function(tab) {
				CodingModule.show3DWindow( CodingModule.show_sceneview );
				CodingModule.showSidePanel( CodingModule.show_panel );
				CodingModule.coding_tabs_widget.refresh();
			},
			callback_canopen: function(){
				//avoid opening the tab if it is in another window
				if(CodingModule.external_window)
					return false;
			},
			callback_leave: function() {
				RenderModule.appendViewportTo(null);
				//CodingModule.assignCurrentCode();
			},
			module: this //used to catch keyboard events
		});

		this.root = LiteGUI.main_tabs.getTab(this.name).content;

		//tabs for every file
		//register some APIs used for autocompletion
		
		//this.registerAPI("glsl", ["uniform","varying","sampler2D","samplerCube"] );
		this.registerAPI("glsl", ["texture2D","textureCube","radians","degrees","sin","cos","tan","asin","acos","atan","pow","exp","log","exp2","length"] );
		this.registerAPI("glsl", ["IN.color","IN.vertex","IN.normal","IN.uv","IN.uv1","IN.camPos","IN.viewDir","IN.worldPos","IN.worldNormal","IN.screenPos"] );
		this.registerAPI("glsl", ["o.Albedo","o.Normal","o.Emission","o.Specular","o.Gloss","o.Alpha","o.Reflectivity"] );

		LiteGUI.menubar.add("Window/Coding Panel", { callback: function(){ CodingTabsWidget.createDialog(); }});
		LiteGUI.menubar.add("Actions/Catch Exceptions", { type: "checkbox", instance: LS, property: "catch_exceptions" });
		LiteGUI.menubar.add("Actions/Reload scripts", { callback: function(){ CodingModule.reloadProjectScripts(); }});

		LiteGUI.menubar.add("Help/Coding/LS/Guides", {  callback: function(){ window.open("https://github.com/jagenjo/litescene.js/tree/master/guides#guide-to-develop-for-litescene","_blank"); }});
		LiteGUI.menubar.add("Help/Coding/LS/API", {  callback: function(){ window.open("http://webglstudio.org/doc/litescene/","_blank"); }});
		LiteGUI.menubar.add("Help/Coding/LiteGL/Guides", {  callback: function(){ window.open("https://github.com/jagenjo/litegl.js/tree/master/guides","_blank"); }});
		LiteGUI.menubar.add("Help/Coding/LiteGL/API", {  callback: function(){ window.open("http://webglstudio.org/doc/litegl/","_blank"); }});

		var coding_area = this.coding_area = new LiteGUI.Area({ id: "codearea", height: "100%"});
		this.root.appendChild( coding_area.root );
		coding_area.split("horizontal",[null,"50%"],true);

		var left_area = coding_area.getSection(0);
		left_area.split("vertical",[null,"25%"],true);

		this.coding_3D_area = left_area.getSection(0).content;
		this.console_area = left_area.getSection(1).content;

		//CONSOLE
		if(window.ConsoleWidget)
		{
			this.console_widget = new ConsoleWidget();
			this.console_area.appendChild( this.console_widget.root );
		}

		//console._log = console.log;
		//console.log = this.onConsoleLog.bind(this);

		//CODING
		var coding_tabs_widget = this.coding_tabs_widget = new CodingTabsWidget();
		coding_tabs_widget.is_master_editor = true;
		coding_area.getSection(1).add( coding_tabs_widget );
		//coding_tabs_widget.onNewTab();

		LEvent.bind( LS, "code_error", this.onCodeError, this );

		LS.catch_exceptions = true;
	},

	reloadProjectScripts: function()
	{
		LS.GlobalScene.loadScripts( null, function(){
			NotifyModule.show("Scripts reloaded");
			EditorModule.refreshAttributes();
		}, function(err, url){
			 LiteGUI.alert("Error loading scripts, not found: " + url );
		},true);
	},

	//registers a coding API (help, links to wiki, autocompletion, etc)
	registerAPI: function( lang, funcs )
	{
		var API = this.APIs[lang];
		if( !this.APIs[lang] )
			API = this.APIs[lang] = {};

		for(var i in funcs)
			API[ funcs[i] ] = true;
	},

	//open coding tab
	openTab: function()
	{
		LiteGUI.main_tabs.selectTab( this.name );
		this.show3DWindow( this.show_sceneview );
	},

	//close coding tab ( back to scene view )
	closeTab: function()
	{
		LiteGUI.main_tabs.selectTab( RenderModule.name );
	},

	//switch coding tab
	editInstanceCode: function( instance, options, open_tab )
	{
		if(!instance)
			return;
		if(open_tab)
			this.openTab();
		this.coding_tabs_widget.editInstanceCode( instance, options );
	},

	closeInstanceTab: function( instance, options )
	{
		return this.coding_tabs_widget.closeInstanceTab( instance, options );
	},

	//
	onNewScript: function( node, type )
	{
		type = type || "Script";
		node = node || SelectionModule.getSelectedNode();
		if(!node)
			node = LS.GlobalScene.root;

		if(type == "Script")
		{
			var component = new LS.Components.Script();
			node.addComponent( component );
			this.editInstanceCode( component, { id: component.uid, title: node.id, lang: "javascript", path: component.uid, help: LS.Components.Script.coding_help });
			this.openTab();
		} 
		else if (type == "ScriptFromFile")
		{
			var component = new LS.Components.ScriptFromFile();
			node.addComponent( component );
		}
		else if (type == "Global")
		{
			LiteGUI.alert("TO DO");
		}
	},

	onShowBreakpointDialog: function( component )
	{
		var context = component.getContext();
		if(!context)
		{
			LiteGUI.alert("Script without context");
			return;
		}

		var dialog = new LiteGUI.Dialog( { title: "Select breakpoints", close: true, width: 200, height: 410, resizable: true, scroll: true, draggable: true});
		var widgets = new LiteGUI.Inspector({height: "100%"});
		dialog.add( widgets );
		for(var i in context)
		{
			if( !isFunction( context[i] ) || !context.hasOwnProperty(i) )
				continue;
			widgets.addCheckbox(i,component.hasBreakpoint(i),{ method:i, name_width: 150,callback: function(v){ component.setBreakpoint( this.options.method,v); }});
		}

		dialog.show();
	},

	//used to extract editor options of a given instance
	extractOptionsFromInstance: function( instance, options )
	{
		if(!instance)
		{
			console.error("instance cannot be null");
			return;
		}

		options = options || {};

		//compute id
		var fullpath = instance.fullpath || instance.filename; //for resources
		var uid = instance.uid || instance.name; //for components
		var id = options.id || fullpath || uid;
		options.id = id;

		if(fullpath)
			fullpath = LS.RM.cleanFullpath(fullpath);

		//compute title
		var title = options.title;
		if(!title)
		{
			if(fullpath) //resources
				title = LS.RM.getFilename( fullpath );
			if(instance.getComponentTitle) //scripts
				title = instance.getComponentTitle();
		}
		options.title = title || "Script";

		//compute lang
		var lang = options.lang;
		if( !lang )
		{
			if( instance.constructor.is_material || instance.constructor == LS.ShaderCode ) 
				lang = "glsl";
			if( fullpath )
			{
				var ext = LS.RM.getExtension(fullpath);
				if( ext == "js" )
					lang = "javascript";
				else if( ext == "txt" )
					lang = "text";
				else
					lang = ext;
			}
		}
		options.lang = lang || "javascript";

		//compute type
		if(instance.constructor.is_resource)
			options.type = LS.TYPES.RESOURCE;
		else if(instance.constructor.is_component)
			options.type = LS.TYPES.COMPONENT;
		else if(instance.constructor.is_material)
			options.type = LS.TYPES.MATERIAL;

		return options;
	},

	//finds instance from options using id and type
	findInstance: function( options, callback )
	{
		var id = options.id;
		if(!id)
		{
			console.warn("findInstance options without id");
			return null;
		}

		//get instance from options
		if(options.type == LS.TYPES.RESOURCE)
		{
			if(LS.RM.resources[ id ])
				return LS.RM.resources[ id ];
			LS.RM.load( id, null, function(res){
				if(callback)
					callback( res, options );
			});
			return null;
		}
		else if(options.type == LS.TYPES.COMPONENT)
		{
			var comp = LS.GlobalScene.findComponentByUId( id );
			if(callback)
				callback( comp, options );
			return comp;
		}
		else
			console.warn("Cannot find code instance: ",id );
		return null;
	},

	showCodingHelp: function( options )
	{
		var help = options.help;
		if(!help)
		{
			if(options.type === LS.TYPES.COMPONENT)
				window.open( "https://github.com/jagenjo/litescene.js/blob/master/guides/scripting.md"	);
			else if(options.type === LS.TYPES.RESOURCE)
			{
				if(options.lang == "glsl")
				{
					if(LS.ShaderCode.help_url)
						window.open( LS.ShaderCode.help_url	);
				}
				else
					window.open( "https://github.com/jagenjo/litescene.js/blob/master/guides/scripting.md"	);
			}
			return;
		}

		var help_options = {
			content: "<pre style='padding:10px; height: 200px; overflow: auto'>" + help + "</pre>",
			title: "Help",
			draggable: true,
			closable: true,
			width: 400,
			height: 260
		};

		var dialog = new LiteGUI.Dialog("info_message",help_options);
		dialog.addButton("Close",{ close: true });
		dialog.show();
	},

	onCodeError: function( e,err )
	{
		//if it is an script of ours, open in code editor
		if(!err.script)
			return;

		var tab = this.coding_tabs_widget.editInstanceCode( err.script );
		if(!tab || !tab.pad)
			return;

		this.openTab();
		tab.pad.markError( err.line, err.msg );

		var msg = String(err.msg || err.message || err.error);

		var elem = InterfaceModule.setStatusBar("<span class='link'>Error in code: " + msg + "</span>", "error" );

		if(err.script)
			elem.querySelector(".link").addEventListener("click", function(e){
				CodingModule.editInstanceCode(err.script,null,true);
			});
	},

	//shows the side 3d window
	show3DWindow: function(v)
	{
		if(v === undefined)
			v = !this.is_sceneview_visible;
		this.is_sceneview_visible = v;
		this.show_sceneview = v;

		if(v)
		{
			RenderModule.appendViewportTo( this.coding_area.sections[0].content );
			this.coding_area.showSection(0);
		}
		else
		{
			RenderModule.appendViewportTo(null);
			this.coding_area.hideSection(0);
		}
	},

	showSidePanel: function(v)
	{
		InterfaceModule.setSidePanelVisibility(v);
		this.show_panel = InterfaceModule.side_panel_visibility;

	},

	onKeyDown: function(e)
	{
		//console.log(e);
		if(e.keyCode == 117) //F6
		{
			localStorage.setItem("_refresh_scene", JSON.stringify( LS.GlobalScene.serialize() ) );
			location.reload();
			e.preventDefault();		
		}

		//this key event must be redirected when the 3D area is selected
		if( this._block_event )
			return;
		this._block_event = true;
		var coding = this.coding_tabs_widget.root.querySelector(".CodeMirror");
		if(coding)
			coding.dispatchEvent( new e.constructor( e.type, e ) );
		this._block_event = false;
	},

	onUnload: function()
	{
		if(this.external_window)
			this.external_window.close();
	},

	//get the current state
	getState: function()
	{
		return this.coding_tabs_widget.getState();
	},

	//get the current state
	setState: function(o)
	{
		return this.coding_tabs_widget.setState(o);
	},

	onConsoleLog: function(a,b)
	{
		console._log.apply( console, arguments );

		var elem = document.createElement("div");
		elem.className = "msg";
		a = String(a);
		if( a.indexOf("%c") != -1)
		{
			a = a.split("%c").join("");
			elem.setAttribute("style",b);
		}
		elem.innerText = a;
		this.console_container.appendChild( elem );
		this.console_container.scrollTop = 1000000;
		if( this.console_container.childNodes.length > 500 )
			this.console_container.removeChild( this.console_container.childNodes[0] );
	}
};

CORE.registerModule( CodingModule );

/* editors **************************************/

LS.Components.Script.prototype.getExtraTitleCode = LS.Components.ScriptFromFile.prototype.getExtraTitleCode = function()
{
	return "<span class='icon script-context-icon'><img src='" + EditorModule.icons_path + LS.Script.icon + "'/></span>";
}

LS.Components.Script["@inspector"] = function( component, inspector )
{
	var context_locator = component.getLocator() + "/context";
	var context = component.getContext();

	var icon = this.current_section.querySelector(".script-context-icon");
	icon.addEventListener("dragstart", function(event) { 
		var context_locator = component.getLocator() + "/context";
		if(!event.shiftKey)
			context_locator = component.root.name + "/" + component.uid + "/context";
		event.dataTransfer.setData("uid", context_locator );
		event.dataTransfer.setData("locator", context_locator );
		event.dataTransfer.setData("type", "object");
		event.dataTransfer.setData("node_uid", component.root.uid);
		if( component.setDragData )
			component.setDragData( event );
	});

	inspector.widgets_per_row = 2;
	inspector.addButton(null,"Edit Code", { width: "100% - 30px", callback: function() {
		CodingModule.openTab();
		var path = component.uid;
		CodingModule.editInstanceCode( component );
	}});
	inspector.addButton(null, "<img src='imgs/mini-icon-rotator.png'/>", { width: 30, callback: function() {
		component.reload();
	}});
	inspector.widgets_per_row = 1;

	if(context)
	{
		if(context.onInspector)
			context.onInspector( inspector );
		else
			this.showObjectFields( context, inspector );
	}
}

LS.Components.ScriptFromFile["@inspector"] = function( component, inspector )
{
	var context_locator = component.getLocator() + "/context";
	var context = component.getContext();

	var icon = this.current_section.querySelector(".script-context-icon");
	icon.addEventListener("dragstart", function(event) { 
		var context_locator = component.getLocator() + "/context";
		if(!event.shiftKey)
			context_locator = component.root.name + "/_" + component.name + "/context";
		event.dataTransfer.setData("uid", context_locator );
		event.dataTransfer.setData("locator", context_locator );
		event.dataTransfer.setData("type", "object");
		event.dataTransfer.setData("node_uid", component.root.uid);
		if( component.setDragData )
			component.setDragData( event );
	});

	inspector.widgets_per_row = 3;
	inspector.addResource( "Filename", component.filename, { name_width: 70, width: "100% - 130px", category: "Script", align:"right", callback: function(v) { 
		component.filename = v;
	}});

	inspector.addButton(null,"Edit Code", { width: 100, callback: function() {
		var path = component.uid;
		if(!component.filename)
		{
			/*
			LiteGUI.prompt("Choose a filename", function(filename){
				if(!filename)
					return;
				CodingModule.openTab();
				var res = new LS.Resource();
				var extension = LS.RM.getExtension(filename);
				if(extension != "js")
					filename = filename + ".js";
				component.filename = filename;
				LS.RM.registerResource(filename,res);
				CodingModule.editInstanceCode( res );
			});
			*/
			DriveModule.showCreateScriptDialog({filename: "script.js"}, function(resource){
				if(!resource)
					return;
				CodingModule.openTab();
				var fullpath = resource.fullpath || resource.filename;
				component.filename = fullpath;
				CodingModule.editInstanceCode( resource );
			});
			return;
		}

		CodingModule.openTab();
		var res = LS.ResourcesManager.load( component.filename, null, function(res){
			CodingModule.editInstanceCode( res );
		});
	}});

	inspector.addButton(null, "<img src='imgs/mini-icon-rotator.png'/>", { title: "reload", width: 30, callback: function() {
		component.reload();
	}});

	inspector.widgets_per_row = 1;

	var context = component.getContext();
	if(context)
	{
		if(context.onInspector)
			context.onInspector( inspector );
		else
			this.showObjectFields(context, inspector );
	}
}

LS.Components.Script.prototype.onComponentInfo = function( widgets )
{
	var component = this;

	var locator_widget = widgets.addString("Context Locator", this.getLocator() + "/context", { disabled: true } );

	var values = [""];
	var context = this.getContext();
	if(context)
	{
		for(var i in context)
		{
			var f = context[i];
			if( typeof(f) != "function")
				continue;
			values.push(i);
		}
		widgets.addCombo("Functions", "", { values: values, callback: function(v){ 
			//TODO
		}});
	}
}

//to write a tiny code snippet
LiteGUI.Inspector.prototype.addCode = function( name, value, options )
{
	options = options || {};
	value = value || "";
	var that = this;
	this.values[ name ] = value;

	var element = null;

	var instance = options.instance || {};
	var uid = instance.uid || ("code_" + this.tab_index);
	var instance_settings = { 
		id: uid,
		path: instance.uid,
		title: uid
	};
	//getCode: function(){ return instance[name];},
	//setCode: function(v){ instance[name] = v;}

	//hardcoded
	var freeze_event = false;

	if(!options.instance) //some generic code in a string, embed editor
	{
		if(!options.name_width)
			options.name_width = "20%";

		if(typeof(CodeMirror) !== undefined)
		{
			var immediate = false;
			element = this.createWidget(null,"<div class='wsectiontitle'>"+(name||"")+"</div><div class='code-container' style='min-height: 100px; width: 100%; overflow: auto'></div>", options);
			element.editor = CodeMirror( element.querySelector(".code-container"), {
				value: value,
				mode:  options.code_lang || "javascript",
				theme: "blackboard",
				lineWrapping: true,
				gutter: true,
				tabSize: 2,
				lineNumbers: true,
				matchBrackets: true,
				styleActiveLine: true,
				extraKeys: {
						"Ctrl-Enter": "assign"
					},
				onCursorActivity: function(e) { //key pressed
					//that.editor.matchHighlight("CodeMirror-matchhighlight");
				}
			});
			element.editor.getScrollerElement().style.minHeight = "100px";
			element.editor.on( immediate ? "change" : "blur" , function(editor){
				//any change in the code
				var value = editor.getValue();
				if(!freeze_event)
					LiteGUI.Inspector.onWidgetChange.call( that, element, name, value, options );
			});

			element.setValue = function(v, skip_event)
			{
				var old = freeze_event;
				freeze_event = skip_event;
				this.editor.setValue(v);
				freeze_event = old;
			}
			element.style.overflow = "auto"; //force scroll
		}
		else
		{
			element = this.createWidget(name,"<textarea style='min-height: 100px;background-color: black; font-style: Courier; color: #eee;' tabIndex='"+ this.tab_index + "'>"+(value||"")+"</textarea>", options);
			var textarea = element.querySelector("textarea");
			textarea.addEventListener("change",function(e){
				var value = this.value;
				LiteGUI.Inspector.onWidgetChange.call( that, element, name, value, options );
			});
			element.setValue = function(v, skip_event)
			{
				var old = freeze_event;
				freeze_event = skip_event;
				textarea.value = v;
				freeze_event = old;
			}
		}
	}
	else if(!options.allow_inline)
	{
		var text = "Edit Code";
		element = this.createWidget(name,"<button class='single' tabIndex='"+ this.tab_index + "'>"+text+"</button>", options);
		var button = element.querySelector("button");
		button.addEventListener("click", function() {
			CodingModule.openTab();
			CodingModule.editInstanceCode( instance, instance_settings );
		});

		element.setValue = function(v, skip_event)
		{
			console.warn("this shouldnt be called");
		}
	}
	else //embeded editor
	{
		element = inspector.addContainer( null, { height: 300} );

		var codepad = new CodingPadWidget();
		element.appendChild( codepad.root );
		codepad.editInstanceCode( instance, instance_settings );
		codepad.top_widgets.addButton(null,"In Editor",{ callback: function() { 
			if(options.callback_button)
				options.callback_button();
			inspector.refresh();
			CodingModule.openTab();
			CodingModule.editInstanceCode( instance, instance_settings );
		}});

		element.setValue = function(v, skip_event)
		{
			console.warn("this shouldnt be called");
		}
	}

	this.tab_index += 1;
	this.append( element );

	//codemirror
	if(element.editor)
		element.editor.refresh();

	return element;
}

LiteGUI.Inspector.widget_constructors["code"] = "addCode";


LS.Components.Script.actions["breakpoint_on_call"] = { 
	title: "Breakpoint on call", 
	callback: function() { 
		if(!this._root)
		{
			console.warn("Script is not attached to a node?");
			return;
		}
		CodingModule.onShowBreakpointDialog(this);
	}
};



LS.Components.Script.actions["convert_to_script"] = { 
	title: "Convert to ScriptFromFile", 
	callback: function() { 
		if(!this._root)
		{
			console.warn("Script is not attached to a node?");
			return;
		}

		var node = this._root;
		var info = this.serialize();
		var code = this.getCode();
		delete info.code;
		var compo = this;

		LiteGUI.prompt("Choose a filename for the source file", function(v){

			var resource = new LS.Resource();
			resource.setData( code );
			LS.RM.registerResource( v, resource );
			info.filename = resource.filename;

			var index = node.getIndexOfComponent(compo);
			node.removeComponent(compo);

			var script = new LS.Components.ScriptFromFile();
			node.addComponent(script, index);
			script.configure(info);
			EditorModule.refreshAttributes();

			console.log("Script converted to ScriptFromFile");
		},{ value:"unnamed_code.js" });
	}
};

LS.Components.ScriptFromFile.actions = {}; //do not share with script
LS.Components.ScriptFromFile.actions["convert_to_script"] = { 
	title: "Convert to Script", 
	callback: function() { 
		if(!this._root)
		{
			console.warn("Script is not attached to a node?");
			return;
		}

		var node = this._root;
		var info = this.serialize();
		delete info.filename;
		info.code = this.getCode();
		var script = new LS.Components.Script();
		var index = node.getIndexOfComponent(this);
		node.removeComponent(this);
		node.addComponent(script, index);
		script.configure(info);
		EditorModule.refreshAttributes();
		console.log("ScriptFromFile converted to Script");
	}
};

LS.Components.ScriptFromFile.actions["breakpoint_on_call"] = LS.Components.Script.actions["breakpoint_on_call"];

//Example code for a shader (used in editor) **************************************************
LS.ShaderCode.examples = {};

LS.ShaderCode.examples.fx = "\n\
\\fx.fs\n\
	precision highp float;\n\
	\n\
	uniform float u_time;\n\
	uniform vec4 u_viewport;\n\
	uniform sampler2D u_texture;\n\
	varying vec2 v_coord;\n\
	void main() {\n\
		gl_FragColor = texture2D( u_texture, v_coord );\n\
	}\n\
";

LS.ShaderCode.examples.flat = "\n\
\n\
\\js\n\
\n\
\\color.vs\n\
\n\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec3 a_normal;\n\
attribute vec2 a_coord;\n\
\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
\n\
//matrices\n\
uniform mat4 u_model;\n\
uniform mat4 u_normal_model;\n\
uniform mat4 u_view;\n\
uniform mat4 u_viewprojection;\n\
\n\
//globals\n\
uniform float u_time;\n\
uniform vec4 u_viewport;\n\
uniform float u_point_size;\n\
\n\
//camera\n\
uniform vec3 u_camera_eye;\n\
void main() {\n\
	\n\
	vec4 vertex4 = vec4(a_vertex,1.0);\n\
	v_normal = a_normal;\n\
	v_uvs = a_coord;\n\
	\n\
	//vertex\n\
	v_pos = (u_model * vertex4).xyz;\n\
	//normal\n\
	v_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\n\
	gl_Position = u_viewprojection * vec4(v_pos,1.0);\n\
}\n\
\n\
\\color.fs\n\
\n\
precision mediump float;\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
\n\
//material\n\
uniform vec4 u_material_color; //color and alpha\n\
void main() {\n\
	vec4 color = u_material_color;\n\
	gl_FragColor = color;\n\
}\n\
\n\
";

LS.ShaderCode.examples.textured = "\n\
\n\
\\js\n\
this.createSampler(\"Texture\",\"u_texture\");\n\
\n\
\\color.vs\n\
\n\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec3 a_normal;\n\
attribute vec2 a_coord;\n\
\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
\n\
//matrices\n\
uniform mat4 u_model;\n\
uniform mat4 u_normal_model;\n\
uniform mat4 u_view;\n\
uniform mat4 u_viewprojection;\n\
\n\
//globals\n\
uniform float u_time;\n\
uniform vec4 u_viewport;\n\
uniform float u_point_size;\n\
\n\
//camera\n\
uniform vec3 u_camera_eye;\n\
void main() {\n\
	\n\
	vec4 vertex4 = vec4(a_vertex,1.0);\n\
	v_normal = a_normal;\n\
	v_uvs = a_coord;\n\
	\n\
	//vertex\n\
	v_pos = (u_model * vertex4).xyz;\n\
	//normal\n\
	v_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\n\
	gl_Position = u_viewprojection * vec4(v_pos,1.0);\n\
}\n\
\n\
\\color.fs\n\
\n\
#ifdef DRAW_BUFFERS\n\
	#extension GL_EXT_draw_buffers : require \n\
#endif\n\
precision mediump float;\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
//globals\n\
uniform sampler2D u_texture;\n\
\n\
//material\n\
uniform vec4 u_material_color; //color and alpha\n\
void main() {\n\
	vec4 color = u_material_color * texture2D( u_texture, v_uvs );\n\
#ifdef DRAW_BUFFERS\n\
        gl_FragData[0] = color;\n\
    #else\n\
        gl_FragColor = color;\n\
    #endif}\n\
\n\
";


LS.ShaderCode.examples.fake_light = "\n\
\n\
\\js\n\
//define exported uniforms from the shader (name, uniform, widget)\n\
this.createUniform(\"Number\",\"u_number\",\"number\");\n\
this.createSampler(\"Texture\",\"u_texture\");\n\
\n\
\\color.vs\n\
\n\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec3 a_normal;\n\
attribute vec2 a_coord;\n\
\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
\n\
//matrices\n\
uniform mat4 u_model;\n\
uniform mat4 u_normal_model;\n\
uniform mat4 u_view;\n\
uniform mat4 u_viewprojection;\n\
\n\
//globals\n\
uniform float u_time;\n\
uniform vec4 u_viewport;\n\
uniform float u_point_size;\n\
\n\
//camera\n\
uniform vec3 u_camera_eye;\n\
void main() {\n\
	\n\
	vec4 vertex4 = vec4(a_vertex,1.0);\n\
	v_normal = a_normal;\n\
	v_uvs = a_coord;\n\
	\n\
	//vertex\n\
	v_pos = (u_model * vertex4).xyz;\n\
	//normal\n\
	v_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\n\
	gl_Position = u_viewprojection * vec4(v_pos,1.0);\n\
}\n\
\n\
\\color.fs\n\
\n\
precision mediump float;\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
//globals\n\
uniform vec3 u_camera_eye;\n\
uniform vec4 u_clipping_plane;\n\
uniform float u_time;\n\
uniform vec3 u_background_color;\n\
uniform vec3 u_ambient_light;\n\
\n\
uniform float u_number;\n\
uniform sampler2D u_texture;\n\
\n\
//material\n\
uniform vec4 u_material_color; //color and alpha\n\
void main() {\n\
	vec3 N = normalize( v_normal );\n\
	vec3 L = vec3( 0.577, 0.577, 0.577 );\n\
	vec4 color = u_material_color;\n\
	color.xyz *= max(0.0, dot(N,L) );\n\
	gl_FragColor = color;\n\
}\n\
\n\
";

LS.ShaderCode.examples.light_and_deformers = "\n\
\n\
\\js\n\
//define exported uniforms from the shader (name, uniform, widget)\n\
this.createSampler(\"Texture\",\"u_texture\",{ missing:'white' });\n\
this.createSampler(\"Spec. Texture\",\"u_specular_texture\");\n\
this.createSampler(\"Normal Texture\",\"u_normal_texture\",{ missing:'normal' });\n\
this._light_mode = 1;\n\
\n\
\\default.vs\n\
\n\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec3 a_normal;\n\
attribute vec2 a_coord;\n\
\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
\n\
//matrices\n\
uniform mat4 u_model;\n\
uniform mat4 u_normal_model;\n\
uniform mat4 u_view;\n\
uniform mat4 u_viewprojection;\n\
\n\
//globals\n\
uniform float u_time;\n\
uniform vec4 u_viewport;\n\
uniform float u_point_size;\n\
\n\
#pragma shaderblock \"light\"\n\
#pragma shaderblock \"morphing\"\n\
#pragma shaderblock \"skinning\"\n\
\n\
//camera\n\
uniform vec3 u_camera_eye;\n\
void main() {\n\
	\n\
	vec4 vertex4 = vec4(a_vertex,1.0);\n\
	v_normal = a_normal;\n\
	v_uvs = a_coord;\n\
  \n\
  //deforms\n\
  applyMorphing( vertex4, v_normal );\n\
  applySkinning( vertex4, v_normal );\n\
	\n\
	//vertex\n\
	v_pos = (u_model * vertex4).xyz;\n\
  \n\
  applyLight(v_pos);\n\
  \n\
	//normal\n\
	v_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\n\
	gl_Position = u_viewprojection * vec4(v_pos,1.0);\n\
}\n\
\n\
\\color.fs\n\
\n\
#ifdef DRAW_BUFFERS\n\
	#extension GL_EXT_draw_buffers : require \n\
#endif\n\
precision mediump float;\n\
\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
\n\
//globals\n\
uniform vec3 u_camera_eye;\n\
uniform vec4 u_clipping_plane;\n\
uniform float u_time;\n\
uniform vec4 u_background_color;\n\
uniform vec4 u_material_color;\n\
\n\
uniform sampler2D u_texture;\n\
uniform sampler2D u_specular_texture;\n\
uniform sampler2D u_normal_texture;\n\
\n\
#pragma snippet \"input\"\n\
#pragma shaderblock \"light\"\n\
#pragma shaderblock \"applyReflection\"\n\
\n\
#pragma snippet \"perturbNormal\"\n\
\n\
void main() {\n\
	Input IN = getInput();\n\
	SurfaceOutput o = getSurfaceOutput();\n\
	vec4 surface_color = texture2D( u_texture, IN.uv ) * u_material_color;\n\
	o.Albedo = surface_color.xyz;\n\
	vec4 spec = texture2D( u_specular_texture, IN.uv );\n\
	o.Specular = spec.x;  \n\
	o.Gloss = spec.y * 10.0;\n\
	o.Reflectivity = spec.x * 0.5;\n\
	vec4 normal_pixel = texture2D( u_normal_texture, IN.uv );\n\
	o.Normal = perturbNormal( IN.worldNormal, IN.worldPos, v_uvs, normal_pixel.xyz );\n\
	  \n\
	vec4 final_color = vec4(0.0);\n\
	Light LIGHT = getLight();\n\
	FinalLight FINALLIGHT = computeLight( o, IN, LIGHT );\n\
	final_color.xyz = applyLight( o, FINALLIGHT );\n\
	final_color.a = surface_color.a;\n\
	if( o.Reflectivity > 0.0 )\n\
		final_color = applyReflection( IN, o, final_color );\n\
	#ifdef DRAW_BUFFERS\n\
		gl_FragData[0] = final_color;\n\
	#else\n\
		gl_FragColor = final_color;\n\
	#endif\n\
}\n\
";


LS.ShaderCode.examples.skybox = "\n\
\n\
\\js\n\
//define exported uniforms from the shader (name, uniform, widget)\n\
this.createUniform(\"ground_color\",\"u_ground_color\",\"color\",[0.5,0.5,0.5]);\n\
this.render_state.cull_face = false;\n\
this.render_state.depth_test = false;\n\
this.flags.ignore_lights = true;\n\
this.flags.ignore_frustum = true;\n\
\n\
\\color.vs\n\
\n\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec3 a_normal;\n\
attribute vec2 a_coord;\n\
\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
\n\
//matrices\n\
uniform mat4 u_model;\n\
uniform mat4 u_normal_model;\n\
uniform mat4 u_view;\n\
uniform mat4 u_viewprojection;\n\
\n\
//globals\n\
uniform vec3 u_camera_eye;\n\
uniform float u_time;\n\
uniform vec4 u_viewport;\n\
uniform float u_point_size;\n\
\n\
//camera\n\
uniform vec3 u_camera_eye;\n\
void main() {\n\
	\n\
	vec4 vertex4 = vec4(a_vertex,1.0);\n\
	v_normal = a_normal;\n\
	v_uvs = a_coord;\n\
	\n\
	//vertex\n\
	v_pos = (u_model * vertex4).xyz;\n\
	//normal\n\
	v_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\n\
	gl_Position = u_viewprojection * vec4(v_pos,1.0);\n\
}\n\
\n\
\\color.fs\n\
\n\
#ifdef DRAW_BUFFERS\n\
	#extension GL_EXT_draw_buffers : require \n\
#endif\n\
precision mediump float;\n\
//varyings\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
varying vec2 v_uvs;\n\
//globals\n\
uniform vec3 u_camera_eye;\n\
uniform vec4 u_clipping_plane;\n\
uniform float u_time;\n\
uniform vec3 u_background_color;\n\
uniform vec3 u_ambient_light;\n\
\n\
//material\n\
uniform vec4 u_material_color; //color and alpha\n\
uniform vec3 u_ground_color; //color and alpha\n\
\n\
void main() {\n\
	vec3 N = normalize( v_normal );\n\
	vec4 color = u_material_color;\n\
	vec3 fog_color = vec3(1.0);\n\
	if(N.y < 0.0)\n\
		color.xyz = u_ground_color * (1.0 - abs(N.y));\n\
	else\n\
		color.xyz = mix( color.xyz, fog_color, 1.0 - N.y );\n\
	#ifdef DRAW_BUFFERS\n\
		gl_FragData[0] = color;\n\
	#else\n\
		gl_FragColor = color;\n\
	#endif\n\
}\n\
\n\
";





LS.Script.templates.global = "//global scripts can have any kind of code.\n//They are used to define new classes (like materials and components) that are used in the scene.\n\n";
LS.Script.templates.component = "//https://github.com/jagenjo/litescene.js/blob/master/guides/programming_components.md\n\
//This is an example of a component code\n\
function MyComponentClass(o) {\n  //define some properties\n	this.someprop = 1;\n  //if we have the state passed, then we restore the state\n  if(o)\n    this.configure(o);\n}\n\n\
//bind events when the component belongs to the scene\nMyComponentClass.prototype.onAddedToScene = function(scene)\n{\n  LEvent.bind(scene, \"update\", this.onUpdate, this );\n}\n\n\
//unbind events when the component no longer belongs to the scene\nMyComponentClass.prototype.onRemovedFromScene = function(scene)\n{\n	//bind events\n  LEvent.unbind(scene, \"update\", this.onUpdate, this );\n}\n\n\
//example of one method called for ever update event\nMyComponentClass.prototype.onUpdate = function(e,dt)\n{\n  //do something\n  //...\n}\n\n\
//you can also implement the methods serialize and configure\n\n\
//register the class so it is a valid component for LS\n\nLS.registerComponent( MyComponentClass );\n";
