/**
 * 	Kepler - Core
 * 	 STUDIOS
 * 	All rights reserved.
 *
 * 	Author: Kaj Dijksta
 *
 **/
 
import {vector2, vector3, matrix4 } from './math.js';
import sampler2D from './sampler2D.js';
import samplerCube from './samplerCube.js';


class uniform_template{
	
	name;
	value;
	prepared = false;
	
}
 
 
/**
 * Shader Object
 * this class takes care of the shader management for you
 */
class shader {
	
	constructor() {

		this.program;
		
		this.uniform_templates = [];
		
		this.uniforms = [];
		this.attributes = [];
		
		
		this.url;
		
		this.samplerId = 0;
		this.libraryContent = false;
		this.librarys = [];
		
		this.pragmas = [];
		
		this.rawShader;
		
		//this.samplerId = 0;
		this.programInfo;
		
		this.compiled = false;
		
		this.blend = 0;
	}

	/**
	 * set viewport
	 * @param {(viewport)} viewport
	**/
	setViewport( viewport ){
		
		this.viewport = viewport;
		this.gl = viewport.gl;
	
	}
	
	createFromFile(url) {
		
		this.url = url;
		
		var shaderText = kepler.resources.loadTextFileSynchronous(url);
		
		var splitShader = shaderText.split("// #keplerEngine - Split");
		
		this.vertexShaderText  = splitShader[0];
		this.fragmentShaderText = splitShader[1];
		
		//add pragma's
		for(var c = 0; c<this.pragmas.length; c++) {
			var currentPragma = this.pragmas[c];
			
			switch (currentPragma.type) {
				case "define":
				
					var pragma = "#define " + currentPragma.name + " " + currentPragma.value + '    \n  ';
					
						this.fragmentShaderText = pragma + this.fragmentShaderText;
						this.vertexShaderText = pragma + this.vertexShaderText;
					
				break;
			}
		}
		
		var allLines = this.fragmentShaderText.split("\n");
		
		for(var b = 0; b<allLines.length; b++) {
			var currentLine = allLines[b];
			
			if( currentLine.includes("#include") ) {
				
				console.log("LAAAL", currentLine);
				
				var fileName = currentLine.replace("#include",'').replace('"','').replace('"','').replace(' ','').replace(' ','').replace(' ','');

				var fileContent = kepler.resources.loadTextFileSynchronous("shaders/"+fileName);
			
				//this.fragmentShaderText = fileContent + this.fragmentShaderText;
				
				allLines[b] = fileContent;
				console.log(allLines[b]);
				
			}

		}
	
		this.fragmentShaderText = allLines.join("\n");
		
		/*
		var includes = shaderText.split("#include");
		
		for(var c = 1; c<includes.length; c++) {
			var currentInclude = includes[c];
	
			var lines = currentInclude.split("\n");
			var currentLine = lines[0];
			var fileName = currentLine.replace('"','').replace('"','').replace(' ','').replace(' ','').replace(' ','');

			var fileContent = kepler.resources.loadTextFileSynchronous("shaders/"+fileName);
			
			this.fragmentShaderText = fileContent + this.fragmentShaderText;
			
			
		}
*/
		

		
		
		var shader_version_3 = this.fragmentShaderText.includes("#version 300 es");
		
		if(shader_version_3) {

							
			this.fragmentShaderText = this.fragmentShaderText.replace("#version 300 es",'');
			this.fragmentShaderText = this.fragmentShaderText.replace("#version 300 es",'');
			this.vertexShaderText = this.vertexShaderText.replace("#version 300 es",'');
			this.vertexShaderText = this.vertexShaderText.replace("#version 300 es",'');
			
			this.fragmentShaderText = "#version 300 es \n" + this.fragmentShaderText;
			this.vertexShaderText = "#version 300 es \n" + this.vertexShaderText;
		
		}
		
		
		

		this.fragmentShaderText = this.fragmentShaderText.replace("#include",'//');
		this.fragmentShaderText = this.fragmentShaderText.replace("#include",'//');
		this.fragmentShaderText = this.fragmentShaderText.replace("#include",'//');
		this.fragmentShaderText = this.fragmentShaderText.replace("#include",'//');
		this.fragmentShaderText = this.fragmentShaderText.replace("#include",'//');
		this.fragmentShaderText = this.fragmentShaderText.replace("#include",'//');
		this.fragmentShaderText = this.fragmentShaderText.replace("#include",'//');
		
		this.vertexShaderText = this.vertexShaderText.replace("#include",'//');
		
		
		if(shaderText.includes("#version 300 es")) {
			
			var attributes = this.vertexShaderText.split("in ");
			
			attributes.shift();
			
			//console.log("3.0 in attributes",  attributes);
			
		} else {
			
			var attributes = shaderText.split("attribute");

		}
		
		var GLSL_TYPES = ['vec2','vec3', 'vec4','mat2','mat3', 'mat4', 'float', 'int', 'sampler2D', 'samplerCube', 'array'];

		for(var c =0; c<attributes.length;c++) {
			var a = attributes[c];
			var u = a.split(';')[0];
			
			u = (u[0] == ' ') ? u.slice(1, u.length+1) : u;
			u = (u[0] == ' ') ? u.slice(1, u.length+1) : u;
			
			var attributeParts = u.split(" ");

			var type = attributeParts[0];
			var name = attributeParts[1];
			
			if(GLSL_TYPES.contains(type)) {
				
				this.addAttribute(name);
				
			}
		}
		
		
		var uniforms =  shaderText.split("uniform");
		
		
		
		for(var c =0; c<uniforms.length;c++) {
			var a = uniforms[c];
			var u = a.split(';')[0];
			
			u = (u[0] == ' ') ? u.slice(1, u.length+1) : u;
			u = (u[0] == ' ') ? u.slice(1, u.length+1) : u;
			
			var uniformParts = u.split(" ");
			var uniform = {};
			
			uniform.type = uniformParts[0];
			uniform.name = uniformParts[1];
			
			
			
			
			var array_Check = uniform.name.split("[");
			
			if(array_Check.length > 1 ){
				var arraySize = parseFloat(array_Check[1].split(']')[0]);
				
				uniform.arrayType = uniform.type;
				uniform.type = "array";
				uniform.size = arraySize;
				uniform.name = array_Check[0];
			}
			
			if(GLSL_TYPES.contains(uniform.type)) {
				//console.log("3.0  uniforms",  uniform.name);
				
				this.addUniform(uniform);
			}
		}
		
		
		//console.log( this );
		
	}
	
	compile(){
		
		this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
		this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
		
		this.gl.shaderSource(this.vertexShader, this.vertexShaderText);
		this.gl.compileShader(this.vertexShader);
		
		this.gl.shaderSource(this.fragmentShader, this.fragmentShaderText);
		this.gl.compileShader(this.fragmentShader);
		
		this.program = this.gl.createProgram();
		
		this.gl.attachShader(this.program, this.vertexShader);
		this.gl.attachShader(this.program, this.fragmentShader);
		
		if (!this.gl.getShaderParameter(this.vertexShader, this.gl.COMPILE_STATUS)) {
			alert(this.gl.getShaderInfoLog(this.vertexShader));
			return null;
		}
		
		if (!this.gl.getShaderParameter(this.fragmentShader, this.gl.COMPILE_STATUS)) {
			alert(this.gl.getShaderInfoLog(this.fragmentShader));
			return null;
		}

		this.gl.linkProgram(this.program);

		if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
			alert("Could not initialise shaders");
		}

		this.gl.useProgram(this.program);
		

		
		for(var c = 0; c<this.attributes.length; c++) {
			
			var attribute = this.attributes[c];
			
			this.bindAttribute( attribute );
			
		}
		
		this.bindUniforms();
		
		this.compiled = true;
	}
	
	/**
	 * Create shader object from source
	 * @param {(String)} fileUrl of shader file.
	**/

	createFomFile( fileUrl ){

		this.url = fileUrl;

		var programInfo = kepler.resources.getShaderProgram(this.url, this.pragmas, this.librarys);
		
		this.program = programInfo.program;
		this.programInfo = programInfo;
		
		var GLSL_TYPES = ['vec2','vec3', 'vec4','mat2','mat3', 'mat4', 'float', 'int', 'sampler2D', 'samplerCube', 'array'];

		

		if(programInfo.rawData.includes("#version 300 es")) {
			
			//console.log(this.programInfo.this.vertexShaderText);
			
			
			var attributes = this.programInfo.this.vertexShaderText.split("in ");
			
			attributes.shift();
			
			console.log("3.0 in attributes",  attributes);
			
		} else {
			
			var attributes = programInfo.rawData.split("attribute");
			
		}
		
		var uniforms =  programInfo.rawData.split("uniform"); 
		
		
		//add attributes
		for(var c =0; c<attributes.length;c++) {
			var a = attributes[c];
			var u = a.split(';')[0];
			
			u = (u[0] == ' ') ? u.slice(1, u.length+1) : u;
			u = (u[0] == ' ') ? u.slice(1, u.length+1) : u;
			
			var attributeParts = u.split(" ");

			var type = attributeParts[0];
			var name = attributeParts[1];
			
			if(GLSL_TYPES.contains(type)) {
				
				this.addAttribute(name);
			}
		}
		
		//add uniforms
		for(var c =0; c<uniforms.length;c++) {
			var a = uniforms[c];
			var u = a.split(';')[0];
			
			u = (u[0] == ' ') ? u.slice(1, u.length+1) : u;
			u = (u[0] == ' ') ? u.slice(1, u.length+1) : u;
			
			var uniformParts = u.split(" ");
			var uniform = {};
			
			uniform.type = uniformParts[0];
			uniform.name = uniformParts[1];
			
			
			var array_Check = uniform.name.split("[");
			
			if(array_Check.length > 1 ){
				var arraySize = parseFloat(array_Check[1].split(']')[0]);
				
				uniform.arrayType = uniform.type;
				uniform.type = "array";
				uniform.size = arraySize;
				uniform.name = array_Check[0];
			}
			
			if(GLSL_TYPES.contains(uniform.type)) {
				this.addUniform(uniform);
			}
		}
		
		
		for(var c = 0; c<this.librarys.length; c++) {
			var lib = this.librarys[c];
			var uniforms = lib.uniforms;
			for(var b = 0; b<uniforms.length; b++) {
				var uniform = uniforms[b];

				this.setUniform(uniform.name, uniform.value, true);
			}
			
		}
		

	};
	
	/**
	 * create shader library object
	 * @param {(String)} fileUrl of shader file.
	**/
	createLibraryFomFile( fileUrl ){
		this.libraryContent =  kepler.resources.loadTextFileSynchronous(fileUrl);
	};


	/**
	 * Add library to shader object
	 * @param {(shaderObject)} shader.
	 * @param {(int)} type (pixel shader = 0, vertex shader = 1).
	**/
	addLibrary( shader, type ){
		var library = {};
		library.content = shader.libraryContent;
		library.type = type;
		library.uniforms = shader.uniforms;
		
		//this.pragmas = pragmas.concat(shader.pragmas);
		this.librarys.push(library);
	};


	/**
	 * Define pragma's
	 * @param {(String)} name.
	 * @param {(string)} value.
	**/
	definePragma(name, value) {
		var pragma = {};
		pragma.type = "define";
		pragma.name = name;
		pragma.value = value;
		
		this.pragmas.push(pragma);
	};


	/**
	 * Get uniform by name
	 * @param {(string)} name.
	**/
	getUniformByName(name) {
		var uniforms = this.uniforms;
		for(var c = 0; c < uniforms.length; c++) {
			var uniform = uniforms[c];
			if(uniform.name == name)
				return uniform.uniformLocation;
		}
		
		return false;
		
		console.log("could not locate buffer :"+name);
	};

	/**
	 * Update uniform variable
	 * @param {(uniformObject)} uniform.
	**/
	updateUniform( uniform ) {

		var uniformLocation = uniform.uniformLocation;

		var value 			= uniform.value;

		switch( uniform.type ) {

			case "sampler2D":

				var sampler 	= value;

				var type 		= sampler.type;
				
				//if(asddsaas==0) 
				//console.log(sampler);
				this.gl.activeTexture(this.gl.TEXTURE0 + sampler.id );

				//if transparent
				if (!sampler.useAlpha) {
					this.gl.disable(this.gl.BLEND);
					this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
					this.gl.enable(this.gl.DEPTH_TEST);
				} else {
					this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
					this.gl.enable(this.gl.BLEND);
					this.gl.disable(this.gl.DEPTH_TEST);
				}

				this.gl.disable(this.gl.BLEND);
				this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
				this.gl.enable(this.gl.DEPTH_TEST);
		
				//this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
				//this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
				//this.gl.enable(this.gl.BLEND); 	
				
			
				//for(var b = 0; b<sampler.textures.length;b++) {
					
					var texture = sampler.getTexture();
					var glTexture = texture.glTexture;
					
					this.gl.bindTexture(sampler.target, glTexture);
					this.gl.uniform1i(uniformLocation, sampler.id);
					
				//}
				//this.gl.bindTexture(this.gl.TEXTURE_2D, null);
			break;
			case "samplerCube":
				//console.log("hiiier");
				
				var sampler = value;
				
				//if(asddsaas==0) {
				//	console.log(sampler);
				//	asddsaas++;
				//}
				
				var type = sampler.type;
				
				
				this.gl.activeTexture(this.gl.TEXTURE0 + sampler.id);

				//if transparent
				if (!sampler.useAlpha) {
					this.gl.disable(this.gl.BLEND);
					this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
					this.gl.enable(this.gl.DEPTH_TEST);
				} else {
					this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
					this.gl.enable(this.gl.BLEND);
					this.gl.disable(this.gl.DEPTH_TEST);
				}

				this.gl.disable(this.gl.BLEND);
				this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
				this.gl.enable(this.gl.DEPTH_TEST);
		
				//this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
				//this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
				//this.gl.enable(this.gl.BLEND); 	
				
			
				//for(var b = 0; b<sampler.textures.length;b++) {
					
					var texture = sampler.cubeTexture;
					var glTexture = texture;
					
					this.gl.bindTexture(sampler.target, glTexture);
					this.gl.uniform1i(uniformLocation, sampler.id);
					
				//}
				//this.gl.bindTexture(this.gl.TEXTURE_2D, null);

				
			break;
			case "float":
				this.gl.uniform1f(uniformLocation, value);
			break;
			case "vec2":
				this.gl.uniform2f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]) );
			break;
			case "vec3":
			
				this.gl.uniform3f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]), parseFloat(value[2])  );
			break;
			case "vec4":
				this.gl.uniform4f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]), parseFloat(value[2]), parseFloat(value[3]));
			break;
			case "mat2":
				this.gl.uniformMatrix2fv(uniformLocation, false, matrix2.getMatrixElements(value));
			break;
			case "mat3":
				this.gl.uniformMatrix3fv(uniformLocation, false, matrix3.getMatrixElements(value));
			break;
			case "mat4":
				this.gl.uniformMatrix4fv(uniformLocation, false, matrix4.getMatrixElements(value));
			break;
			case "int":
			
			break;
			case "ivec2":
			
			break;
			case "array":
				var arrayType = uniform.arrayType;
				//console.log(arrayType);
				switch(arrayType) {
					case "float":
						this.gl.uniform1fv(uniformLocation, value );
					break;
					case "vec2":
						this.gl.uniform2fv(uniformLocation, value );
					break;
					case "vec3":
						var flat = [];

						for(var c = 0; c<value.length; c++) {
							var v = value[c];
							flat.push(v[0], v[1], v[2]);
						}
					
						this.gl.uniform3fv( uniformLocation, flat );
					break;
					case "vec4":
					
						var flat = [];

						for(var c = 0; c<value.length; c++) {
							var v = value[c];
							flat.push(v[0], v[1], v[2], v[3]);
						}
					
						this.gl.uniform4fv( uniformLocation, flat );
					break;
				}
			break;
		} 
	}



	setUniform(name, value) {
		//console.log(name, value);

		for(var c = 0; c < this.uniforms.length; c++) {
			if(this.uniforms[c].name == name) {
				
				this.uniforms[c].value = value;
				
				
				if(this.compiled && this.uniforms[c].uniformLocation)
					this.updateUniform( this.uniforms[c] );
			}
			
		}
		

		
	}

	/**
	 * Set uniform variable
	 * @param {(String)} name.
	 * @param {(String)} value.
	 * @param {(boolean)} noError.
	**/
	/*
	setUniform(name, value, update) {
		
		var a = 0;
		var uniforms = this.uniforms;

		for(var c = 0; c < uniforms.length; c++) {
			if(uniforms[c].name == name) {
				
				if(uniforms[c].type == "sampler2D") {
					//console.log(value);
					value.bind(this);
				}
				
				if(uniforms[c].type == "samplerCube") {
					//console.log(value);
					value.bind(this);
				}
				
				uniforms[c].value = value;
				
				if(update)
					this.updateUniform(uniforms[c]);
				
				a++;
			}
		}
		
		if(this.libraryContent) {
			var uniform = {};
			
			uniform.name = name;
			uniform.value = value;
			
			uniforms.push(uniform)
			a++;
		}
		
		if(a == 0)
			console.log('uniform '+name+' not found in shader ', this.url, this);
	}
*/
	/**
	 * Set uniform variable
	 * @param {(String)} name.
	 * @param {(String)} value.
	 * @param {(boolean)} noError.
	**/
	
	addUniform(uniform) {  
	
		uniform.value = this.createEmptyValue(uniform);
		uniform.objectType = 'uniform';
		
		this.uniforms.push(uniform);
		
	}
	
	
	/**
	 * Set uniform variable
	 * @param {(String)} name.
	 * @param {(String)} value.
	 * @param {(boolean)} noError.
	**/
	
	bindUniforms() { 
	
		//console.log("bind uniforms");

		var program = this.program;

		var uniformCount	= gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );

		this.gl.useProgram(this.program);


		this.uniforms = [];

		for (var i = 0; i < uniformCount; i++) {

			var glUniform			= gl.getActiveUniform( program, i );

			var name 				= glUniform.name;


			var uniform				= {};
		
			uniform.type 			= glUniform.type;

			uniform.name 			= glUniform.name;

			uniform.uniformLocation = gl.getUniformLocation( program, name );

			uniform.value 			= this.createEmptyValue(uniform);
		
			uniform.objectType 		= 'uniform';

			uniform.uniformLocation = gl.getUniformLocation( program, name );

			if(!uniform.value){
				uniform.value = this.createEmptyValue(uniform);
			}
			
		
			uniform.objectType = 'uniform';

			this.uniforms.push(uniform);
	

	
			if( !uniform.value ){

				uniform.value = this.createEmptyValue(uniform);

			}

			//console.log("uniform 1:		", uniform);
			
			uniform.compiled = true;




			if( uniform.type == gl.SAMPLER_2D ) {


				
				var originalSampler = uniform.value; 
				var sampler = new sampler2D();

				console.log('uniform loading', uniform);
				
				
				var texture = originalSampler.texture;
	
				if(!texture.loaded && !originalSampler.isFramebuffer && texture.dataType != "int") {
					sampler.texture = kepler.resources.getLoadedTexture( texture.name, this.viewport );	
				}
				
				if(originalSampler.isFramebuffer)
				{
					sampler.datatype = originalSampler.datatype;
					sampler.format = originalSampler.format;
					sampler.internalFormat = originalSampler.internalFormat;
						
					sampler.filter = originalSampler.filter;
					sampler.texture = originalSampler.texture;
					
				}
				
				if(texture.dataType == "int") {
					sampler.texture = originalSampler.texture;
					sampler.texture.setViewport( this.viewport );
				}
				
				//sampler.samplerID = ++this.samplerId;
				//console.log("new texture", kepler.resources, texture.name, sampler);
				
				sampler.setViewport( this.viewport );
				
				if(!sampler.binded) {
				
				}
				

				
				
				sampler.bind(this);
				//console.log("new sampler", sampler);
				uniform.value = sampler;
			}
		
			if(uniform.type == gl.SAMPLER_CUBE) {
				var sampler = uniform.value;
				
				sampler.setViewport( this.viewport );
				
				//sampler.samplerID = ++this.samplerId;
				
				var textures = sampler.textures;
				
				for(var b = 0; b<textures.length;b++) {
					//console.log("cube texture", textures[c]);
					//console.log(sampler.textures[c].texture.name);
					var texture = textures[b];
					
					if(!texture.loaded) {
						sampler.textures[b].texture = kepler.resources.getLoadedTexture(sampler.textures[b].texture.name, this.viewport );
					}
				}
				
				if(!sampler.binded) {
				
				}
				
				sampler.bind(this);
				
				uniform.value = sampler;
			}





			
			uniform.objectType = 'uniform';

			this.addUniform( uniform );

			//this.updateUniform(uniform);

			//this.uniforms.push( uniform );




		}

	this.uniforms = [];


		var oldUniforms = this.uniforms;
		
		
		
		
		for(var c = 0; c<oldUniforms.length; c++) {
			
			var uniform = oldUniforms[c];
			
			//this.gl.useProgram(this.program);
		
		//if(!uniform.compiled) {
			
			//console.log("bind uniform", uniform.name);
			
			uniform.uniformLocation = this.gl.getUniformLocation(this.program, uniform.name);
			
			//console.log("uniformLocation", uniform.name, uniform.uniformLocation);
	
			if(!uniform.value)
			uniform.value = this.createEmptyValue(uniform);
		
			uniform.objectType = 'uniform';

			console.log("uniform 2:		", uniform);
			
			if(uniform.type == "sampler2D") {
				
				var originalSampler = uniform.value; 
				var sampler = new sampler2D();

				//console.log('uniform loading', uniform);
				
				
				var texture = originalSampler.texture;
	
				if(!texture.loaded && !originalSampler.isFramebuffer && texture.dataType != "int") {
					sampler.texture = kepler.resources.getLoadedTexture( texture.name, this.viewport );	
				}
				
				if(originalSampler.isFramebuffer)
				{
					sampler.datatype = originalSampler.datatype;
					sampler.format = originalSampler.format;
					sampler.internalFormat = originalSampler.internalFormat;
						
					sampler.filter = originalSampler.filter;
					sampler.texture = originalSampler.texture;
					
				}
				
				if(texture.dataType == "int") {
					sampler.texture = originalSampler.texture;
					sampler.texture.setViewport( this.viewport );
				}
				
				//sampler.samplerID = ++this.samplerId;
				//console.log("new texture", kepler.resources, texture.name, sampler);
				
				sampler.setViewport( this.viewport );
				
				if(!sampler.binded) {
				
				}
				

				
				
				sampler.bind(this);
				//console.log("new sampler", sampler);
				uniform.value = sampler;
			}
		
			if(uniform.type == "samplerCube") {
				var sampler = uniform.value;
				
				sampler.setViewport( this.viewport );
				
				//sampler.samplerID = ++this.samplerId;
				
				var textures = sampler.textures;
				
				for(var b = 0; b<textures.length;b++) {
					//console.log("cube texture", textures[c]);
					//console.log(sampler.textures[c].texture.name);
					var texture = textures[b];
					
					if(!texture.loaded) {
						sampler.textures[b].texture = kepler.resources.getLoadedTexture(sampler.textures[b].texture.name, this.viewport );
					}
				}
				
				if(!sampler.binded) {
				
				}
				
				sampler.bind(this);
				
				uniform.value = sampler;
			}
			
			uniform.compiled = true;
			
			var exist = this.getUniformByName(uniform.name);

			if(uniform.uniformLocation != null && !exist){
				
				//console.log("add", uniform.name);
				this.uniforms.push(uniform);
				
				//console.log("update", uniform.name);
				this.updateUniform(uniform);
				
			}
			
		}
	
			//if(uniform.uniformLocation)
			//	this.uniforms.push(uniform);
				
		//}
	}


	/**

	createEmptyValue(uniform) {
		switch(uniform.type) {
			case gl.SAMPLER_2D:
				var def = kepler.resources.getTexture("default");
				var sampler = new sampler2D( kepler );
				
				sampler.addTexture(def);
				return sampler;
			break;
			case gl.SAMPLER_CUBE:
				
				var dataArray = [];
				var width = 512;
				var height = 512;
				
				for( var y = 0; y < height; y++ )
				{
					for( var x = 0; x < width; x++ )
					{
						dataArray.push(x / width);
						dataArray.push( y / width);

						dataArray.push(  x  / width);
						dataArray.push(  y * x / width); 
					}
				}
			
				var def = kepler.textureFromArray(dataArray, width, height, true);
			
				//var def = kepler.resources.getTexture("default");
				 var sampler = new samplerCube( );

				 sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_POSITIVE_X);
				 sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_NEGATIVE_X);
				 sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_POSITIVE_Y);
				 sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y);
				 sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_POSITIVE_Z);
				 sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z);
				 
				return sampler;
			break;
			
			
			case gl.FLOAT:
				return 0.0;
			break;
			case gl.FLOAT_VEC2:
				return new vector2(0,0);
			break;
			case gl.FLOAT_VEC3:
				return new vector3(0,0,0);
			break;
			case gl.FLOAT_VEC3:
				return new vector4(0,0,0,0);
			break;
			case gl.FLOAT_MAT2:
				return matrix2.identity();
			break;
			case gl.FLOAT_MAT3:
				return matrix3.identity();
			break;
			case gl.FLOAT_MAT4:
				return matrix4.identity();
			break;
			case gl.INT:
				return 0;
			break;
			case "ivec2":
			
			break;
			case "array":

			break;
		} 
	}
*/
	/**
	 * Update uniform variable
	 * @param {(uniformObject)} uniform.
	**/
	updateUniform(uniform) {
		var uniformLocation = uniform.uniformLocation;
		var value = uniform.value;

		switch(uniform.type) {
			
			case "sampler2D":

				var sampler = value;
				var type = sampler.type;
				
				//if(asddsaas==0) 
				//console.log(sampler);
				this.gl.activeTexture(this.gl.TEXTURE0 + sampler.id );

				//if transparent
				if (!sampler.useAlpha) {
					this.gl.disable(this.gl.BLEND);
					this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
					this.gl.enable(this.gl.DEPTH_TEST);
				} else {
					this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
					this.gl.enable(this.gl.BLEND);
					this.gl.disable(this.gl.DEPTH_TEST);
				}

				this.gl.disable(this.gl.BLEND);
				this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
				this.gl.enable(this.gl.DEPTH_TEST);
		
				//this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
				//this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
				//this.gl.enable(this.gl.BLEND); 	
				
			
				//for(var b = 0; b<sampler.textures.length;b++) {
					
					var texture = sampler.getTexture();
					var glTexture = texture.glTexture;
					
					this.gl.bindTexture(sampler.target, glTexture);
					this.gl.uniform1i(uniformLocation, sampler.id);
					
				//}
				//this.gl.bindTexture(this.gl.TEXTURE_2D, null);
			break;
			case "samplerCube":
				//console.log("hiiier");
				
				var sampler = value;
				
				//if(asddsaas==0) {
				//	console.log(sampler);
				//	asddsaas++;
				//}
				
				var type = sampler.type;
				
				
				this.gl.activeTexture(this.gl.TEXTURE0 + sampler.id);

				//if transparent
				if (!sampler.useAlpha) {
					this.gl.disable(this.gl.BLEND);
					this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
					this.gl.enable(this.gl.DEPTH_TEST);
				} else {
					this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
					this.gl.enable(this.gl.BLEND);
					this.gl.disable(this.gl.DEPTH_TEST);
				}

				this.gl.disable(this.gl.BLEND);
				this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
				this.gl.enable(this.gl.DEPTH_TEST);
		
				//this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
				//this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
				//this.gl.enable(this.gl.BLEND); 	
				
			
				//for(var b = 0; b<sampler.textures.length;b++) {
					
					var texture = sampler.cubeTexture;
					var glTexture = texture;
					
					this.gl.bindTexture(sampler.target, glTexture);
					this.gl.uniform1i(uniformLocation, sampler.id);
					
				//}
				//this.gl.bindTexture(this.gl.TEXTURE_2D, null);

				
			break;
			case "float":
				this.gl.uniform1f(uniformLocation, value);
			break;
			case "vec2":
				this.gl.uniform2f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]) );
			break;
			case "vec3":
			
				this.gl.uniform3f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]), parseFloat(value[2])  );
			break;
			case "vec4":
				this.gl.uniform4f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]), parseFloat(value[2]), parseFloat(value[3]));
			break;
			case "mat2":
				this.gl.uniformMatrix2fv(uniformLocation, false, matrix2.getMatrixElements(value));
			break;
			case "mat3":
				this.gl.uniformMatrix3fv(uniformLocation, false, matrix3.getMatrixElements(value));
			break;
			case "mat4":
				this.gl.uniformMatrix4fv(uniformLocation, false, matrix4.getMatrixElements(value));
			break;
			case "int":
			
			break;
			case "ivec2":
			
			break;
			case "array":
				var arrayType = uniform.arrayType;
				//console.log(arrayType);
				switch(arrayType) {
					case "float":
						this.gl.uniform1fv(uniformLocation, value );
					break;
					case "vec2":
						this.gl.uniform2fv(uniformLocation, value );
					break;
					case "vec3":
						var flat = [];

						for(var c = 0; c<value.length; c++) {
							var v = value[c];
							flat.push(v[0], v[1], v[2]);
						}
					
						this.gl.uniform3fv( uniformLocation, flat );
					break;
					case "vec4":
					
						var flat = [];

						for(var c = 0; c<value.length; c++) {
							var v = value[c];
							flat.push(v[0], v[1], v[2], v[3]);
						}
					
						this.gl.uniform4fv( uniformLocation, flat );
					break;
				}
			break;
		} 
	}


	/**
	 * Update shader
	**/
	update(viewport) {
		
		
		
		
		if( !this.compiled ) {
			
			this.setViewport( viewport );
			
			this.gl.useProgram(this.program);
			
			this.compile();
			
		}
		
		this.gl.useProgram(this.program);

		var uniforms = this.uniforms;
		
		for(var c = 0; c < uniforms.length; c++) {
			var uniform = uniforms[c];
			this.updateUniform(uniform);
		}
	}


	/**
	 * Get Attribute by name
	 * @param {(String)} name.
	**/
	getAttributeByName(name) {
		var attributes = this.attributes;
		
		for(var c = 0; c < attributes.length; c++) {
			var attribute = attributes[c];

			if(attribute.name == name)
				return attribute.uniformLocation;
		}

		console.log("could not locate buffer :"+name);
	}
	
	addAttribute( name ) {
		
		var attr = {};
		attr.name = name;
		
		this.attributes.push( attr );
	}

	/**
	 * add attribute to shader.
	 * @param {(String)} name.
	**/
	
	bindAttribute( attr ) {
		//var attr = {};
		
		this.gl.useProgram(this.program);
		//console.log('this.gl.getAttribLocation()', this.program, name);
		
		//attr.name = name;
		//console.log("bind", this.program, attr.name);
		attr.uniformLocation = this.gl.getAttribLocation(this.program, attr.name);
		
		if(attr.uniformLocation != null) {
			
			this.gl.enableVertexAttribArray( attr.uniformLocation );
		}
		
		if(typeof(attr.uniformLocation)!='number')
			console.log("attribute '"+attr.name+"' Does not exist in shader ",this);
		
		//else
		//	console.log("added attribute",attr);
		
		//this.attributes.push( attr );
	}

	
	
}


export {shader as default};


	/**
	 * number padding
	 * @param {(int)} number.
	**/
	function pad2(number) {
		return (number < 10 ? '0' : '') + number
	}


	/**
	 * Contains
	 * @param {(object)} obj.
	**/
	Array.prototype.contains = function(obj) {
		var i = this.length;
		while (i--) {
			if (this[i] === obj) {
				return true;
			}
		}
		return false;
	};


	/**
	 * Between
	 * @param {(string)} prefix.
	 * @param {(string)} suffix.
	**/
	String.prototype.between = function(prefix, suffix)  {
	  s = this;
	  var i = s.indexOf(prefix);
	  if (i >= 0) {
		s = s.substring(i + prefix.length);
	  }
	  else {
		return '';
	  }
	  if (suffix) {
		i = s.indexOf(suffix);
		if (i >= 0) {
		  s = s.substring(0, i);
		}
		else {
		  return '';
		}
	  }
	  return s;
	};


	/**
	 * Chech if object is array
	 * @param {(obj)} object.
	**/
	function isArray(obj) {
		return obj.constructor == Array;
	}

	
	/**
	 * add string to string at a particular index
	 * @param {(String)} src.
	 * @param {(int)} index.
	 * @param {(String)} str.
	**/
	function insertAt(src, index, str) {
		return src.substr(0, index) + str + src.substr(index)
	}

var ttt= 0;
var samplerId = 0;