/**

* Box plots
*/

$.fn.sparkline.box = box = createClass($.fn.sparkline._base, {

type: 'box',

init: function (el, values, options, width, height) {
    box._super.init.call(this, el, values, options, width, height);
    this.values = $.map(values, Number);
    this.width = options.get('width') === 'auto' ? '4.0em' : width;
    this.initTarget();
    if (!this.values.length) {
        this.disabled = 1;
    }
},

/**
 * Simulate a single region
 */
getRegion: function () {
    return 1;
},

getCurrentRegionFields: function () {
    var result = [
        { field: 'lq', value: this.quartiles[0] },
        { field: 'med', value: this.quartiles[1] },
        { field: 'uq', value: this.quartiles[2] }
    ];
    if (this.loutlier !== undefined) {
        result.push({ field: 'lo', value: this.loutlier});
    }
    if (this.routlier !== undefined) {
        result.push({ field: 'ro', value: this.routlier});
    }
    if (this.lwhisker !== undefined) {
        result.push({ field: 'lw', value: this.lwhisker});
    }
    if (this.rwhisker !== undefined) {
        result.push({ field: 'rw', value: this.rwhisker});
    }
    return result;
},

render: function () {
    var target = this.target,
        values = this.values,
        vlen = values.length,
        options = this.options,
        canvasWidth = this.canvasWidth,
        canvasHeight = this.canvasHeight,
        minValue = options.get('chartRangeMin') === undefined ? Math.min.apply(Math, values) : options.get('chartRangeMin'),
        maxValue = options.get('chartRangeMax') === undefined ? Math.max.apply(Math, values) : options.get('chartRangeMax'),
        canvasLeft = 0,
        lwhisker, loutlier, iqr, q1, q2, q3, rwhisker, routlier, i,
        size, unitSize;

    if (!box._super.render.call(this)) {
        return;
    }

    if (options.get('raw')) {
        if (options.get('showOutliers') && values.length > 5) {
            loutlier = values[0];
            lwhisker = values[1];
            q1 = values[2];
            q2 = values[3];
            q3 = values[4];
            rwhisker = values[5];
            routlier = values[6];
        } else {
            lwhisker = values[0];
            q1 = values[1];
            q2 = values[2];
            q3 = values[3];
            rwhisker = values[4];
        }
    } else {
        values.sort(function (a, b) { return a - b; });
        q1 = quartile(values, 1);
        q2 = quartile(values, 2);
        q3 = quartile(values, 3);
        iqr = q3 - q1;
        if (options.get('showOutliers')) {
            lwhisker = rwhisker = undefined;
            for (i = 0; i < vlen; i++) {
                if (lwhisker === undefined && values[i] > q1 - (iqr * options.get('outlierIQR'))) {
                    lwhisker = values[i];
                }
                if (values[i] < q3 + (iqr * options.get('outlierIQR'))) {
                    rwhisker = values[i];
                }
            }
            loutlier = values[0];
            routlier = values[vlen - 1];
        } else {
            lwhisker = values[0];
            rwhisker = values[vlen - 1];
        }
    }
    this.quartiles = [q1, q2, q3];
    this.lwhisker = lwhisker;
    this.rwhisker = rwhisker;
    this.loutlier = loutlier;
    this.routlier = routlier;

    unitSize = canvasWidth / (maxValue - minValue + 1);
    if (options.get('showOutliers')) {
        canvasLeft = Math.ceil(options.get('spotRadius'));
        canvasWidth -= 2 * Math.ceil(options.get('spotRadius'));
        unitSize = canvasWidth / (maxValue - minValue + 1);
        if (loutlier < lwhisker) {
            target.drawCircle((loutlier - minValue) * unitSize + canvasLeft,
                canvasHeight / 2,
                options.get('spotRadius'),
                options.get('outlierLineColor'),
                options.get('outlierFillColor')).append();
        }
        if (routlier > rwhisker) {
            target.drawCircle((routlier - minValue) * unitSize + canvasLeft,
                canvasHeight / 2,
                options.get('spotRadius'),
                options.get('outlierLineColor'),
                options.get('outlierFillColor')).append();
        }
    }

    // box
    target.drawRect(
        Math.round((q1 - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight * 0.1),
        Math.round((q3 - q1) * unitSize),
        Math.round(canvasHeight * 0.8),
        options.get('boxLineColor'),
        options.get('boxFillColor')).append();
    // left whisker
    target.drawLine(
        Math.round((lwhisker - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight / 2),
        Math.round((q1 - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight / 2),
        options.get('lineColor')).append();
    target.drawLine(
        Math.round((lwhisker - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight / 4),
        Math.round((lwhisker - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight - canvasHeight / 4),
        options.get('whiskerColor')).append();
    // right whisker
    target.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight / 2),
        Math.round((q3 - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight / 2),
        options.get('lineColor')).append();
    target.drawLine(
        Math.round((rwhisker - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight / 4),
        Math.round((rwhisker - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight - canvasHeight / 4),
        options.get('whiskerColor')).append();
    // median line
    target.drawLine(
        Math.round((q2 - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight * 0.1),
        Math.round((q2 - minValue) * unitSize + canvasLeft),
        Math.round(canvasHeight * 0.9),
        options.get('medianColor')).append();
    if (options.get('target')) {
        size = Math.ceil(options.get('spotRadius'));
        target.drawLine(
            Math.round((options.get('target') - minValue) * unitSize + canvasLeft),
            Math.round((canvasHeight / 2) - size),
            Math.round((options.get('target') - minValue) * unitSize + canvasLeft),
            Math.round((canvasHeight / 2) + size),
            options.get('targetColor')).append();
        target.drawLine(
            Math.round((options.get('target') - minValue) * unitSize + canvasLeft - size),
            Math.round(canvasHeight / 2),
            Math.round((options.get('target') - minValue) * unitSize + canvasLeft + size),
            Math.round(canvasHeight / 2),
            options.get('targetColor')).append();
    }
    target.render();
}

});