/*
 * Copyright notice
 *
 * Copyright (c) 2012-2014 Sebastian F. Mazza <sebastian@mazza.at>
 * All rights reserved.
 *
 * This file is part of MMC.
 */

/**
 * 
 * Sigelton factory class for three.js shaders.
 * The class can only be used after the class CutawayRenderer.util.ShaderUtil has fired the CutawayRenderer.util.ShaderUtil#ready event.
 * 
 * Sample usage:
 *		
 *		CutawayRenderer.util.ShaderUtil.waitForReady(frunction() {
 *			var myShader =  CutawayRenderer.util.MaterialFactory.getDrawTexture();
 *		});
 * 
 */
Ext.define('CutawayRenderer.util.MaterialFactory', {
	//mixins: ['Ext.mixin.Observable'],
	singleton : true,
	requires: [
		'CutawayRenderer.util.ShaderUtil'
	],
	
	//materials: {},
	
	/**
	 * 
	 * @param {Object} config
	 */
	constructor: function (config) {
		var me = this;
		//this.initConfig(config);
		//this.mixins.observable.constructor.call(this, config);
		//CutawayRenderer.util.ShaderUtil.waitForReady(function(){	
		//});
	},
	
	/*
	getMaterial: function(matName, vertName, fragName) {
		if(!this.materials[name]) {
			
		}
		
		return this.materials[name];
	}
	*/
   
    /**
	 * Creates a generic THREE.ShaderMaterial with vertex and fragment shader codes spesified by its names 
	 * (see: CutawayRenderer.util.ShaderUtil.files).
	 * 
	 * @param {String} vertName name of the vertex shader code provided by CutawayRenderer.util.ShaderUtil
	 * @param {String} fragName  name of the fragment shader code provided by CutawayRenderer.util.ShaderUtil
	 * @returns {THREE.ShaderMaterial}
	 */
	createShaderMaterial: function(vertName, fragName) {
		var vertexShader = CutawayRenderer.util.ShaderUtil.getCode(vertName);
		var fragmentShader = CutawayRenderer.util.ShaderUtil.getCode(fragName);
		var material = new THREE.ShaderMaterial({
			vertexShader: vertexShader,
			fragmentShader: fragmentShader
			//uniforms: {
					//cameraNear: { value: this.hCamera.near },
					//cameraFar:  { value: this.hCamera.far },
					//tex: new THREE.Uniform(new THREE.Texture())
					//tDepth:     { value: target.depthTexture }
			//},
			//depthTest: false,
			//depthWrite: false
        });
		
		return material;
	},
   
	/**
	 * Creates a debug shader for copieing textures to a render target.
	 * 
	 * @returns {THREE.ShaderMaterial}
	 * @protected
	 */
	getDrawTexture: function() {
		var material = this.createShaderMaterial('drawTexture_vert', 'drawTexture_frag');
		material.uniforms = {
				//cameraNear: { value: this.hCamera.near },
				//cameraFar:  { value: this.hCamera.far },
				tex: new THREE.Uniform(new THREE.Texture())
				//tDepth:     { value: target.depthTexture }
		};
		material.depthTest = false;
		material.depthWrite = false;
		
		return material;
	},
	
	/**
	 * Creates a debug shader for copieing rgb textures to a render target.
	 * 
	 * @returns {THREE.ShaderMaterial}
	 */
	getDrawRgbTexture: function() {
		var material = this.getDrawTexture();
		material.defines.DRAW_RGB = true;
		return material;
	},
	
	/**
	 * Creates a debug shader for copieing depth textures to a render target.
	 * 
	 * @returns {THREE.ShaderMaterial}
	 */
	getDrawDepthTexture: function() {
		var material = this.getDrawTexture();
		material.defines.DRAW_DEPTH = true;
		return material;
	},
	
	/**
	 * Creates an shader that renders the backfaces of the objects that ar most fare away from the camera to the depth buffer.
	 * Therefor it uses an reverse depth test.
	 * 
	 * @returns {THREE.MeshDepthMaterial}
	 */
	getInverseDepthMaterial: function() {
		var material = new THREE.MeshDepthMaterial();
		material.depthFunc = THREE.GreaterDepth;
			//	THREE.NeverDepth
			//	THREE.AlwaysDepth
			//	THREE.LessDepth
			//	THREE.LessEqualDepth
			//	THREE.GreaterEqualDepth
			//	THREE.GreaterDepth
			//	THREE.NotEqualDepth
		material.depthTest = true;
		material.depthWrite = true;
		material.colorWrite = true;
		material.side = THREE.BackSide; // THREE.FrontSide, THREE.BackSide and THREE.DoubleSide
		
		return material;
	},
        
	/**
	 * Returns an THREE.RawShaderMaterial that performes one step of the jump-flood algorithem.
	 * see CutawayRenderer.service.CutawaySurface for details.
	 * 
	 * @returns {THREE.RawShaderMaterial}
	 */
	getJumpFloodMaterial: function() {
		var vertexShader = CutawayRenderer.util.ShaderUtil.getCode('jumpFlood_vert');
		var fragmentShader = CutawayRenderer.util.ShaderUtil.getCode('jumpFlood_frag');
		var material = new THREE.RawShaderMaterial({
			vertexShader: vertexShader,
			fragmentShader: fragmentShader
		});
		//material.extensions
		//var material = this.createShaderMaterial('jumpFlood_vert', 'jumpFlood_frag');
		
		material.uniforms = {
			//cameraNear: { value: this.hCamera.near },
			//cameraFar:  { value: this.hCamera.far },
			//texIn: new THREE.Uniform(new THREE.Texture()),
			//texOut: new THREE.Uniform(new THREE.Texture())

			//tDepth:     { value: target.depthTexture }
			initialize: new THREE.Uniform(false),
			tex: new THREE.Uniform(new THREE.Texture()),
			texCol: new THREE.Uniform(new THREE.Texture()),
			testWidth: new THREE.Uniform(1.0),
			aspectRatioCorr: new THREE.Uniform(1.0),
			slopeDenom: new THREE.Uniform(0.5),
			slopePM: new THREE.Uniform(1.0)
		};

		//material.type  = 'JumpFloodMaterial';
		material.depthFunc = THREE.GreaterEqualDepth;
		material.depthTest = true;
		material.depthWrite = true;
		material.colorWrite = true;

		return material;
	},
	
	/**
	 * Returns a physical material that is based on THREE.MeshStandardMaterial.
	 * It is extendet by the posibliiti to render cutaways and cutridges.
	 * 
	 * Type can be checked by:
	 *		
	 *		if(material.type == 'MeshStandardCutawayMaterial') {
	 *		}
	 *		
	 * Additional uniforms:
	 *		
	 *		uCutawayTest = new THREE.Uniform(true);
	 *		uCutawaySurfaceTex = new THREE.Uniform(new THREE.Texture());
	 *		
	 *		uRenderCutRidge = new THREE.Uniform(false);
	 *		uUseCutRidgeColor = new THREE.Uniform(false);
	 *		uCutRidgeColor = new THREE.Uniform(new THREE.Color(0xff0000));
	 *		uViewProjectionInverse = new THREE.Uniform(new THREE.Matrix4());
	 *		uScreenSize = new THREE.Uniform(new THREE.Vector2( 100, 100));
	 *		uViewNormalMatrix = new THREE.Uniform(new THREE.Matrix3());
	 *				
	 * 
	 * @param {Object} config see: THREE.MeshStandardMaterial constructor
	 * @returns {THREE.MeshStandardMaterial}
	 */
	getStandardCutawayMaterial: function(config) {
		//var material = this.createShaderMaterial('drawTexture_vert', 'drawTexture_frag');
		// see THREE.WebGLRenderer.refreshUniformsCommon()
		// 
		// see THREE.WebGLRenderer.refreshUniformsStandard()
		// MeshStandardMaterial
		/*material.uniforms = {
				roughness: new THREE.Uniform(0.5),
				metalness: new THREE.Uniform(0.5),
				roughnessMap: new THREE.Uniform(null),
				metalnessMap: new THREE.Uniform(null),
				emissiveMap: new THREE.Uniform(null),
				bumpMap: new THREE.Uniform(null),
				bumpScale: new THREE.Uniform(null),
				normalMap: new THREE.Uniform(null),
				normalScale: new THREE.Uniform(null),
				displacementMap: new THREE.Uniform(null),
				displacementScale: new THREE.Uniform(null),
				displacementBias: new THREE.Uniform(null),
				envMapIntensity: new THREE.Uniform(null),
		};*/
		
		var material = new THREE.MeshStandardMaterial(config);
		material.type  = 'MeshStandardCutawayMaterial'; // damit der renderer die shaderID nicht findet und meinen eigen shafer cod ladet
		
		material.vertexShader = CutawayRenderer.util.ShaderUtil.getCode('meshPhysicalCutaway_vert');
		material.fragmentShader = CutawayRenderer.util.ShaderUtil.getCode('meshPhysicalCutaway_frag');
		
		var defaultShader = THREE.ShaderLib['standard'];
		material.uniforms = THREE.UniformsUtils.clone( defaultShader.uniforms );
		
		
		material.uniforms.uCutawayTest = new THREE.Uniform(true);
		material.uniforms.uCutawaySurfaceTex = new THREE.Uniform(new THREE.Texture());
		
		material.uniforms.uRenderCutRidge = new THREE.Uniform(false);
		material.uniforms.uUseCutRidgeColor = new THREE.Uniform(false);
		material.uniforms.uCutRidgeColor = new THREE.Uniform(new THREE.Color(0xff0000));
		
		material.uniforms.uViewProjectionInverse = new THREE.Uniform(new THREE.Matrix4());
		material.uniforms.uScreenSize = new THREE.Uniform(new THREE.Vector2( 100, 100));
		material.uniforms.uViewNormalMatrix = new THREE.Uniform(new THREE.Matrix3());
		
		return material;
	}
});