package classes.away3d.materials.shaders
{
	import classes.away3d.arcane;
	import classes.away3d.containers.*;
	import classes.away3d.core.base.*;
	import classes.away3d.core.render.*;
	import classes.away3d.core.utils.*;
	import classes.away3d.lights.*;	
	
	import flash.display.*;
	import flash.geom.*;
	
	use namespace arcane;
	
	/**
	 * Diffuse shader class for directional lighting.
	 * 
	 * @see away3d.lights.DirectionalLight3D
	 */
    public class DiffusePhongShader extends AbstractShader
    {
		/** @private */
        arcane override function updateMaterial(source:Object3D, view:View3D):void
        {
        	var _source_scene_directionalLights:Array = source.scene.directionalLights;
        	for each (var directional:DirectionalLight3D in _source_scene_directionalLights) {
        		if (!directional.diffuseTransform[source] || view._updatedObjects[source]) {
        			directional.setDiffuseTransform(source);
        			updateFaces(source, view);
        		}
        	}
        }
		/** @private */
        arcane override function renderLayer(priIndex:uint, viewSourceObject:ViewSourceObject, renderer:Renderer, layer:Sprite, level:int):int
        {
        	super.renderLayer(priIndex, viewSourceObject, renderer, layer, level);
        	
        	var _source_scene_directionalLights:Array = _source.scene.directionalLights;
        	for each (var directional:DirectionalLight3D in _source_scene_directionalLights)
        	{
        		if (_source.scene.numLights > 1) {
					_shape = _session.getLightShape(this, level++, layer, directional);
		        	_shape.blendMode = blendMode;
		        	_graphics = _shape.graphics;
		        } else {
		        	_graphics = layer.graphics;
		        }
        		
        		_diffuseTransform = directional.diffuseTransform[_source];
				
				_n0 = _source.geometry.getVertexNormal(_face.v0);
				_n1 = _source.geometry.getVertexNormal(_face.v1);
				_n2 = _source.geometry.getVertexNormal(_face.v2);
				
				_szx = _diffuseTransform.rawData[2];
				_szy = _diffuseTransform.rawData[6];
				_szz = _diffuseTransform.rawData[10];
				
				_normal0z = _n0.x * _szx + _n0.y * _szy + _n0.z * _szz;
				_normal1z = _n1.x * _szx + _n1.y * _szy + _n1.z * _szz;
				_normal2z = _n2.x * _szx + _n2.y * _szy + _n2.z * _szz;
	            
				_source.session.renderTriangleBitmap(directional.ambientDiffuseBitmap, getUVData(priIndex), _screenVertices, _screenIndices, _startIndex, _endIndex, smooth, false, _graphics);
        	}
			
			if (debug)
                _source.session.renderTriangleLine(0, 0x0000FF, 1, _screenVertices, renderer.primitiveCommands[priIndex], _screenIndices, _startIndex, _endIndex);
            
            return level;
        }
        
		private var _diffuseTransform:Matrix3D;
		private var _szx:Number;
		private var _szy:Number;
		private var _szz:Number;
		private var _normal0z:Number;
		private var _normal1z:Number;
		private var _normal2z:Number;
		private var eTriConst1:Number = 512/Math.PI;
        private var eTriConst2:Number = 2/Math.PI;
		/**
		 * @inheritDoc
		 */
        protected function updateFaces(source:Object3D, view:View3D):void
        {
        	view;//TODO : FDT Warning
        	notifyMaterialUpdate();
        	
        	for each (var _faceMaterialVO:FaceMaterialVO in _faceDictionary) {
        		if (source == _faceMaterialVO.source) {
	        		if (!_faceMaterialVO.cleared)
	        			_faceMaterialVO.clear();
	        		_faceMaterialVO.invalidated = true;
	        	}
        	}
        }
		
		protected override function calcUVT(priIndex:uint, uvt:Vector.<Number>):Vector.<Number>
		{
			priIndex;
			
			uvt[uint(0)] = eTriConst2*Math.acos(_normal0z);
    		uvt[uint(1)] = 0;
    		uvt[uint(3)] = eTriConst2*Math.acos(_normal1z) ;
    		uvt[uint(4)] = 0.5;
    		uvt[uint(6)] = eTriConst2*Math.acos(_normal2z);
    		uvt[uint(7)] = 1;
    		
    		return uvt;
		}
		
		protected override function calcMapping(priIndex:uint, map:Matrix):Matrix
		{
			priIndex;
			
			map.a = eTriConst1*Math.acos(_normal1z) - eTri0x;
			map.b = 127;
			map.c = eTriConst1*Math.acos(_normal2z) - eTri0x;
			map.d = 255;
			map.tx = eTri0x;
			map.ty = 0;
            map.invert();
            
            return map;
		}
		
		/**
		 * @inheritDoc
		 */
        protected override function renderShader(priIndex:uint):void
        {
			_n0 = _source.geometry.getVertexNormal(_face.v0);
			_n1 = _source.geometry.getVertexNormal(_face.v1);
			_n2 = _source.geometry.getVertexNormal(_face.v2);
			
			var _source_scene_directionalLights:Array = _source.scene.directionalLights;
			for each (var directional:DirectionalLight3D in _source_scene_directionalLights)
	    	{
				_diffuseTransform = directional.diffuseTransform[_source];
				
				_szx = _diffuseTransform.rawData[2];
				_szy = _diffuseTransform.rawData[6];
				_szz = _diffuseTransform.rawData[10];
				
				_normal0z = _n0.x * _szx + _n0.y * _szy + _n0.z * _szz;
				_normal1z = _n1.x * _szx + _n1.y * _szy + _n1.z * _szz;
				_normal2z = _n2.x * _szx + _n2.y * _szy + _n2.z * _szz;
				
				//check to see if the uv triangle lies inside the bitmap area
				if (_normal0z > 0 || _normal1z > 0 || _normal2z > 0) {
					
					eTri0x = eTriConst1*Math.acos(_normal0z);
					
					//store a clone
					if (_faceMaterialVO.cleared && !_parentFaceMaterialVO.updated) {
						_faceMaterialVO.bitmap = _parentFaceMaterialVO.bitmap.clone();
						_faceMaterialVO.bitmap.lock();
					}
					
					_faceMaterialVO.cleared = false;
					_faceMaterialVO.updated = true;
					
					//calulate mapping
					_mapping = getMapping(priIndex);
		            _mapping.concat(_faceMaterialVO.invtexturemapping);
		            
					//draw into faceBitmap
					_graphics = _s.graphics;
					_graphics.clear();
					_graphics.beginBitmapFill(directional.diffuseBitmap, _mapping, false, smooth);
					_graphics.drawRect(0, 0, _bitmapRect.width, _bitmapRect.height);
		            _graphics.endFill();
					_faceMaterialVO.bitmap.draw(_s, null, null, blendMode);
					//_faceMaterialVO.bitmap.draw(directional.diffuseBitmap, _mapping, null, blendMode, _faceMaterialVO.bitmap.rect, smooth);
				}
	    	}
        }
        
		/**
		 * Creates a new <code>DiffusePhongShader</code> object.
		 * 
		 * @param	init	[optional]	An initialisation object for specifying default instance properties.
		 */
        public function DiffusePhongShader(init:Object = null)
        {
        	super(init);
        }
    }
}
