/**
 * A sprite that represents a line.
 *
 *     @example
 *     Ext.create({
 *        xtype: 'draw',
 *        renderTo: document.body,
 *        width: 600,
 *        height: 400,
 *        sprites: [{
 *            type: 'line',
 *            fromX: 20,
 *            fromY: 20,
 *            toX: 120,
 *            toY: 120,
 *            strokeStyle: '#1F6D91',
 *            lineWidth: 3
 *        }]
 *     });
 */
Ext.define('Ext.draw.sprite.Line', {
    extend: 'Ext.draw.sprite.Sprite',
    alias: 'sprite.line',
    type: 'line',
 
    inheritableStatics: {
        def: {
            processors: {
                fromX: 'number',
                fromY: 'number',
                toX: 'number',
                toY: 'number',
                crisp: 'bool'
            },
 
            defaults: {
                fromX: 0,
                fromY: 0,
                toX: 1,
                toY: 1,
                crisp: false,
                strokeStyle: 'black'
            },
 
            aliases: {
                x1: 'fromX',
                y1: 'fromY',
                x2: 'toX',
                y2: 'toY'
            },
 
            triggers: {
                crisp: 'bbox'
            }
        }
    },
 
    updateLineBBox: function (bbox, isTransform, x1, y1, x2, y2) {
        var attr = this.attr,
            matrix = attr.matrix,
            halfLineWidth = attr.lineWidth / 2,
            fromX, fromY, toX, toY,
            p;
 
        if (attr.crisp) {
            x1 = this.align(x1);
            x2 = this.align(x2);
            y1 = this.align(y1);
            y2 = this.align(y2);
        }
 
        if (isTransform) {
            p = matrix.transformPoint([x1, y1]);
            x1 = p[0];
            y1 = p[1];
 
            p = matrix.transformPoint([x2, y2]);
            x2 = p[0];
            y2 = p[1];
        }
 
        fromX = Math.min(x1, x2);
        toX   = Math.max(x1, x2);
 
        fromY = Math.min(y1, y2);
        toY   = Math.max(y1, y2);
 
        var angle = Math.atan2(toX - fromX, toY - fromY),
            sin = Math.sin(angle),
            cos = Math.cos(angle),
            dx = halfLineWidth * cos,
            dy = halfLineWidth * sin;
 
        // Offset start and end points of the line by half its thickness,
        // while accounting for line's angle.
        fromX -= dx;
        fromY -= dy;
        toX += dx;
        toY += dy;
 
        bbox.x = fromX;
        bbox.y = fromY;
        bbox.width = toX - fromX;
        bbox.height = toY - fromY;
    },
 
    updatePlainBBox: function (plain) {
        var attr = this.attr;
 
        this.updateLineBBox(plain, false, attr.fromX, attr.fromY, attr.toX, attr.toY);
    },
 
    updateTransformedBBox: function (transform, plain) {
        var attr = this.attr;
 
        this.updateLineBBox(transform, true, attr.fromX, attr.fromY, attr.toX, attr.toY);
    },
 
    align: function (x) {
        return Math.round(x) - 0.5;
    },
 
    render: function (surface, ctx) {
        var me = this,
            attr = me.attr,
            matrix = attr.matrix;
 
        matrix.toContext(ctx);
 
        ctx.beginPath();
 
        if (attr.crisp) {
            ctx.moveTo(me.align(attr.fromX), me.align(attr.fromY));
            ctx.lineTo(me.align(attr.toX),   me.align(attr.toY));
        } else {
            ctx.moveTo(attr.fromX, attr.fromY);
            ctx.lineTo(attr.toX, attr.toY);
        }
 
        ctx.stroke();
 
        //<debug>
        var debug = attr.debug || this.statics().debug || Ext.draw.sprite.Sprite.debug;
        if (debug) {
            // This assumes no part of the sprite is rendered after this call.
            // If it is, we need to re-apply transformations.
            // But the bounding box should always be rendered as is, untransformed.
            this.attr.inverseMatrix.toContext(ctx);
            debug.bbox && this.renderBBox(surface, ctx);
        }
        //</debug>
    }
});