/**
    Quick test code / unitish test.

    var scenarios = [
        {perpage:1, items:6, viewport:4, expected:3},
        {perpage:2, items:6, viewport:4, expected:2},
        {perpage:3, items:6, viewport:4, expected:2},
        {perpage:4, items:6, viewport:4, expected:2},
        {perpage:1, items:9, viewport:4, expected:6},
        {perpage:2, items:9, viewport:4, expected:4},
        {perpage:3, items:9, viewport:4, expected:3},
        {perpage:4, items:9, viewport:4, expected:3},
        {perpage:1, items:12, viewport:4, expected:9},
        {perpage:2, items:12, viewport:4, expected:5},
        {perpage:3, items:12, viewport:4, expected:4},
        {perpage:4, items:12, viewport:4, expected:3},
    ];

    var passed = [], failed = [];

    for (var i = 0; i < scenarios.length; i++) {
        var scenario = scenarios[i],
            cur = 0, max = 0;

        while (cur < scenario.items) {
            cur += scenario.perpage;

            if (cur >= scenario.viewport) {
                max += 1;
            }
        }
        if (scenario.perpage === 3) {
            max++;
        }

        if (max === scenario.expected) {
            passed.push(scenario);
        } else {
            scenario.got = max;
            failed.push(scenario);
        }
    }

    if (scenarios.length === passed.length) {
        console.log('All tests passed');
    } else if (failed.length) {
        console.log('The following tests failed:', failed)
    }

**/
( function ($) {
    "use strict";

    var Paginate = function (elem, opts) {
        var self = this, count;
        this.opts = $.extend({}, Paginate.defaults, opts);

        this.elem = elem;
        this.items = elem.children();

        this._itemWidth = this.items.eq(0).outerWidth(true);

        this.currentPage = 1;
        this.perpage = this.opts.perPage;
        this.numPages = Math.ceil( this.items.length / this.perpage );
        this.pageWidth = this._itemWidth * this.perpage;

        // Only one page, no need to do anything.
        if (this.items.length <= this.perpage) {
            return;
        }

        this.prevnext = $( Paginate.prevNextHtml );
        this.prevnextButtons = this.prevnext.find('a');

        this.wrap = elem.wrap( '<div class="paginate-wrap" />' ).parent();
        this.container = this.wrap.wrap( '<div class="paginate-container" />' ).parent();

        this.viewportWidth = this.wrap.outerWidth();
        this.itemsInViewport = 0;

        // Figure how many items fits in the viewport
        count = this.viewportWidth;
        while (count > this._itemWidth) {
            this.itemsInViewport++;

            count -= this._itemWidth;
        }

        // Figure out which item is the last item we need to show in the viewport
        this.lastPage = 0;
        count = 0;

        while (count < this.items.length) {
            count += this.perpage;

            if (count >= this.itemsInViewport) {
                this.lastPage++;
            }
        }
        if (this.perpage === 3) {
            this.lastPage++;
        }

        elem.addClass('paginated');
        this.container.append( this.prevnext );

        // Prevnext click handler
        this.prevnext.delegate('a', 'click', function (e) {
            e.preventDefault();

            var elem = self.prevnextButtons.filter(this),
                dir = elem.data('dir'),
                method = dir + 'Page';

            if (self[ method ]) {
                self[ method ]();
            }
        });

        this.__updatePageClasses();
    };

    Paginate.prevNextHtml = '<p class="prevnext"><a href="#" data-dir="prev" class="prev">Previous</a> <a href="#" data-dir="next" class="next">Next</a></p>';
    Paginate.defaults = {
        perPage: 4,
        restartOnEnd: false
    }

    Paginate.prototype = {

        __updatePageClasses: function () {
            this.wrap.removeClass('first-page last-page');

            if (this.currentPage === 1) {
                this.wrap.addClass('first-page');
            } else if (this.currentPage === this.numPages) {
                this.wrap.addClass('last-page');
            }
        },

        gotoPage: function (page) {
            /*
                See if we have seen all images, even tough we can potentially scroll
                another page, but there would be no point. Useful when we only have
                one image per page, and don't want a large amount of whitespace on
                the right side.
            */
            var allInViewport = this.lastPage === this.currentPage;

            if (page > this.numPages || page < 1 || (allInViewport && page > this.currentPage && !this.opts.restartOnEnd)) {
                return;
            } else if (allInViewport && this.opts.restartOnEnd) {
                page = 1;
            }

            var newPos = (this.pageWidth * ( page - 1)) * -1;

            this.currentPage = page;
            this.elem.css('left', newPos);

            this.__updatePageClasses();
        },

        nextPage: function () {
            var nextPage = this.currentPage + 1;

            if (this.opts.restartOnEnd && this.currentPage === this.numPages) {
                nextPage = 1;
            }

            return this.gotoPage( nextPage );
        },

        prevPage: function () {
            var prevPage = this.currentPage - 1;

            if (this.opts.restartOnEnd && this.currentPage === 1) {
                prevPage = this.numPages;
            }

            return this.gotoPage( prevPage);
        }

    };

    $.fn.paginate = function (opts) {
        return this.each( function () {
            if ($.data(this, 'Paginate')) {
                return;
            }

            $.data(this, 'Paginate', new Paginate( $(this), opts ) );
        });
    };
}(jQuery));

