package classes.away3d.core.filter
{
	import classes.away3d.arcane;
	import classes.away3d.containers.*;
	import classes.away3d.core.base.*;
	import classes.away3d.core.clip.*;
	import classes.away3d.core.render.*;
    import classes.away3d.core.utils.*;
	import classes.away3d.core.vos.*;
    import classes.away3d.materials.*;
	
	use namespace arcane;
	
    /**
    * Adds fog layers to a view and provides automatic farfield filtering for primitives outside the furthest fog layers.
	*/
    public class FogFilter implements IPrimitiveFilter
    {
    	private var i:int;
    	private var _order:Vector.<uint>;
    	private var _sub:uint;
    	private var _primitives:Vector.<uint>;
    	private var _material:ColorMaterial;
    	private var _minZ:Number;
    	private var _maxZ:int;
    	private var _minT:uint;
    	private var _subdivisions:int;
    	private var _materials:Array;
    	private var _fogVOs:Vector.<FogVO>;
 		private var _materialsDirty:Boolean = true;
 		
 		private function updateMaterials():void
 		{
 			_materialsDirty = false;
 			
			//materials override subdivisions
            if (!_materials.length) {
            	i = _sub = _subdivisions;
            	while (i--)
            		_materials.push(_material.clone());
            } else {
            	_sub = _materials.length;
            }
            
            i = _sub;
            
            _fogVOs = new Vector.<FogVO>();
			var fog:FogVO;
            while(i--) {
            	(_materials[i] as ColorMaterial).alpha = 0.45*(_sub - i)/_sub;
            	fog = new FogVO();
            	fog.screenZ = _maxZ + (_minZ - _maxZ)*i/(_sub - 1);
            	fog.material = _materials[i];
            	_fogVOs.push(fog);
            }
 		}
 		
        /**
        * Instance of the Init object used to hold and parse default property values
        * specified by the initialiser object in the 3d object constructor.
        */
		protected var ini:Init;
		
		/**
		 * Defines the material used by the fog layers.
		 */
		public function get material():ColorMaterial
		{
			return _material;
		}
		
		public function set material(val:ColorMaterial):void
		{
			if (!(val is ColorMaterial))
            	throw new Error("FogFilter requires IFogMaterial");
            
			_material = val;
			
			_materialsDirty = true;
		}
		
		/**
		 * Defines the minimum distance (start distance) of the fog layers.
		 */
		public function get minZ():Number
		{
			return _minZ;
		}
		
		public function set minZ(val:Number):void
		{
			if (_minZ == val)
				return;
			
			_minZ = val;
			
			_materialsDirty = true;
		}
		
		/**
		 * Defines the maximum distance (end distance) of the fog layers.
		 */
		public function get maxZ():Number
		{
			return _maxZ;
		}
		
		public function set maxZ(val:Number):void
		{
			if (_maxZ == val)
				return;
			
			_maxZ = val;
			
			_materialsDirty = true;
		}
		
		/**
		 * Defines the maximum distance (end distance) of the fog layers.
		 */
		public function get subdivisions():Number
		{
			return _subdivisions;
		}
		
		public function set subdivisions(val:Number):void
		{
			if (_subdivisions == val)
				return;
			
			_subdivisions = val;
			
			_materialsDirty = true;
		}
		
		/**
		 * Defines an array of materials used by the fog layers (overrides material and subdivisions).
		 */
		public function get materials():Array
		{
			return _materials;
		}
		
		public function set materials(val:Array):void
		{
			_materials = val;
			
			_materialsDirty = true;
		}
		
		/**
		 * Creates a new <code>FogFilter</code> object.
		 *
		 * @param	init			[optional]	An initialisation object for specifying default instance properties.
		 */
		public function FogFilter(init:Object = null):void
		{
			ini = Init.parse(init);
			
			_material = ini.getMaterial("material") as ColorMaterial || new ColorMaterial(0x000000);
			_minZ = ini.getNumber("minZ", 1000, {min:0});
            _maxZ = ini.getNumber("maxZ", 5000, {min:0});
            _subdivisions = ini.getInt("subdivisions", 20, {min:1, max:50});
            _materials = ini.getArray("materials");
		}
		
		/**
		 * Allows color change at runtime of the filter
		 * @param	color			The new color for the filter
		 */
		public function updateMaterialColor(color:uint):void
		{
			i = _sub;
			while(i--)
            	(_materials[i] as ColorMaterial).color = color;
		}
		
		/**
		 * @inheritDoc
		 */
        public function filter(renderer:Renderer):void
        {
        	if (_materialsDirty)
        		updateMaterials();
        	
        	var source:Object3D = renderer.primitiveSource[renderer._primitives[0]].source;
        	var view:View3D = renderer._view;
        	var scene:Scene3D = view.scene;
        	var clip:Clipping = view.screenClipping;
        	if (!renderer._primitives.length || !renderer.primitiveSource[renderer._primitives[0]].source || source.session != scene.session)
        		return;
			
			_order = renderer._order;
			_primitives = renderer._primitives;
			_minT = renderer._coeffScreenT/_maxZ;
			var i:uint = 0;
			var j:uint = 0;
			var screenT:Number;
			var fogT:Number = renderer._coeffScreenT/_fogVOs[j].screenZ;
			var orderLength:Number = _order.length;
			var fogLength:Number = _fogVOs.length;
			while (i < orderLength) {
				screenT = renderer._screenTs[_order[i]];
				if (j < fogLength && screenT < fogT) {
					_order.splice(i, 0, _primitives.length);
					_primitives.push(renderer.createDrawFog(_fogVOs[j], clip));
					j++;
					if (j < fogLength)
						fogT = renderer._coeffScreenT/_fogVOs[j].screenZ;
				}
				if (screenT < _minT) {
					_order.splice(i, _order.length - i);
					break;
				}
				i++;
			}
        }
		
		/**
		 * Used to trace the values of a filter.
		 * 
		 * @return A string representation of the filter object.
		 */
        public function toString():String
        {
            return "FogFilter";
        }
    }
}
