package classes.away3d.lights
{
	import classes.away3d.arcane;
	import classes.away3d.containers.*;
	import classes.away3d.core.base.*;
	import classes.away3d.core.utils.*;
	import classes.away3d.events.*;
	import classes.away3d.materials.*;
	import classes.away3d.primitives.*;
	
	import flash.geom.*;
	import flash.utils.*;
	
	use namespace arcane;
	
    /**
    * Lightsource that colors all shaded materials proportional to the dot product of the distance vector with the normal vector.
    * The scalar value of the distance is used to calulate intensity using the inverse square law of attenuation.
    */
    public class PointLight3D extends AbstractLight
    {
    	/** @private */
		arcane var _vertex:Vertex = new Vertex();
		/**
		 * @private
         * Positions dictionary for the view positions used by shading materials.
         */
        arcane var viewPositions:Dictionary;
 		
        /**
         * @private
         * Updates the view position.
         */
        arcane function setViewPosition(view:View3D):void
        {
			viewPositions[view] = view.camera.viewMatrix.transformVector(_scenePosition);
        }
        
    	private var _position:Vector3D = new Vector3D();
        private var _radius:Number = 200;
        private var _falloff:Number = 1000;
    	private var _scenePosition:Vector3D = new Vector3D();
    	
        private var _innerDebugSphere:Sphere;
        private var _outerDebugSphere:Sphere;
        private var _debugMaterial:ColorMaterial;
		        
        protected override function addDebugPrimitive(parent:ObjectContainer3D):void
		{
			parent.addChild(_innerDebugSphere);
			parent.addChild(_outerDebugSphere);
		}
		
		protected override function removeDebugPrimitive(parent:ObjectContainer3D):void
		{
			parent.removeChild(_innerDebugSphere);
			parent.removeChild(_outerDebugSphere);
		}
		
		protected override function updateDebugPrimitive():void
		{
			if (!_innerDebugSphere)
				_innerDebugSphere = new Sphere();
			
			if (!_outerDebugSphere)
				_outerDebugSphere = new Sphere({segmentsW:10, segmentsH:8});
			
			if(!_debugMaterial) {
				_debugMaterial = new ColorMaterial();
				_debugMaterial.alpha = .15;
				_innerDebugSphere.material = _debugMaterial;
				_outerDebugSphere.material = _debugMaterial;
			}
			
			_innerDebugSphere.position = _position;
			_innerDebugSphere.radius = _radius;
			_outerDebugSphere.position = _position;
			_outerDebugSphere.radius = _falloff;
			_debugMaterial.color = _color;
		}
		
		protected override function onSceneTransformChange(event:Object3DEvent = null):void
        {
			_scenePosition = _parent.scenePosition.add(_position);
			
			viewPositions = new Dictionary(true);
        }
		
		/**
		 * Defines a coefficient for the ambient light intensity.
		 */
        public var ambient:Number;
		
		/**
		 * Defines a coefficient for the diffuse light intensity.
		 */
        public var diffuse:Number;
		
		/**
		 * Defines a coefficient for the specular light intensity.
		 */
        public var specular:Number;
		
		/**
		 * Defines a coefficient for the overall light intensity.
		 */
        public var brightness:Number;
		        
    	/**
    	 * Defines the x coordinate of the light relative to the local coordinates of the parent <code>ObjectContainer3D</code>.
    	 */
        public function get x():Number
        {
            return _vertex.x;
        }
    
        public function set x(value:Number):void
        {
            if (isNaN(value))
                throw new Error("isNaN(x)");
			
			if (_vertex.x == value)
				return;
			
            if (value == Infinity)
                Debug.warning("x == Infinity");

            if (value == -Infinity)
                Debug.warning("x == -Infinity");

            _vertex.x = _position.x = value;
            
            if (_parent)
            	onSceneTransformChange();
            
			if (_debug)
				updateDebugPrimitive();
        }
		
    	/**
    	 * Defines the y coordinate of the light relative to the local coordinates of the parent <code>ObjectContainer3D</code>.
    	 */
        public function get y():Number
        {
            return _vertex.y;
        }
    
        public function set y(value:Number):void
        {
            if (isNaN(value))
                throw new Error("isNaN(y)");
			
			if (_vertex.y == value)
				return;
			
            if (value == Infinity)
                Debug.warning("y == Infinity");

            if (value == -Infinity)
                Debug.warning("y == -Infinity");

            _vertex.y = _position.y = value;
            
            if (_parent)
            	onSceneTransformChange();
            
			if (_debug)
				updateDebugPrimitive();
        }
		
    	/**
    	 * Defines the z coordinate of the light relative to the local coordinates of the parent <code>ObjectContainer3D</code>.
    	 */
        public function get z():Number
        {
            return _vertex.z;
        }
    	
        public function set z(value:Number):void
        {
            if (isNaN(value))
                throw new Error("isNaN(z)");
			
			if (_vertex.z == value)
				return;
			
            if (value == Infinity)
                Debug.warning("z == Infinity");

            if (value == -Infinity)
                Debug.warning("z == -Infinity");

            _vertex.z = _position.z = value;
            
            if (_parent)
            	onSceneTransformChange();
            
			if (_debug)
				updateDebugPrimitive();
        }
                
    	/**
    	 * Defines the position of the light relative to the local coordinates of the parent <code>ObjectContainer3D</code>.
    	 */
        public function get position():Vector3D
        {
            return _position;
        }
		
        public function set position(value:Vector3D):void
        {
            _vertex.x = _position.x = value.x;
            _vertex.y = _position.y = value.y;
            _vertex.z = _position.z = value.z;
            
            if (_parent)
				onSceneTransformChange();
			
			if (_debug)
				updateDebugPrimitive();
        }
		
        
		/**
		 * Defines the radius of the light at full intensity, infleunced object get within this range full color of the light
		 */
		public function get radius():Number
        {
        	return _radius;
        }
        
        public function set radius(val:Number):void
        {
        	_radius = val;
			_falloff = (radius>_falloff)? radius+1 : _falloff;
			
			if (_debug)
				updateDebugPrimitive();
        }
		
		/**
		 * Defines the max length of the light rays, beyond this distance, light doesn't have influence
		 * the light values are from radius 100% to falloff 0%
		 */
        public function get fallOff():Number
        {
        	return _falloff;
        }
        
        public function set fallOff(val:Number):void
        {
        	_falloff = (radius>_falloff)? radius+1 : val;
        	
			if (_debug)
				updateDebugPrimitive();
        }
		
		public function get scenePosition():Vector3D
		{
			return _scenePosition;
		}
		
		/**
		 * Creates a new <code>PointLight3D</code> object.
		 * 
		 * @param	init	[optional]	An initialisation object for specifying default instance properties.
		 */
        public function PointLight3D(init:Object = null)
        {
            super(init);
            
            x = ini.getNumber("x", 0);
            y = ini.getNumber("y", 0);
            z = ini.getNumber("z", 0);
            ambient = ini.getNumber("ambient", 1);
            diffuse = ini.getNumber("diffuse", 1);
            specular = ini.getNumber("specular", 1);
            brightness = ini.getNumber("brightness", 1);
            
			_radius = ini.getNumber("radius", 50);
			_falloff = ini.getNumber("fallOff", 1000);
        }
		
		/**
		 * Duplicates the light object's properties to another <code>PointLight3D</code> object
		 * 
		 * @param	light	[optional]	The new light instance into which all properties are copied
		 * @return						The new light instance with duplicated properties applied
		 */
        public override function clone(light:AbstractLight = null):AbstractLight
        {
            var pointLight3D:PointLight3D = (light as PointLight3D) || new PointLight3D();
            super.clone(pointLight3D);
            pointLight3D.ambient = ambient;
            pointLight3D.diffuse = diffuse;
            pointLight3D.specular = specular;
			  
			pointLight3D.radius = _radius;
            pointLight3D.fallOff = _falloff;
			 
            return pointLight3D;
        }
    }
}