package flare.vis.data.render
{
    import flare.util.Geometry;
    import flare.util.Shapes;
    import flare.vis.data.DataSprite;
    import flare.vis.data.EdgeSprite;
    import flare.vis.data.NodeSprite;
    
    import flash.display.Graphics;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    /**
     * Renderer that draws edges as lines. The EdgeRenderer supports straight
     * lines, poly lines, and curves as Bezier or cardinal splines. The type
     * of edge drawn is determined from an EdgeSprite's <code>shape</code>
     * property and control points found in the <code>points</code> property.
     * The line rendering properties are set by the <code>setLineStyle</code>
     * method, which can be overridden by subclasses. By default, the line
     * rendering settings are determined using default property values set
     * on this class (for example, the <code>scaleMode<code> and 
     * <code>caps</code> properties).
     */
    public class EdgeRenderer implements IRenderer
    {        
        private static const ROOT3:Number = Math.sqrt(3);
        
        private static var _instance:EdgeRenderer = new EdgeRenderer();
        /** Static EdgeRenderer instance. */
        public static function get instance():EdgeRenderer { return _instance; }
        
        /** Pixel hinting flag for line rendering. */
        public var pixelHinting:Boolean = false;
        /** Scale mode for line rendering (normal by default). */
        public var scaleMode:String = "normal";
        /** The joint style for line rendering. */
        public var joints:String = null;
        /** The caps style for line rendering. */
        public var caps:String = null;
        /** The miter limit for line rendering. */
        public var miterLimit:int = 3;
        
        // temporary variables
        private var _p:Point = new Point(), _q:Point = new Point();
        private var _pts:Array = new Array(20);
        
        /** @inheritDoc */
        public function render(d:DataSprite):void
        {
            var e:EdgeSprite = d as EdgeSprite;
            if (e == null) { return; } // TODO: throw exception?
            var s:NodeSprite = e.source;
            var t:NodeSprite = e.target;
            var g:Graphics = e.graphics;
            
            var ctrls:Array = e.points as Array;
            var x1:Number = e.x1, y1:Number = e.y1;
            var x2:Number = e.x2, y2:Number = e.y2;
            var xL:Number = ctrls==null ? x1 : ctrls[ctrls.length-2];
            var yL:Number = ctrls==null ? y1 : ctrls[ctrls.length-1];
            var dx:Number, dy:Number, dd:Number;

            // modify end points as needed to accomodate arrow
            if (e.arrowType != ArrowType.NONE)
            {
                // determine arrow head size
                var ah:Number = e.arrowHeight, aw:Number = e.arrowWidth/2;
                if (ah < 0 && aw < 0)
                    aw = 1.5 * e.lineWidth;
                if (ah < 0) {
                    ah = ROOT3 * aw;
                } else if (aw < 0) {
                    aw = ah / ROOT3;
                }
                
                // get arrow tip point as intersection of edge with bounding box
                if (t==null) {
                    _p.x = x2; _p.y = y2;
                } else {
                    var r:Rectangle = t.getBounds(t.parent);
                    if (Geometry.intersectLineRect(xL,yL,x2,y2, r, _p,_q) <= 0)
                    {
                        _p.x = x2; _p.y = y2;
                    }
                }
                
                // get unit vector along arrow line
                dx = _p.x - xL; dy = _p.y - yL;
                dd = Math.sqrt(dx*dx + dy*dy);
                dx /=