Ext.define('Ext.sparkline.CanvasCanvas', {
    extend: 'Ext.sparkline.CanvasBase',
 
    constructor: function(ownerSparkLine) {
        this.owner = ownerSparkLine;
    },
 
    setWidth: function(width) {
        this.callParent(arguments);
        this.owner.element.dom.width = width;
    },
 
    setHeight: function(height) {
        this.callParent(arguments);
        this.owner.element.dom.height = height;
    },
 
    onOwnerUpdate: function() {
        var me = this;
 
        me.el = me.owner.element;
        me.interact = !me.owner.initialConfig.disableInteraction;
        me.shapes = {};
        me.shapeseq = [];
        me.currentTargetShapeId = me.lastShapeId = null;
    },
 
    _getContext: function (lineColor, fillColor, lineWidth) {
        var context = this.el.dom.getContext('2d');
        if (lineColor != null) {
            context.strokeStyle = lineColor;
        }
        context.lineWidth = lineWidth || 1;
        if (fillColor != null) {
            context.fillStyle = fillColor;
        }
        return context;
    },
 
    reset: function () {
        var context = this._getContext();
        context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
        this.shapes = {};
        this.shapeseq = [];
        this.currentTargetShapeId = this.lastShapeId = null;
    },
 
    _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) {
        var context = this._getContext(lineColor, fillColor, lineWidth),
            i, plen;
 
        context.beginPath();
        context.moveTo(path[0][0] + 0.5, path[0][1] + 0.5);
        for (= 1, plen = path.length; i < plen; i++) {
            context.lineTo(path[i][0] + 0.5, path[i][1] + 0.5); // the 0.5 offset gives us crisp pixel-width lines 
        }
        if (lineColor != null) {
            context.stroke();
        }
        if (fillColor != null) {
            context.fill();
        }
        if (this.targetX != null && this.targetY != null && context.isPointInPath(this.targetX, this.targetY)) {
            this.currentTargetShapeId = shapeid;
        }
    },
 
    _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) {
        var context = this._getContext(lineColor, fillColor, lineWidth);
 
        context.beginPath();
        context.arc(x, y, radius, 0, 2 * Math.PI, false);
        if (this.targetX != null && this.targetY != null && context.isPointInPath(this.targetX, this.targetY)) {
            this.currentTargetShapeId = shapeid;
        }
        if (lineColor !== undefined) {
            context.stroke();
        }
        if (fillColor !== undefined) {
            context.fill();
        }
    },
 
    _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {
        var context = this._getContext(lineColor, fillColor);
 
        context.beginPath();
        context.moveTo(x, y);
        context.arc(x, y, radius, startAngle, endAngle, false);
        context.lineTo(x, y);
        context.closePath();
        if (lineColor != null) {
            context.stroke();
        }
        if (fillColor) {
            context.fill();
        }
        if (this.targetX !== undefined && this.targetY !== undefined &&
            context.isPointInPath(this.targetX, this.targetY)) {
            this.currentTargetShapeId = shapeid;
        }
    },
 
    _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) {
        return this._drawShape(shapeid, [[x, y], [+ width, y], [+ width, y + height], [x, y + height], [x, y]], lineColor, fillColor);
    },
 
    appendShape: function (shape) {
        this.shapes[shape.id] = shape;
        this.shapeseq.push(shape.id);
        this.lastShapeId = shape.id;
        return shape.id;
    },
 
    replaceWithShape: function (shapeid, shape) {
        var shapeseq = this.shapeseq,
            i;
        this.shapes[shape.id] = shape;
        for (= shapeseq.length; i--;) {
            if (shapeseq[i] == shapeid) {
                shapeseq[i] = shape.id;
            }
        }
        delete this.shapes[shapeid];
    },
 
    replaceWithShapes: function (shapeids, shapes) {
        var shapeseq = this.shapeseq,
            shapemap = {},
            sid, i, first;
 
        for (= shapeids.length; i--;) {
            shapemap[shapeids[i]] = true;
        }
        for (= shapeseq.length; i--;) {
            sid = shapeseq[i];
            if (shapemap[sid]) {
                shapeseq.splice(i, 1);
                delete this.shapes[sid];
                first = i;
            }
        }
        for (= shapes.length; i--;) {
            shapeseq.splice(first, 0, shapes[i].id);
            this.shapes[shapes[i].id] = shapes[i];
        }
 
    },
 
    insertAfterShape: function (shapeid, shape) {
        var shapeseq = this.shapeseq,
            i;
        for (= shapeseq.length; i--;) {
            if (shapeseq[i] === shapeid) {
                shapeseq.splice(+ 1, 0, shape.id);
                this.shapes[shape.id] = shape;
                return;
            }
        }
    },
 
    removeShapeId: function (shapeid) {
        var shapeseq = this.shapeseq,
            i;
        for (= shapeseq.length; i--;) {
            if (shapeseq[i] === shapeid) {
                shapeseq.splice(i, 1);
                break;
            }
        }
        delete this.shapes[shapeid];
    },
 
    getShapeAt: function (x, y) {
        this.targetX = x;
        this.targetY = y;
        this.render();
        return this.currentTargetShapeId;
    },
 
    render: function () {
        var shapeseq = this.shapeseq,
            shapes = this.shapes,
            shapeCount = shapeseq.length,
            context = this._getContext(),
            shapeid, shape, i;
 
        context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
        for (= 0; i < shapeCount; i++) {
            shapeid = shapeseq[i];
            shape = shapes[shapeid];
            this['_draw' + shape.type].apply(this, shape.args);
        }
        if (!this.interact) {
            // not interactive so no need to keep the shapes array 
            this.shapes = {};
            this.shapeseq = [];
        }
    }
});