/*******************************************************************************
 * Copyright (c) 2012 by Thomas Jahn
 * Questions? Mail me at lithander@gmx.de!
 ******************************************************************************/
package
{
	import net.pixelpracht.geometry.Angle;
	import net.pixelpracht.geometry.Vector2D;
	import net.pixelpracht.graphics.Color;
	import net.pixelpracht.geometry.Sector;

	public class LightGridData
	{
		public var opaque:Boolean = false;
		
		//color
		private var _color:Color = new Color();
		
		//spatial data
		public var x:int = 0;
		public var y:int = 0;
		
		public var left:LightGridData;
		public var right:LightGridData;
		public var up:LightGridData;
		public var down:LightGridData;
		
		//used for raycasting
		public var touched:uint = 0;
		
		//occlusion
		public var occlusion:Number = UNKNOWN_OCCLUSION;
		public static const UNKNOWN_OCCLUSION:int = -1;
		private var _lightSector:Sector = new Sector();
		private var _incomingA:Sector = new Sector();
		private var _incomingB:Sector = new Sector();
		
		public function LightGridData(cx:int, cy:int)
		{
			x = cx;
			y = cy;
		}
		
		//COLOR
		
		public function get color():uint
		{
			if(opaque)
				return 0x7080C0;
			else
				return _color.rgba;						
		}
		
		public function clear(c:uint):void
		{
			_color.red = (c >> 16 & 0xFF) / 255;
			_color.green = (c >> 8 & 0xFF) / 255;
			_color.blue = (c & 0xFF) / 255;
		}
		
		public function addScaled(c:uint, sf:Number):void
		{
			_color.red += sf * (c >> 16 & 0xFF) / 255.0;
			_color.green += sf * (c >> 8 & 0xFF) / 255.0;
			_color.blue += sf * (c & 0xFF) / 255.0;
		}
		
		public function resetOcclusion():void
		{
			occlusion = UNKNOWN_OCCLUSION;				
		}
		
		//evaluate this cells occlusion (ITERATOR version)		
		public function calcOcclusion(pos:Vector2D, normalize:Boolean):void
		{
			if(opaque) //this cell itself occludes all light
			{
				_lightSector.clear();
				occlusion = 1.0;
				return;
			}
			
			//vector (dx, dy) describes relative position from center
			var dx:int = Math.ceil(pos.x) - x - 1;
			var dy:int = Math.ceil(pos.y) - y - 1;					
			//sign of vector components
			var sx:Number = (dx == 0) ? 0 : (dx < 0) ? -1 : 1;
			var sy:Number = (dy == 0) ? 0 : (dy < 0) ? -1 : 1;
			//offset necessary for vertical/horizontal lines
			var ox:Number = (sy == 0) ? sx : 0
			var oy:Number = (sx == 0) ? sy : 0
			//calculate the points that define the sector
			var x1:Number = x + 0.5 * (1 - sy + ox);
			var y1:Number = y + 0.5 * (1 + sx + oy);
			var x2:Number = x + 0.5 * (1 + sy + ox);
			var y2:Number = y + 0.5 * (1 - sx + oy);
			//calculate sector
			_lightSector.setFromCoords(pos.x, pos.y, x1, y1, x2, y2, normalize);			
			if(Math.abs(dx) + Math.abs(dy) > 1) //no direct connection with lightsource - there might be occlusion
			{
				//gather horizontal
				if(dx > 0 && right != null)
					_incomingA.setIntersection(_lightSector, right._lightSector);	
				else if(dx < 0 && left != null)
					_incomingA.setIntersection(_lightSector, left._lightSector);	
				else
					_incomingA.clear();
				
				//gather vertical
				if(dy > 0 && down != null)
					_incomingB.setIntersection(_lightSector, down._lightSector);	
				else if(dy < 0 && up != null)
					_incomingB.setIntersection(_lightSector, up._lightSector);	
				else
					_incomingB.clear();
				
				//combine exposure from both edges and compare with max possible exposure (myTheta)
				var myTheta:Number = _lightSector.theta;
				_lightSector.setUnion(_incomingA, _incomingB);
				occlusion = 1.0 - (_lightSector.theta / myTheta);					
			}
			else
				occlusion = 0.0; //ends recursion				
		}
		
		//evaluate this cells occlusion (RECURSIVE version)		
		public function calclOcclusionRecursive(pos:Vector2D, normalize:Boolean):void
		{
			if(occlusion != UNKNOWN_OCCLUSION)
				return;
			
			//offset relative to the center position
			var dx:int = Math.ceil(pos.x) - x - 1;
			var dy:int = Math.ceil(pos.y) - y - 1;					
			
			//make sure relevant neighbours are evaluated first
			var a:LightGridData = null;
			if(dx > 0)
				a = right;
			else if (dx < 0)
				a = left;
			
			if(a != null)
				a.calclOcclusionRecursive(pos, normalize);

			var b:LightGridData = null;
			if(dy > 0)
				b = down;
			else if(dy < 0)
				b = up;

			if(b != null)
				b.calclOcclusionRecursive(pos, normalize);	
			
			//the rest is similar to the iterator version...
			
			if(opaque)
			{
				_lightSector.clear();
				occlusion = 1.0;
				return;
			}
			var sx:Number = (dx == 0) ? 0 : (dx < 0) ? -1 : 1;
			var sy:Number = (dy == 0) ? 0 : (dy < 0) ? -1 : 1;
			var ox:Number = (sy == 0) ? sx : 0
			var oy:Number = (sx == 0) ? sy : 0
			var x1:Number = x + 0.5 * (1 - sy + ox);
			var y1:Number = y + 0.5 * (1 + sx + oy);
			var x2:Number = x + 0.5 * (1 + sy + ox);
			var y2:Number = y + 0.5 * (1 - sx + oy);
			_lightSector.setFromCoords(pos.x, pos.y, x1, y1, x2, y2, normalize);			
			if(Math.abs(dx) + Math.abs(dy) > 1) //no direct connection with lightsource - there might be occlusion
			{
				//evaluate right
				if(a != null)
					_incomingA.setIntersection(_lightSector, a._lightSector);	
				else
					_incomingA.clear();
				
				//evaluate down
				if(b != null)
					_incomingB.setIntersection(_lightSector, b._lightSector);	
				else
					_incomingB.clear();
				
				//combine exposure from both edges and compare with max possible exposure (myTheta)
				var myTheta:Number = _lightSector.theta;
				_lightSector.setUnion(_incomingA, _incomingB);
				occlusion = 1.0 - (_lightSector.theta / myTheta);					
			}
			else
				occlusion = 0.0; //ends recursion				
		}
	}
}