(function ($) {
    function init(plot) {

        var win = "",
            winMs = null,
            X = 0,
            Y = 1;

        function parsewin(plot, options) {
            if (options.window) {
                win = options.window;
            }

            winMs = parseFloat(win);
            if (win.match(/.*(s)$/)) { // seconds
                if (!win.match(/.*(ms)$/)) {
                    winMs = winMs * 1000;
                }
            } else if (win.match(/.*(m)$/)) { // minutes
                winMs = winMs * 60 * 1000;
            } else if (win.match(/.*(h)$/)) {
                winMs = winMs * 60 * 60 * 1000;
            }
        }


        function recalcYBounds(flot) {
            var series = flot.getData(),
                yaxis = flot.getYAxes()[0],
                ymin = yaxis.dataBounds.min,
                ymax = yaxis.dataBounds.max;

            $.each(series, function (index, serie) {
                var points = serie.datapoints.points,
                    ps = serie.datapoints.pointsize;

                for (var i = 0; i < points.length; i += ps) {
                    var y = points[i + Y];
                    if (y < ymin) {
                        ymin = y;
                    }

                    if (y > ymax) {
                        ymax = y;
                    }
                }
            });

            yaxis.dataBounds = { min: ymin, max: ymax };
        }


        plot.hooks.processOptions.push(function (plot, options) {
            parsewin(plot, options);
        });


        plot.hooks.processDatapoints.push(function (flot, serie, datapoints, sliceBounds) {
            if (flot.getOptions().window == undefined) {
                return;
            }

            var redoymin = false,
                redoymax = false,
                xaxis = flot.getXAxes()[0],
                yaxis = flot.getYAxes()[0],
                points = datapoints.points,
                ps = datapoints.pointsize,
                point;

            if (flot.getOptions().window !== win) {
                parsewin(flot, flot.getOptions());
            }

            if (points.length > 0) {
                var lastTimestamp = points[points.length - ps],
                    mustBeFirst = lastTimestamp - winMs;

                // Remove all points outside the window, except the one that just left.
                // Check if I need to recalculate axes bounds.
                while (points[X] < mustBeFirst) {
                    point = shiftPoint(serie, sliceBounds);

                    // Check if we need to recalculate y axis boundaries.
                    if (yaxis.dataBounds != undefined) {
                        redoymin = redoymin || point[Y] <= yaxis.dataBounds.min;
                        redoymax = redoymax || point[Y] >= yaxis.dataBounds.max;
                    }
                }

                if (point) {
                    unshiftPoint(point, serie, sliceBounds);
                }

                if (xaxis.dataBounds != undefined) {
                    xaxis.dataBounds.min = Math.max(mustBeFirst, points[X]);

                    if (redoymin || redoymax) {
                        yaxis.dataBounds.min = Number.POSITIVE_INFINITY;
                        yaxis.dataBounds.max = Number.NEGATIVE_INFINITY;
                        recalcYBounds(flot);
                    }
                }
            }
        });


        function shiftPoint(serie, sliceBounds) {
            var point = serie.data.shift();
            for (var i = 0; i < serie.datapoints.pointsize; i++) {
                serie.datapoints.points.shift();
            }

            sliceBounds[0] -= 1;
            sliceBounds[1] -= 1;

            return point;
        }


        function unshiftPoint(point, serie, sliceBounds) {
            serie.data.unshift(point);
            for (var i = point.length - 1; i >= 0; i--) {
                serie.datapoints.points.unshift(point[i]);
            }

            sliceBounds[0] += 1;
            sliceBounds[1] += 1;
        }
    }

    $.plot.plugins.push({
        init: init,
        options: { window: null },
        name: "window",
        version: "0.1"
    });

})(jQuery);

