(function(){

"use strict";

var root = this,
        Chart = root.Chart,
        helpers = Chart.helpers;

var defaultConfig = {
        //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
        scaleBeginAtZero : true,

        //Boolean - Whether grid lines are shown across the chart
        scaleShowGridLines : true,

        //String - Colour of the grid lines
        scaleGridLineColor : "rgba(0,0,0,.05)",

        //Number - Width of the grid lines
        scaleGridLineWidth : 1,

        //Boolean - Whether to show horizontal lines (except X axis)
        scaleShowHorizontalLines: true,

        //Boolean - Whether to show vertical lines (except Y axis)
        scaleShowVerticalLines: true,

        //Boolean - If there is a stroke on each bar
        barShowStroke : true,

        //Number - Pixel width of the bar stroke
        barStrokeWidth : 2,

        //Number - Spacing between each of the X value sets
        barValueSpacing : 5,

        //Number - Spacing between data sets within X values
        barDatasetSpacing : 1,

        //String - A legend template
        legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"

};

Chart.Type.extend({
        name: "Bar",
        defaults : defaultConfig,
        initialize:  function(data){

                //Expose options as a scope variable here so we can access it in the ScaleClass
                var options = this.options;

                this.ScaleClass = Chart.Scale.extend({
                        offsetGridLines : true,
                        calculateBarX : function(datasetCount, datasetIndex, barIndex){
                                //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
                                var xWidth = this.calculateBaseWidth(),
                                        xAbsolute = this.calculateX(barIndex) - (xWidth/2),
                                        barWidth = this.calculateBarWidth(datasetCount);

                                return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
                        },
                        calculateBaseWidth : function(){
                                return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
                        },
                        calculateBarWidth : function(datasetCount){
                                //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
                                var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);

                                return (baseWidth / datasetCount);
                        }
                });

                this.datasets = [];

                //Set up tooltip events on the chart
                if (this.options.showTooltips){
                        helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
                                var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];

                                this.eachBars(function(bar){
                                        bar.restore(['fillColor', 'strokeColor']);
                                });
                                helpers.each(activeBars, function(activeBar){
                                        activeBar.fillColor = activeBar.highlightFill;
                                        activeBar.strokeColor = activeBar.highlightStroke;
                                });
                                this.showTooltip(activeBars);
                        });
                }

                //Declare the extension of the default point, to cater for the options passed in to the constructor
                this.BarClass = Chart.Rectangle.extend({
                        strokeWidth : this.options.barStrokeWidth,
                        showStroke : this.options.barShowStroke,
                        ctx : this.chart.ctx
                });

                //Iterate through each of the datasets, and build this into a property of the chart
                helpers.each(data.datasets,function(dataset,datasetIndex){

                        var datasetObject = {
                                label : dataset.label || null,
                                fillColor : dataset.fillColor,
                                strokeColor : dataset.strokeColor,
                                bars : []
                        };

                        this.datasets.push(datasetObject);

                        helpers.each(dataset.data,function(dataPoint,index){
                                //Add a new point for each piece of data, passing any required data to draw.
                                datasetObject.bars.push(new this.BarClass({
                                        value : dataPoint,
                                        label : data.labels[index],
                                        datasetLabel: dataset.label,
                                        strokeColor : dataset.strokeColor,
                                        fillColor : dataset.fillColor,
                                        highlightFill : dataset.highlightFill || dataset.fillColor,
                                        highlightStroke : dataset.highlightStroke || dataset.strokeColor
                                }));
                        },this);

                },this);

                this.buildScale(data.labels);

                this.BarClass.prototype.base = this.scale.endPoint;

                this.eachBars(function(bar, index, datasetIndex){
                        helpers.extend(bar, {
                                width : this.scale.calculateBarWidth(this.datasets.length),
                                x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
                                y: this.scale.endPoint
                        });
                        bar.save();
                }, this);

                this.render();
        },
        update : function(){
                this.scale.update();
                // Reset any highlight colours before updating.
                helpers.each(this.activeElements, function(activeElement){
                        activeElement.restore(['fillColor', 'strokeColor']);
                });

                this.eachBars(function(bar){
                        bar.save();
                });
                this.render();
        },
        eachBars : function(callback){
                helpers.each(this.datasets,function(dataset, datasetIndex){
                        helpers.each(dataset.bars, callback, this, datasetIndex);
                },this);
        },
        getBarsAtEvent : function(e){
                var barsArray = [],
                        eventPosition = helpers.getRelativePosition(e),
                        datasetIterator = function(dataset){
                                barsArray.push(dataset.bars[barIndex]);
                        },
                        barIndex;

                for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
                        for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
                                if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
                                        helpers.each(this.datasets, datasetIterator);
                                        return barsArray;
                                }
                        }
                }

                return barsArray;
        },
        buildScale : function(labels){
                var self = this;

                var dataTotal = function(){
                        var values = [];
                        self.eachBars(function(bar){
                                values.push(bar.value);
                        });
                        return values;
                };

                var scaleOptions = {
                        templateString : this.options.scaleLabel,
                        height : this.chart.height,
                        width : this.chart.width,
                        ctx : this.chart.ctx,
                        textColor : this.options.scaleFontColor,
                        fontSize : this.options.scaleFontSize,
                        fontStyle : this.options.scaleFontStyle,
                        fontFamily : this.options.scaleFontFamily,
                        valuesCount : labels.length,
                        beginAtZero : this.options.scaleBeginAtZero,
                        integersOnly : this.options.scaleIntegersOnly,
                        calculateYRange: function(currentHeight){
                                var updatedRanges = helpers.calculateScaleRange(
                                        dataTotal(),
                                        currentHeight,
                                        this.fontSize,
                                        this.beginAtZero,
                                        this.integersOnly
                                );
                                helpers.extend(this, updatedRanges);
                        },
                        xLabels : labels,
                        font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
                        lineWidth : this.options.scaleLineWidth,
                        lineColor : this.options.scaleLineColor,
                        showHorizontalLines : this.options.scaleShowHorizontalLines,
                        showVerticalLines : this.options.scaleShowVerticalLines,
                        gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
                        gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
                        padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
                        showLabels : this.options.scaleShowLabels,
                        display : this.options.showScale
                };

                if (this.options.scaleOverride){
                        helpers.extend(scaleOptions, {
                                calculateYRange: helpers.noop,
                                steps: this.options.scaleSteps,
                                stepValue: this.options.scaleStepWidth,
                                min: this.options.scaleStartValue,
                                max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
                        });
                }

                this.scale = new this.ScaleClass(scaleOptions);
        },
        addData : function(valuesArray,label){
                //Map the values array for each of the datasets
                helpers.each(valuesArray,function(value,datasetIndex){
                        //Add a new point for each piece of data, passing any required data to draw.
                        this.datasets[datasetIndex].bars.push(new this.BarClass({
                                value : value,
                                label : label,
                                x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
                                y: this.scale.endPoint,
                                width : this.scale.calculateBarWidth(this.datasets.length),
                                base : this.scale.endPoint,
                                strokeColor : this.datasets[datasetIndex].strokeColor,
                                fillColor : this.datasets[datasetIndex].fillColor
                        }));
                },this);

                this.scale.addXLabel(label);
                //Then re-render the chart.
                this.update();
        },
        removeData : function(){
                this.scale.removeXLabel();
                //Then re-render the chart.
                helpers.each(this.datasets,function(dataset){
                        dataset.bars.shift();
                },this);
                this.update();
        },
        reflow : function(){
                helpers.extend(this.BarClass.prototype,{
                        y: this.scale.endPoint,
                        base : this.scale.endPoint
                });
                var newScaleProps = helpers.extend({
                        height : this.chart.height,
                        width : this.chart.width
                });
                this.scale.update(newScaleProps);
        },
        draw : function(ease){
                var easingDecimal = ease || 1;
                this.clear();

                var ctx = this.chart.ctx;

                this.scale.draw(easingDecimal);

                //Draw all the bars for each dataset
                helpers.each(this.datasets,function(dataset,datasetIndex){
                        helpers.each(dataset.bars,function(bar,index){
                                if (bar.hasValue()){
                                        bar.base = this.scale.endPoint;
                                        //Transition then draw
                                        bar.transition({
                                                x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
                                                y : this.scale.calculateY(bar.value),
                                                width : this.scale.calculateBarWidth(this.datasets.length)
                                        }, easingDecimal).draw();
                                }
                        },this);

                },this);
        }
});

}).call(this);