/*
 * Copyright 2013, kaj dijkstra , 
 * Author, Kaj Dijkstra.
 * All rights reserved.
 *
 */

 import subMesh from './subMesh.js';
 import boundingBox from './boundingBox.js';
 
/**
 * Mesh object
**/
class mesh{
	constructor(  ){
		
		this.gl = gl;

		this._className = 'mesh';
		this.fileName;
		this.name = "new";
		
		this.shaderName;
		
		this.vertexIndexBuffer;
		this.vertexPositionBuffer;
		this.vertexNormalBuffer;
		this.textureCoordBuffer;
		this.binormalBuffer = false;
		this.tangentBuffer = false;
		
		this.materials = [];
		this.subMeshes = [];
		
		this.jsonStruture;
		this.shader;
		this.forwardShader;
		
		this.renderType = "indexed";
		
		this.materialName;
		this.materialId = 1.0;
		this.useParallax = false;
		
		this.colorInfoShader;
		this.infoShader;
		
		this.materialName;
		
		
		//this.customShader = new shader();
		//this.customShader.createLibraryFomFile("shaders/custom.default.shader");

		this.heightmap;

		this.useNormal = false;
		
		this.draw_type = gl.TRIANGLES;
		this.shape = 3;
		
		this.entityID = 0;
	};

	/**
	 * set viewport
	 * @param {(viewport)} viewport
	**/
	setViewport( viewport ){
		
		this.viewport = viewport;
		this.gl = viewport.gl;
		
		
		
	}
	
	
	/**
	 * Set custom shader (Not yet in use)
	 * @param {(String)} name
	**/
	setCustomShader( name ) {
		this.customShader = new shader();
		this.customShader.createLibraryFomFile("shaders/custom."+name+".shader");
	}


	/**
	 * Set custom shader (Not yet in use)
	 * @param {(String)} name
	**/
	addHeightmap( heightmap ) {
		this.heightmap = heightmap;
		
		this.colorInfoShader.setUniform("heightmap", heightmap );
		this.infoShader.setUniform("heightmap", heightmap );
	}


	/**
	 * set material id
	 * @param {(int)} material id
	**/
	setMaterialId( id ) {
		this.materialId = id;
		//this.colorInfoShader.setUniform("materialId", this.materialId / 256 );
	}


	/**
	 * Load mesh from file
	 * @param {(String)} url to file
	**/
	loadMeshFromFile( url ) {
		this.jsonStruture = JSON.parse(kepler.loadTextFileSynchronous('media/models/'+url));
		this.fileName = url;
		this.parseLoadedMesh();
	}


	/**
	 * Combine displacement and normal data in one texture
	 * @param {(Array)} displacement data
	 * @param {(Array)} normal data
	 * @return {(Sampler)} sampler
	**/
	combineDisplacementNormal( displacement, normal ) {

		var displacementImage = displacement.data;
		var normalImage = normal.data;

		var canvas = document.createElement('Canvas');
		var width = displacement.width;
		var height = displacement.height;
		
		canvas.width  = width;
		canvas.height = height;
		
		canvas.getContext('2d').drawImage(displacementImage, 0, 0, width, height);
		var displacementpixelData = canvas.getContext('2d').getImageData(0, 0, width, height).data;
		
		
		canvas.getContext('2d').drawImage(normalImage, 0, 0, width, height);
		var normalpixelData = canvas.getContext('2d').getImageData(0, 0, width, height).data;

		var dataArray = [];

		for(var x = 0; x<width; x++) {
			for(var y = 0; y<height; y++) {
				var index = (x + (y * height) ) * 4;
				
				dataArray.push(normalpixelData[index] / 255);
				dataArray.push(normalpixelData[index+1] / 255);
				dataArray.push(normalpixelData[index+2] / 255);
				dataArray.push(displacementpixelData[index]  / 255);
					
			}
		}
		
		var text = kepler.textureFromArray(dataArray, width, height, true);
		var sampler = new sampler2D();
		
		sampler.texture = dataArray;
		
		return sampler;
	}


	/**
	 * Create tangent and binormal vectors (* This is slow!!)
	**/
	createTangentAndBinormal( ) {
		var bn = this.viewport.primitives.createTangentsAndBinormals(  this.vertexPositionBuffer,
																	  this.vertexNormalBuffer,
																	  this.textureCoordBuffer,
																	this.vertexIndexBuffer );

			
		
		if(bn.tangent) {
			
		
			var tangentArray = this.viewport.primitives.layoutArray(bn.tangent);							
			var binormalArray = this.viewport.primitives.layoutArray(bn.binormal);	
			
			
			

			this.binormalBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.binormalBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(binormalArray), this.gl.STATIC_DRAW);
			this.binormalBuffer.itemSize = 3;
			this.binormalBuffer.numItems = binormalArray.length / 3;
			this.binormalBuffer.data = binormalArray;
			
			this.tangentBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.tangentBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(tangentArray), this.gl.STATIC_DRAW);
			this.tangentBuffer.itemSize = 3;
			this.tangentBuffer.numItems = tangentArray.length / 3;
			this.tangentBuffer.data = tangentArray;
		
		}
	}

	/**
	 * set mesh name based on the material types that are used 
	 * @param {(MaterialObject)} Material
	**/
	setName( material ) {
		var name = this.name;
		
		if(material.normals.length > 0)
			name += "_normal";
			
		if(material.useParallax)
			name += "_parallax";
		
		if(material.textures.length > 0)
			name += "_texture";
			
		if(material.transparencyMaps.length > 0)
			name += "_transparent";
		
		if(material.specularMaps.length > 0)
			name += "_specular";
			
		this.shaderName = name;
	}


	/**
	 * add material to mesh 
	 * @param {(MaterialObject)} Material
	**/
	addMaterial( material ) {
		
		
		
		if(!material.created) {
			
			material.create();
			
		}
		
		
		
		this.material = material;
		
		this.material.setViewport( this.viewport );
		
		this.material.entityID = this.entityID;
		
		/*
		this.shader = new shader();

		this.shader.definePragma("TEXTURE", (material.textures.length > 0) ? 1.0 : 0.0 );
		this.shader.definePragma("NORMAL_MAP", (material.normals.length > 0) ? 1.0 : 0.0 );
		this.shader.definePragma("ROUGHNESS_MAP", (material.roughnessMaps.length > 0) ? 1.0 : 0.0 );
		this.shader.definePragma("SHADING_MODEL_ID", material.shadingModelID );
		this.shader.definePragma("PARALLAX", (material.useParallax) ? 1.0 : 0.0 );
		this.shader.definePragma("DEFERRED", 1.0 );
		
		

		this.shader.createFomFile("shaders/color.shader");
		
		this.updateShader(material, this.shader);
		

		this.forwardShader = new shader();
		
		
		
		this.forwardShader.definePragma("TEXTURE", (material.textures.length > 0) ? 1.0 : 0.0 );
		this.forwardShader.definePragma("NORMAL_MAP", (material.normals.length > 0) ? 1.0 : 0.0 );
		this.forwardShader.definePragma("ROUGHNESS_MAP", (material.roughnessMaps.length > 0) ? 1.0 : 0.0 );
		this.forwardShader.definePragma("SHADING_MODEL_ID", material.shadingModelID );
		this.forwardShader.definePragma("PARALLAX", (material.useParallax) ? 1.0 : 0.0 );
		this.forwardShader.definePragma("DEFERRED", 0 );
		
		this.forwardShader.createFomFile("shaders/color.shader");
		
		this.updateShader(material, this.forwardShader);
		
		//forwardShader
		this.updateMaterial();
		
		*/
	}

	updateShader( material, shader ) {

		shader.setUniform("far", kepler.mainCamera.far );
		//this.shader = kepler.resources.getShader("shaders/color.shader");
		
		shader.setUniform("reflectionSampler", kepler.system.reflectionSampler );
		
		if( material.textures.length > 0 ) {
		
			material.textures[0].anisotropic = 4;
			
			if(	material.textures.length > 0 ) {
				shader.setUniform("diffuseSampler", material.textures[0] );
			}
			
			if(	material.transparencyMaps.length > 0 ) {
				shader.setUniform("transparencyMapSampler", material.transparencyMaps[0] );
			}
			
			if(	material.normals.length > 0 ) {
				shader.setUniform("normalSampler", material.normals[0] );
			}
			
			if(	material.roughnessMaps.length > 0 ) {
				shader.setUniform("normalSampler", material.roughnessMaps[0] );
			}
			
			if( material.displacementMaps.length > 0  ) {
				shader.setUniform("heightSampler", material.displacementMaps[0]);
			} 
			
		} else {
			
			shader.setUniform("diffuseColor", material.diffuseColor );

			//var color = material.color;
			//this.shader.setUniform("rgb", color );
		}
		
		shader.setUniform("roughness", material.roughness );
		shader.setUniform("id", material.id );
		
		
		shader.setUniform("clearCoat", 1 );
		shader.setUniform("clearCoatThickness", 1 );
		shader.setUniform("clearCoatIOR", 1 );
		shader.setUniform("anisotropy", 1 );
		shader.setUniform("lightIntensity", 2);
		shader.setUniform("environmentLuminance", 1 );
		shader.setUniform("lightGeometry", [1,1,1] );
		shader.setUniform("clearCoatColor", [1,1,1] );
		shader.setUniform("lightColor", [1,1,1] );
		
		var sphericalHarmonics = [];
		
		for(var c = 0; c<8; c++) {
			sphericalHarmonics.push([1,1,1]);
			
		}
		
		shader.setUniform("sphericalHarmonics", sphericalHarmonics );
		

	}

	updateMaterial(  ) {
		
		
		this.shader.setUniform("roughness", this.material.roughness );
		
		this.forwardShader.setUniform("roughness", this.material.roughness );
		this.forwardShader.setUniform("alpha", this.material.alpha );
		console.log("roughness", this.material.roughness,this.shader );
	}

	/**
	 * load object from file >?>
	 * @param {(string)} url
	**/
	loadObjFromFile( url ) {

		var objText = kepler.loadTextFileSynchronous(url);
		var obj = {};
		var vertexMatches = objText.match(/^v( -?\d+(\.\d+)?){3}$/gm);
		if (vertexMatches)
		{
			console.log(obj.vertices = vertexMatches.map(function(vertex)
			{
				var vertices = vertex.split(" ");
				vertices.shift();
				return vertices;
			}));
		}
		
		
	}


	/**
	 * parse mesh, load extra mesh data into buffers
	 * @param {(string)} url
	**/
	parseLoadedMesh( url ) {

		var subMeshObject = new subMeshObject();
		
		if(this.jsonStruture.indices) {
			this.renderType = "indexed";
		
			subMeshObject.indices = this.jsonStruture.indices;

			this.vertexIndexBuffer = this.gl.createBuffer();
			
			this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer);
			
			//if(kepler.extensions.elementIndexUint)
				this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(subMeshObject.indices), this.gl.STATIC_DRAW);
			//else
			//	this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(subMeshObject.indices), this.gl.STATIC_DRAW);
			
			this.vertexIndexBuffer.itemSize = 3;
			this.vertexIndexBuffer.numItems = subMeshObject.indices.length;
			this.vertexIndexBuffer.data = subMeshObject.indices;
		} else {
			this.renderType = "array";
		}
		
		subMeshObject.vertices = this.jsonStruture.positions;
		
		this.vertexPositionBuffer = this.gl.createBuffer();
		this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexPositionBuffer);
		this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(subMeshObject.vertices), this.gl.STATIC_DRAW);
		this.vertexPositionBuffer.itemSize = 3;
		this.vertexPositionBuffer.numItems = subMeshObject.vertices.length / 3;
		this.vertexPositionBuffer.data = subMeshObject.vertices;
		
		if(this.jsonStruture.uv) {
		
			subMeshObject.textcoords = this.jsonStruture.uv;
			
			this.textureCoordBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureCoordBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(subMeshObject.textcoords), this.gl.STATIC_DRAW);
			this.textureCoordBuffer.itemSize = 2;
			this.textureCoordBuffer.numItems = subMeshObject.textcoords.length / 2;
			this.textureCoordBuffer.data = subMeshObject.textcoords;
			
		}
		
		if(this.jsonStruture.normals) {
		
			subMeshObject.normals = this.jsonStruture.normals;
		
			this.vertexNormalBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexNormalBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(subMeshObject.normals), this.gl.STATIC_DRAW);
			this.vertexNormalBuffer.itemSize = 3;
			this.vertexNormalBuffer.numItems = subMeshObject.normals.length / 3;
			this.vertexNormalBuffer.data = subMeshObject.normals;
		
		}

		this.subMeshes.push(subMeshObject);
	}


	copyMesh( mesh ) {
		
		this.createdBuffers = false;
		
		this.indices = mesh.indices;
		this.vertices = mesh.vertices;
		this.normals = mesh.normals;
		this.textureCoordinates = mesh.textureCoordinates;
		this.tangents = mesh.tangents;
		this.binormals = mesh.binormals;
		
	}
	
	/**
	 * Create mesh from arrays
	 * @param {(array)} indices
	 * @param {(array)} vertices
	 * @param {(array)} normals
	 * @param {(array)} uvs
	 * @param {(array)} tangents
	 * @param {(array)} binormals
	**/
	createMeshFromArrays( indices, vertices, normals, textureCoordinates, tangents, binormals ) {

		//todo: loop trough sub meshes
		//var subMeshObject = new subMesh();
		
		this.createdBuffers = false;
		
		this.indices = indices;
		this.vertices = vertices;
		this.normals = normals;
		this.textureCoordinates = textureCoordinates;
		this.tangents = tangents;
		this.binormals = binormals;
		
		/*
		
		if(indices) {
			this.renderType = "indexed";
		
			//subMeshObject.indices = indices;

			this.vertexIndexBuffer = this.gl.createBuffer();
			
			this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer);
			
			//if(kepler.extensions.elementIndexUint)
			//	this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(subMeshObject.indices), this.gl.STATIC_DRAW);
			//else
				this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), this.gl.STATIC_DRAW);

				
			this.vertexIndexBuffer.itemSize = 3;
			this.vertexIndexBuffer.numItems = indices.length;
			this.vertexIndexBuffer.data = indices;
			
		} else {
			this.renderType = "array";
		}

		//subMeshObject.vertices = vertices;

		this.vertexPositionBuffer = this.gl.createBuffer();
		this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexPositionBuffer);
		this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(vertices), this.gl.STATIC_DRAW);
		this.vertexPositionBuffer.itemSize = 3;
		this.vertexPositionBuffer.numItems = vertices.length / 3;
		this.vertexPositionBuffer.data = vertices;
		
		if(uvs) {
			//subMeshObject.textcoords = uvs;
			
			this.textureCoordBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureCoordBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(uvs), this.gl.STATIC_DRAW);
			this.textureCoordBuffer.itemSize = 2;
			this.textureCoordBuffer.numItems = uvs.length / 2;
			this.textureCoordBuffer.data = uvs;
		}
		
		
		if(normals) {
			//subMeshObject.normals = normals;
			
			this.vertexNormalBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexNormalBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(normals), this.gl.STATIC_DRAW);
			this.vertexNormalBuffer.itemSize = 3;
			this.vertexNormalBuffer.numItems = normals.length / 3;
			this.vertexNormalBuffer.data = normals;
		}

		
		
		if(tangents) {
			//subMeshObject.tangents = tangents;
			
			this.tangentBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.tangentBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(tangents), this.gl.STATIC_DRAW);
			this.tangentBuffer.itemSize = 3;
			this.tangentBuffer.numItems = tangents.length / 3;
			this.tangentBuffer.data = tangents;
		} else {
			
			this.createTangentAndBinormal();
			
		}
		
		if(binormals) {
			this.binormalBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.binormalBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(binormals), this.gl.STATIC_DRAW);
			this.binormalBuffer.itemSize = 3;
			this.binormalBuffer.numItems = binormals.length / 3;
			this.binormalBuffer.data = binormals;
		}
		
		*/

		//this.subMeshes.push(subMeshObject);
		

		this.boundingBox = new boundingBox();
		this.boundingBox.fitBoxToPoints_( vertices  );
		
	}
	
	createBuffers() {
		
		if(!this.createdBuffers) {
			
			if(this.indices) {
				
				this.renderType = "indexed";
			
				this.vertexIndexBuffer = this.gl.createBuffer();
				
				this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer);
				
				//if(kepler.extensions.elementIndexUint)
				//	this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(subMeshObject.indices), this.gl.STATIC_DRAW);
				//else
					this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(this.indices), this.gl.STATIC_DRAW);

					
				this.vertexIndexBuffer.itemSize = this.shape;
				this.vertexIndexBuffer.numItems = this.indices.length;
				this.vertexIndexBuffer.data = this.indices;
				
			} else {
				this.renderType = "array";
			}

			this.vertexPositionBuffer = this.gl.createBuffer();
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexPositionBuffer);
			this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.vertices), this.gl.STATIC_DRAW);
			this.vertexPositionBuffer.itemSize = 3;
			this.vertexPositionBuffer.numItems = this.vertices.length / 3;
			this.vertexPositionBuffer.data = this.vertices;
			
			if(this.textureCoordinates) {

				this.textureCoordBuffer = this.gl.createBuffer();
				this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureCoordBuffer);
				this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.textureCoordinates), this.gl.STATIC_DRAW);
				this.textureCoordBuffer.itemSize = 2;
				this.textureCoordBuffer.numItems = this.textureCoordinates.length / 2;
				this.textureCoordBuffer.data = this.textureCoordinates;
			}
			
			
			if(this.normals) {

				this.vertexNormalBuffer = this.gl.createBuffer();
				this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexNormalBuffer);
				this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.normals), this.gl.STATIC_DRAW);
				this.vertexNormalBuffer.itemSize = 3;
				this.vertexNormalBuffer.numItems = this.normals.length / 3;
				this.vertexNormalBuffer.data = this.normals;
			}

			
			
			if(this.tangents) {

				this.tangentBuffer = this.gl.createBuffer();
				this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.tangentBuffer);
				this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.tangents), this.gl.STATIC_DRAW);
				this.tangentBuffer.itemSize = 3;
				this.tangentBuffer.numItems = this.tangents.length / 3;
				this.tangentBuffer.data = this.tangents;
			} else {
				if(this.draw_type != this.gl.LINES)
					this.createTangentAndBinormal();
				
			}
			
			if(this.binormals) {
				
				this.binormalBuffer = this.gl.createBuffer();
				this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.binormalBuffer);
				this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.binormals), this.gl.STATIC_DRAW);
				this.binormalBuffer.itemSize = 3;
				this.binormalBuffer.numItems = this.binormals.length / 3;
				this.binormalBuffer.data = this.binormals;
			}
			
			this.createdBuffers = true;
		}
		
	}

	/**
	 * write content to ext console
	 * @param {(string)} content
	**/
	writeConsole(content) {
		top.consoleRef=window.open('','myconsole',
		'width=350,height=250'
		+',menubar=0'
		+',toolbar=1'
		+',status=0'
		+',scrollbars=1'
		+',resizable=1')
		top.consoleRef.document.writeln(
		'<html><head><title>Console</title></head>'
		+'<body bgcolor=white onLoad="self.focus()">'
		+content
		+'</body></html>'
		)
		top.consoleRef.document.close()
	}


	/**
	 * Show tangent
	 * @param {(subMeshObject)} 
	**/
	showTangent( subMeshObject ) {
		writeConsole( this.fileName + ' Tangent ' + JSON.stringify(this.tangentBuffer.data) );
	}


	/**
	 * Show Binormal
	 * @param {(subMeshObject)} 
	**/
	showBinormal( subMeshObject ) {
		var tangentArray = this.binormalBuffer.data;
		var a = [];
		for(var c = 0; c<tangentArray.length;c++){
		
			var s = tangentArray[c];

			a[c] = parseFloat(s.toFixed(5));
		}
		
		writeConsole( this.fileName + ' binormal ' + JSON.stringify(a) );
	}

	/**
	 * add subMeshObject to mesh
	 * @param {(subMeshObject)} 
	**/
	addsubMeshObject( subMeshObject ) {

		this.vertexIndexBuffer = subMeshObject.indexBuffer;
		this.vertexPositionBuffer = subMeshObject.vertexBuffer;
		this.vertexNormalBuffer = subMeshObject.normalBuffer;
		this.textureCoordBuffer = subMeshObject.uvBuffer;
		this.tangentBuffer = subMeshObject.tangentBuffer;
		this.binormalBuffer = subMeshObject.binormalBuffer;
		
		this.subMeshes.push(subMeshObject);
	}
}

export {mesh as default};

