package classes.away3d.core.session
{
	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.core.vos.*;
	import classes.away3d.events.*;
	import classes.away3d.lights.*;
	import classes.away3d.materials.*;
	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.utils.*;
    
	use namespace arcane;
	
	/**
	 * Dispatched when the render contents of the session require updating.
	 * 
	 * @eventType away3d.events.SessionEvent
	 */
	[Event(name="sessionUpdated",type="away3d.events.SessionEvent")]
	
    /**
    * Abstract Drawing session object containing the method used for drawing the view to screen.
    * Not intended for direct use - use <code>SpriteRenderSession</code> or <code>BitmapRenderSession</code>.
    */
	public class AbstractSession extends EventDispatcher
	{
		/** @private */
		arcane var _containers:Dictionary = new Dictionary(true);
		/** @private */
		arcane var _shape:Shape;
		/** @private */
		arcane var _sprite:Sprite;
		/** @private */
		arcane var _level:int = -1;
		/** @private */
		arcane var _material:Material;
		/** @private */
        arcane var _renderSource:Object3D;
		/** @private */
        arcane var _layerDirty:Boolean;
		/** Array for storing old displayobjects to the canvas */
		arcane var _shapeStore:Array;
		/** Array for storing added displayobjects to the canvas */
		arcane var _shapeActive:Array;
		/** Array for storing old displayobjects to the canvas */
		arcane var _spriteStore:Array;
		/** Array for storing added displayobjects to the canvas */
		arcane var _spriteActive:Array;
		/** @private */
        arcane function updateSession():void
        {
        	if (!object3D.scene)
        		return;
        	
        	object3D.scene.flagSession(this);
        	
        	notifySessionUpdate();
        }
		
		private function notifySessionUpdate():void
		{
			if (_sessionDirty)
				return;
			
			_sessionDirty = true;
			
			if (this is BitmapSession && object3D)
				object3D.scene.flagSession(this);
			
			if (!hasEventListener(SessionEvent.SESSION_UPDATED))
                return;
			
            if (!_sessionupdated)
                _sessionupdated = new SessionEvent(SessionEvent.SESSION_UPDATED, this);
            
            dispatchEvent(_sessionupdated);
		}
		/** @private */
        arcane function internalAddSceneSession(session:AbstractSession):void
        {
        	sessions = [session];
        	session.addOnSessionUpdate(onSessionUpdate);
        }
		/** @private */
        arcane function internalRemoveSceneSession(session:AbstractSession):void
        {
        	sessions = [];
        	session.removeOnSessionUpdate(onSessionUpdate);
        }
        
        private var _shapeStores:Dictionary = new Dictionary(true);
        private var _shapeActives:Dictionary = new Dictionary(true);
        private var _spriteStores:Dictionary = new Dictionary(true);
        private var _spriteActives:Dictionary = new Dictionary(true);
        private var _spriteLayers:Dictionary = new Dictionary(true);
        private var _spriteLayer:Dictionary;
        private var _shapeLayers:Dictionary = new Dictionary(true);
        private var _shapeLayer:Dictionary;
        private var _lightShapeLayers:Dictionary = new Dictionary(true);
        private var _lightShapeLayer:Dictionary;
        private var _dictionary:Dictionary;
        private var _array:Array;
        private var _defaultColorTransform:ColorTransform = new ColorTransform();
		private var fill:GraphicsBitmapFill = new GraphicsBitmapFill();
        private var path:GraphicsTrianglePath = new GraphicsTrianglePath(new Vector.<Number>(3), null, new Vector.<Number>(3));
        private var end:GraphicsEndFill = new GraphicsEndFill();
        private var drawing:Vector.<IGraphicsData> = Vector.<IGraphicsData>([fill, path, end]);
		private var _renderers:Dictionary = new Dictionary(true);
		private var _renderer:Renderer;
        private var _session:AbstractSession;
        private var _sessionupdated:SessionEvent;
        private var _sessionDirty:Boolean;
        private var _i:uint;
        private var _index0:uint;
        private var _index1:uint;
        private var _index2:uint;
        private var v0x:Number;
        private var v0y:Number;
        private var v1x:Number;
        private var v1y:Number;
        private var v2x:Number;
        private var v2y:Number;
        private var a2:Number;
        private var b2:Number;
        private var c2:Number;
        private var d2:Number;
		private var m:Matrix = new Matrix();
		private var _r:Renderer;
        private var _spriteVO:SpriteVO;
        private var _mapping:Matrix;
        private var _scale:Number;
        private var _scaleWidth:Number;
        private var _scaleHeight:Number;
        private var _vx:Number;
        private var _vy:Number;
        private var _vertices:Vector.<Number> = new Vector.<Number>();
		
		private function getShapeStore(view:View3D):Array
		{
			if (!_shapeStores[view])
        		return _shapeStores[view] = new Array();
        	
			return _shapeStores[view];
		}
		
		private function getShapeActive(view:View3D):Array
		{
			if (!_shapeActives[view])
        		return _shapeActives[view] = new Array();
        	
			return _shapeActives[view];
		}
		
		private function getSpriteStore(view:View3D):Array
		{
			if (!_spriteStores[view])
        		return _spriteStores[view] = new Array();
        	
			return _spriteStores[view];
		}
		
		private function getSpriteActive(view:View3D):Array
		{
			if (!_spriteActives[view])
        		return _spriteActives[view] = new Array();
        	
			return _spriteActives[view];
		}
		
		public function getSpriteLayer(view:View3D):Dictionary
		{
			if (!_spriteLayers[view])
        		return _spriteLayers[view] = new Dictionary(true);
        	
			return _spriteLayers[view];
		}
		
		public function getShapeLayer(view:View3D):Dictionary
		{
			if (!_shapeLayers[view])
        		return _shapeLayers[view] = new Dictionary(true);
        	
			return _shapeLayers[view];
		}
		
		public function getLightShapeLayer(view:View3D):Dictionary
		{
			if (!_lightShapeLayers[view])
        		return _lightShapeLayers[view] = new Dictionary(true);
        	
			return _lightShapeLayers[view];
		}
		
        protected function onSessionUpdate(event:SessionEvent):void
        {
        	notifySessionUpdate();
        }
        
		/** @private */
        protected var i:int;
        
        public var layer:DisplayObject;
        
        public var parent:AbstractSession;
        
        public var updated:Boolean;
        
        public var primitives:Array;
        
        public var screenZ:Number;
        /**
        * Placeholder for filters property of containers
        */
        public var filters:Array;
        
        /**
        * Placeholder for alpha property of containers
        */
        public var alpha:Number = 1;
        
        /**
        * Placeholder for blendMode property of containers
        */
        public var blendMode:String;
        
        /**
        * Array of child sessions.
        */
       	public var sessions:Array = new Array();
        
        /**
        * Reference to the current graphics object being used for drawing.
        */
        public var graphics:Graphics;
        
        public var priconsumers:Dictionary = new Dictionary(true);
        
        public var consumer:Renderer;
        
        public var object3D:Object3D;
		
		public function get renderer():Renderer
		{
			return _renderer;
		}
		
		public function set renderer(val:Renderer):void
		{
			if (_renderer == val)
				return;
			
			_renderer = val;
			
			clearRenderers();
			
			for each (var _session:AbstractSession in sessions)
				_session.clearRenderers();
		}
        
		/**
		 * Adds a session as a child of the session object.
		 * 
		 * @param	session		The session object to be added as a child.
		 */
        public function addChildSession(session:AbstractSession):void
        {
        	if (sessions.indexOf(session) != -1)
        		return;
        	
        	sessions.push(session);
        	session.addOnSessionUpdate(onSessionUpdate);
        	session.parent = this;
        }
        
		/**
		 * Removes a child session of the session object.
		 * 
		 * @param	session		The session object to be removed.
		 */
        public function removeChildSession(session:AbstractSession):void
        {
        	session.removeOnSessionUpdate(onSessionUpdate);
        	
        	var index:int = sessions.indexOf(session);
            if (index == -1)
                return;
            
            sessions.splice(index, 1);	
        }
        
        public function clearChildSessions():void
        {
        	for each (_session in sessions)
        		_session.removeOnSessionUpdate(onSessionUpdate);
        		
        	sessions = new Array();
        }
        
       	/**
       	 * Creates a new render layer for rendering composite materials.
       	 * 
       	 * @see away3d.materials.CompositeMaterial#renderTriangle()
       	 */
        protected function createLayer():void
        {
			throw new Error("Not implemented");
        }
        
		/**
		 * Returns a display object representing the container for the specified view.
		 * 
		 * @param	view	The view object being rendered.
		 * @return			The display object container.
		 */
		public function getContainer(view:View3D):DisplayObject
		{
			throw new Error("Not implemented");
		}
			
		public function getRenderer(view:View3D):Renderer
		{
			if (_renderers[view])
				return _renderers[view];
			
			if (_renderer)
				_r = _renderers[view] = _renderer.clone();
			else if (parent)
				_r = _renderers[view] = parent.getRenderer(view).clone();
			else
				_r = _renderers[view] = view.session.renderer.clone();
				
			_r._view = view;
			_r._session = this;
			
			return _r;
		}
		
        public function getTotalFaces(view:View3D):int
        {
        	var output:int = getRenderer(view).list().length;
			
			for each (_session in sessions)
				output += _session.getTotalFaces(view);
				
			return output;
        }
        
		/**
		 * Clears the render session.
		 */
        public function clear(view:View3D):void
        {
        	_sessionDirty = false;
        	
        	updated = view.updated || view.forceUpdate || view._updatedSessions[this];
			
        	for each(_session in sessions)
       			_session.clear(view);
        	
			if (updated) {
	        	
	        	_spriteLayer = getSpriteLayer(view);
	        	
	        	for each (_array in _spriteLayer)
	        		_array.length = 0;
	        	
	        	_shapeLayer = getShapeLayer(view);
	        	
	        	for each (_array in _shapeLayer)
	        		_array.length = 0;
	        	
	        	_lightShapeLayer = getLightShapeLayer(view);
	        	
	        	for each (_dictionary in _lightShapeLayer)
	        		for each (_array in _dictionary)
	        			_array.length = 0;
	        	
	        	_level = -1;
	        	
	        	_shapeStore = getShapeStore(view);
	        	_shapeActive = getShapeActive(view);
	        	
	        	//clear child shapes
	            i = _shapeActive.length;
	            while (i--) {
	            	_shape = _shapeActive.pop();
	            	_shape.graphics.clear();
	            	_shape.filters = [];
	            	_shape.blendMode = BlendMode.NORMAL;
	            	_shape.transform.colorTransform = _defaultColorTransform;
	            	_shapeStore.push(_shape);
	            }
	            
	            _spriteStore = getSpriteStore(view);
	        	_spriteActive = getSpriteActive(view);
	        	
	        	//clear child sprites
	        	i = _spriteActive.length;
	            while (i--) {
	            	_sprite = _spriteActive.pop();
	            	_sprite.graphics.clear();
	            	_sprite.filters = [];
	            	while (_sprite.numChildren)
        				_sprite.removeChildAt(0);
	            	_spriteStore.push(_sprite);
	            }
	            
	            //clear primitives consumer
	            getRenderer(view).clear();
			}
        }
        
        public function render(view:View3D):void
        {
	        //index -= priconsumer.length;
        	for each(_session in sessions)
       			_session.render(view);
       		
        	//fill.bitmapData = null;
        	
        	if (updated)
	            getRenderer(view).render();
	        
	        delete view._updatedSessions[this];
	        //if (fill.bitmapData)
	        //	drawBitmapTriangles();
        }
        
        public function clearRenderers():void
        {
        	_renderers = new Dictionary(true);
        }
        
        /**
        * Adds a display object to the render session display list.
        * 
        * @param	child	The display object to add.
        */
        public function addDisplayObject(child:DisplayObject):void
        {
        	throw new Error("Not implemented");
        }
        
        public function getSprite(material:LayerMaterial, level:int, parent:Sprite = null):Sprite
        {
        	if (!(_array = _spriteLayer[material])) 
        		_array = _spriteLayer[material] = new Array();
        	
        	if (!parent && material != _material) {
        		_level = -1;
        		_material = material;
        	}
        	
        	if (_level >= level && _array.length) {
        		_sprite = _array[0];
        	} else {
	        	_level = level;
        		_array.unshift(_sprite = createSprite(parent));
        	}
            return _sprite;
        }
        
        public function getShape(material:LayerMaterial, level:int, parent:Sprite):Shape
        {
        	if (!(_array = _shapeLayer[material]))
        		_array = _shapeLayer[material] = new Array();
        	
        	if (_level >= level && _array.length) {
        		_shape = _array[0];
        	} else {
	        	_level = level;
        		_array.unshift(_shape = createShape(parent));
        	}
        	
            return _shape;
        }
        
        
        public function getLightShape(material:LayerMaterial, level:int, parent:Sprite, light:AbstractLight):Shape
        {
        	if (!(_dictionary = _lightShapeLayer[material]))
        		_dictionary = _lightShapeLayer[material] = new Dictionary(true);
        	
        	if (!(_array = _dictionary[light]))
        		_array = _dictionary[light] = new Array();
        	
        	if (_level >= level && _array.length) {
        		_shape = _array[0];
        	} else {
        		_level = level;
        		_array.unshift(_shape = createShape(parent));
        	}
        	
            return _shape;
        }
        
        protected function createSprite(parent:Sprite = null):Sprite
        {
        	throw new Error("Not implemented");
        }
        
		/**
		 * @inheritDoc
		 */
        protected function createShape(parent:Sprite):Shape
        {
        	if (_shapeStore.length) {
            	_shapeActive.push(_shape = _shapeStore.pop());
            } else {
            	_shapeActive.push(_shape = new Shape());
            }
            
            parent.addChild(_shape);
            
            _layerDirty = true;
            
            return _shape;
        }
        
        /**
         * Draws a segment element into the graphics object.
         */
        public function renderLine(v0x:Number, v0y:Number, v1x:Number, v1y:Number, width:Number, color:uint, alpha:Number):void
        {
        	if (_layerDirty)
        		createLayer();
        	
            graphics.lineStyle(width, color, alpha);
            graphics.moveTo(v0x, v0y);
            graphics.lineTo(v1x, v1y);
        }
        
        /**
         * Draws a triangle element with a bitmap texture into the graphics object, with no uv transforms.
         */
        public function renderTriangleBitmapMask(bitmap:BitmapData, offX:Number, offY:Number, sc:Number, screenVertices:Vector.<Number>, screenIndices:Vector.<int>, startIndex:uint, endIndex:uint, smooth:Boolean, repeat:Boolean, layerGraphics:Graphics = null):void
        {
        	endIndex;
        	
        	if (_layerDirty)
        		createLayer();
        	
        	_index0 = screenIndices[startIndex]*2;
        	_index1 = screenIndices[uint(startIndex+1)]*2;
        	_index2 = screenIndices[uint(startIndex+2)]*2;
        	
        	a2 = (v1x = screenVertices[_index1]) - (v0x = screenVertices[_index0]);
        	b2 = (v1y = screenVertices[uint(_index1+1)]) - (v0y = screenVertices[uint(_index0+1)]);
        	c2 = (v2x = screenVertices[_index2]) - v0x;
        	d2 = (v2y = screenVertices[uint(_index2+1)]) - v0y;
        	
			m.identity();
			m.scale(sc, sc);
			m.translate(offX, offY);
			
			if (layerGraphics) {
				layerGraphics.lineStyle();
	            layerGraphics.moveTo(v0x, v0y);
	            layerGraphics.beginBitmapFill(bitmap, m, repeat, smooth && (v0x*(d2 - b2) - v1x*d2 + v2x*b2 > 400));
	            layerGraphics.lineTo(v1x, v1y);
	            layerGraphics.lineTo(v2x, v2y);
	            layerGraphics.endFill();
	  		} else {
	  			graphics.lineStyle();
	            graphics.moveTo(v0x, v0y);
	            graphics.beginBitmapFill(bitmap, m, repeat, smooth && (v0x*(d2 - b2) - v1x*d2 + v2x*b2 > 400));
	            graphics.lineTo(v1x, v1y);
	            graphics.lineTo(v2x, v2y);
	            graphics.endFill();
	  		}
        }
        
        /**
         * Draws a triangle element with a bitmap texture into the graphics object (Flash 10)
         */
        public function renderTriangleBitmap(bitmap:BitmapData, uvtData:Vector.<Number>, screenVertices:Vector.<Number>, screenIndices:Vector.<int>, startIndex:uint, endIndex:uint, smooth:Boolean, repeat:Boolean, layerGraphics:Graphics = null):void
        {
        	endIndex;
        	
        	if (!layerGraphics && _layerDirty)
        		createLayer();
        		
        	_index0 = screenIndices[startIndex]*2;
        	_index1 = screenIndices[uint(startIndex+1)]*2;
        	_index2 = screenIndices[uint(startIndex+2)]*2;
        	
        	fill.bitmapData = bitmap;
			fill.repeat = repeat;
			fill.smooth = smooth;
			//remove vector creation
			
			_vertices.length = 0;
			_vertices[uint(0)] = screenVertices[_index0];
			_vertices[uint(1)] = screenVertices[uint(_index0 + 1)];
			_vertices[uint(2)] = screenVertices[_index1];
			_vertices[uint(3)] = screenVertices[uint(_index1 + 1)];
			_vertices[uint(4)] = screenVertices[_index2];
			_vertices[uint(5)] = screenVertices[uint(_index2 + 1)];
			
			path.vertices = _vertices;    	
        	path.uvtData = uvtData;
			
			if (layerGraphics) {
				layerGraphics.lineStyle();
				layerGraphics.drawGraphicsData(drawing);
	  		} else {
	  			graphics.lineStyle();
	  			graphics.drawGraphicsData(drawing);
	  		}
        }
        
		/**
         * Draws a rect dot at given coordinates
         */
        public function renderDot(color:int, alpha:Number, x:Number, y:Number, layerGraphics:Graphics = null, w:int = 1):void
        {
        	if (!layerGraphics && _layerDirty)
        		createLayer();
        	
        	var applicableGraphics:Graphics = layerGraphics ? layerGraphics : graphics;
			
			applicableGraphics.lineStyle();
			applicableGraphics.beginFill(color, alpha);
			if(w == 1){
				applicableGraphics.drawRect(x, y, w, w);
			} else{
				var half:Number = w*.5;
				applicableGraphics.drawRect(x-half, y-half, w, w);
			}
			applicableGraphics.endFill();
        }
		
        /**
         * Draws a triangle element with a fill color into the graphics object.
         */
        public function renderTriangleColor(color:int, alpha:Number, screenVertices:Vector.<Number>, commands:Vector.<String>, screenIndices:Vector.<int>, startIndex:Number, endIndex:Number, layerGraphics:Graphics = null):void
        {
        	if (!layerGraphics && _layerDirty)
        		createLayer();
        	
        	var applicableGraphics:Graphics = layerGraphics ? layerGraphics : graphics;
        	
        	if(endIndex - startIndex > 3) {
        		
        		applicableGraphics.lineStyle();
        		
        		_i = startIndex;
	            while(_i < endIndex) {
	            	_index0 = screenIndices[uint(_i++)]*2;
					switch (commands[uint(_i - startIndex - 1)]) {
						case "M":
							applicableGraphics.moveTo(screenVertices[_index0], screenVertices[uint(_index0+1)]);
							if (_i - 1 == startIndex)
								applicableGraphics.beginFill(color, alpha);
							break;
						case "L":
							applicableGraphics.lineTo(screenVertices[_index0], screenVertices[uint(_index0+1)]);
							break;
						case "C":
							_index1 = screenIndices[uint(_i++)]*2;
							applicableGraphics.curveTo(screenVertices[_index0], screenVertices[uint(_index0+1)], screenVertices[_index1], screenVertices[uint(_index1+1)]);
							break;
					}
	            }
	            applicableGraphics.endFill();
	        } else {
	        	_index0 = screenIndices[startIndex]*2;
	        	_index1 = screenIndices[startIndex+1]*2;
	        	_index2 = screenIndices[startIndex+2]*2;
	        	applicableGraphics.lineStyle();
	            applicableGraphics.moveTo(screenVertices[_index0], screenVertices[_index0+1]); // Always move before begin will to prevent bugs
	            applicableGraphics.beginFill(color, alpha);
	            applicableGraphics.lineTo(screenVertices[_index1], screenVertices[_index1+1]);
	            applicableGraphics.lineTo(screenVertices[_index2], screenVertices[_index2+1]);
	            applicableGraphics.endFill();
	        }
        }
        
        /**
         * Draws a wire triangle element into the graphics object.
         */
        public function renderTriangleLine(thickness:Number, wireColor:int, wireAlpha:Number, screenVertices:Vector.<Number>, commands:Vector.<String>, screenIndices:Vector.<int>, startIndex:Number, endIndex:Number):void
        {
        	if (_layerDirty)
        		createLayer();
        	
            graphics.lineStyle(thickness, wireColor, wireAlpha);
			i = startIndex;
			if(endIndex - startIndex > 3) {
	            while(i < endIndex) {
	            	_index0 = screenIndices[uint(i++)]*2;
					switch (commands[uint(i - startIndex - 1)]) {
						case "M":
							graphics.moveTo(screenVertices[_index0], screenVertices[uint(_index0+1)]);
							break;
						case "L":
							graphics.lineTo(screenVertices[_index0], screenVertices[uint(_index0+1)]);
							break;
						case "C":
							_index1 = screenIndices[uint(i++)]*2;
							graphics.curveTo(screenVertices[_index0], screenVertices[uint(_index0+1)], screenVertices[_index1], screenVertices[uint(_index1+1)]);
							break;
					}
	            }
	        } else if (endIndex - startIndex > 2) {
	        	_index0 = screenIndices[startIndex]*2;
	        	_index1 = screenIndices[uint(startIndex+1)]*2;
	        	_index2 = screenIndices[uint(startIndex+2)]*2; 
	        	
	        	graphics.moveTo(v0x = screenVertices[_index0], v0y = screenVertices[uint(_index0+1)]);
	            graphics.lineTo(screenVertices[_index1], screenVertices[uint(_index1+1)]);
		        graphics.lineTo(screenVertices[_index2], screenVertices[uint(_index2+1)]);
		        graphics.lineTo(v0x, v0y);
	        } else {
	        	_index0 = screenIndices[startIndex]*2;
	        	_index1 = screenIndices[uint(startIndex+1)]*2;
	        	
				graphics.moveTo(v0x = screenVertices[_index0], v0y = screenVertices[uint(_index0+1)]);
	            graphics.lineTo(screenVertices[_index1], screenVertices[uint(_index1+1)]);
				//extra lineTo to remove artifacts with combined segment and triangle rendering of line materials in FP10
				graphics.lineTo(v0x, v0y);
	        }
        }
        
        /**
         * Draws a wire triangle element with a fill color into the graphics object.
         */
        public function renderTriangleLineFill(thickness:Number, color:int, alpha:Number, wireColor:int, wireAlpha:Number, screenVertices:Vector.<Number>, commands:Vector.<String>, screenIndices:Vector.<int>, startIndex:int, endIndex:int):void
        {
        	if(_layerDirty)
        		createLayer();
        	
            if(wireAlpha > 0)
                graphics.lineStyle(thickness, wireColor, wireAlpha);
            else
                graphics.lineStyle();
        	
        	if(endIndex - startIndex > 3) {
        		
				_i = startIndex;
	            while(_i < endIndex) {
	            	_index0 = screenIndices[uint(_i++)]*2;
					switch (commands[uint(_i - startIndex - 1)]) {
						case "M":
							graphics.moveTo(screenVertices[_index0], screenVertices[uint(_index0+1)]);
							if (_i - 1 == startIndex && alpha > 0)
								graphics.beginFill(color, alpha);
							break;
						case "L":
							graphics.lineTo(screenVertices[_index0], screenVertices[uint(_index0+1)]);
							break;
						case "C":
							_index1 = screenIndices[uint(_i++)]*2;
							graphics.curveTo(screenVertices[_index0], screenVertices[uint(_index0+1)], screenVertices[_index1], screenVertices[uint(_index1+1)]);
							break;
					}
	            }
	        } else {
	        	_index0 = screenIndices[startIndex]*2;
	        	_index1 = screenIndices[uint(startIndex+1)]*2;
	        	_index2 = screenIndices[uint(startIndex+2)]*2;
	        	
	        	graphics.moveTo(v0x = screenVertices[_index0], v0y = screenVertices[uint(_index0+1)]);
	        	
	        	if(alpha > 0)
					graphics.beginFill(color, alpha);
				
	            graphics.lineTo(screenVertices[_index1], screenVertices[uint(_index1+1)]);
	        	graphics.lineTo(screenVertices[_index2], screenVertices[uint(_index2+1)]);
	    
	            if (wireAlpha > 0)
	                graphics.lineTo(v0x, v0y);
	        }
	        
	        if(alpha > 0)
	        	graphics.endFill();
        }
                
        /**
         * Draws a billboard element with a fill color into the graphics object.
         */
        public function renderSpriteLine(thickness:Number, wireColor:int, wireAlpha:Number, priIndex:uint, viewSourceObject:ViewSourceObject, renderer:Renderer):void
        {
        	if (_layerDirty)
        		createLayer();
        	
        	graphics.lineStyle(thickness, wireColor, wireAlpha);
        	
        	_spriteVO = renderer.primitiveElements[priIndex] as SpriteVO;
        	_scale = renderer.primitiveProperties[uint(priIndex*9 + 8)];
        	_scaleWidth = _scale*_spriteVO.width;
        	_scaleHeight = _scale*_spriteVO.height;
			_index0 = viewSourceObject.screenIndices[uint(renderer.primitiveProperties[uint(priIndex*9)])]*2;
			_vx = viewSourceObject.screenVertices[_index0];
			_vy = viewSourceObject.screenVertices[uint(_index0 + 1)];
			
			if (_spriteVO.rotation != 0) {
	            graphics.moveTo(_vx + _spriteVO.topleftx*_scaleWidth, _vy + _spriteVO.toplefty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.toprightx*_scaleWidth, _vy + _spriteVO.toprighty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.bottomrightx*_scaleWidth, _vy + _spriteVO.bottomrighty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.bottomleftx*_scaleWidth, _vy + _spriteVO.bottomlefty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.topleftx*_scaleWidth, _vy + _spriteVO.toplefty*_scaleHeight);
            } else {
	            graphics.drawRect(_vx + _spriteVO.minX*_scaleWidth, _vy + _spriteVO.minY*_scaleHeight, (_spriteVO.maxX - _spriteVO.minX)*_scaleWidth, (_spriteVO.maxY - _spriteVO.minY)*_scaleHeight);
            }
        }
        
        /**
         * Draws a billboard element with a fill color into the graphics object.
         */
        public function renderSpriteColor(color:int, alpha:Number, priIndex:uint, viewSourceObject:ViewSourceObject, renderer:Renderer):void
        {
        	if (_layerDirty)
        		createLayer();
        	
        	graphics.lineStyle();
        	
        	_spriteVO = renderer.primitiveElements[priIndex] as SpriteVO;
        	_scale = renderer.primitiveProperties[uint(priIndex*9 + 8)];
        	_scaleWidth = _scale*_spriteVO.width;
        	_scaleHeight = _scale*_spriteVO.height;
        	_index0 = viewSourceObject.screenIndices[uint(renderer.primitiveProperties[uint(priIndex*9)])]*2;
			_vx = viewSourceObject.screenVertices[_index0];
			_vy = viewSourceObject.screenVertices[uint(_index0 + 1)];
			
			if (_spriteVO.rotation != 0) {
	            graphics.beginFill(color, alpha);
	            graphics.moveTo(_vx + _spriteVO.topleftx*_scaleWidth, _vy + _spriteVO.toplefty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.toprightx*_scaleWidth, _vy + _spriteVO.toprighty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.bottomrightx*_scaleWidth, _vy + _spriteVO.bottomrighty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.bottomleftx*_scaleWidth, _vy + _spriteVO.bottomlefty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.topleftx*_scaleWidth, _vy + _spriteVO.toplefty*_scaleHeight);
	            graphics.endFill();
            } else {
	            graphics.beginFill(color, alpha);
	            graphics.drawRect(_vx + _spriteVO.minX*_scaleWidth, _vy + _spriteVO.minY*_scaleHeight, (_spriteVO.maxX - _spriteVO.minX)*_scaleWidth, (_spriteVO.maxY - _spriteVO.minY)*_scaleHeight);
            	graphics.endFill();
            }
        }
        
        /**
         * Draws a billboard element with a fill bitmap into the graphics object.
         */
        public function renderSpriteBitmap(bitmap:BitmapData, smooth:Boolean, priIndex:uint, viewSourceObject:ViewSourceObject, renderer:Renderer):void
        {
        	if (_layerDirty)
        		createLayer();
        	
        	graphics.lineStyle();
        	
        	_spriteVO = renderer.primitiveElements[priIndex] as SpriteVO;
        	_scale = renderer.primitiveProperties[uint(priIndex*9 + 8)];
        	_scaleWidth = _scale*bitmap.width;
        	_scaleHeight = _scale*bitmap.height;
        	_index0 = viewSourceObject.screenIndices[uint(renderer.primitiveProperties[uint(priIndex*9)])]*2;
			_vx = viewSourceObject.screenVertices[_index0];
			_vy = viewSourceObject.screenVertices[uint(_index0 + 1)];
			_mapping = _spriteVO.mapping.clone();
			_mapping.scale(_scale, _scale);
			_mapping.translate(_vx - _scale*bitmap.width/2, _vy - _scale*bitmap.height/2);
			
			if (_spriteVO.rotation != 0) {
	            graphics.beginBitmapFill(bitmap, _mapping, false, smooth);
	            graphics.moveTo(_vx + _spriteVO.topleftx*_scaleWidth, _vy + _spriteVO.toplefty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.toprightx*_scaleWidth, _vy + _spriteVO.toprighty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.bottomrightx*_scaleWidth, _vy + _spriteVO.bottomrighty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.bottomleftx*_scaleWidth, _vy + _spriteVO.bottomlefty*_scaleHeight);
	            graphics.lineTo(_vx + _spriteVO.topleftx*_scaleWidth, _vy + _spriteVO.toplefty*_scaleHeight);
	            graphics.endFill();
            } else {
	            graphics.beginBitmapFill(bitmap, _mapping, false, smooth);
	            graphics.drawRect(_vx + _spriteVO.minX*_scaleWidth, _vy + _spriteVO.minY*_scaleHeight, (_spriteVO.maxX - _spriteVO.minX)*_scaleWidth, (_spriteVO.maxY - _spriteVO.minY)*_scaleHeight);
            	graphics.endFill();
            }
        }
        
        /**
         * Draws a fog element into the graphics object.
         */
        public function renderFogColor(color:int, alpha:Number, minX:Number, maxX:Number, minY:Number, maxY:Number):void
        {
        	if (_layerDirty)
        		createLayer();
        	
        	graphics.lineStyle();
        	
            graphics.beginFill(color, alpha);
            graphics.drawRect(minX, minY, maxX - minX, maxY - minY);
            graphics.endFill();
        }
		
		/**
		 * Duplicates the render session's properties to another render session.
		 * 
		 * @return						The new render session instance with duplicated properties applied
		 */
        public function clone():AbstractSession
        {
        	throw new Error("Not implemented");
        }
		
		/**
		 * Default method for adding a sessionUpdated event listener
		 * 
		 * @param	listener		The listener function
		 */
        public function addOnSessionUpdate(listener:Function):void
        {
            addEventListener(SessionEvent.SESSION_UPDATED, listener, false, 0, false);
        }
		
		/**
		 * Default method for removing a sessionUpdated event listener
		 * 
		 * @param	listener		The listener function
		 */
        public function removeOnSessionUpdate(listener:Function):void
        {
            removeEventListener(SessionEvent.SESSION_UPDATED, listener, false);
        }
	}
}