/* Copyright 2012 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        define('pdfjs-web/pdf_rendering_queue', ['exports'], factory);
    } else if (typeof exports !== 'undefined') {
        factory(exports);
    } else {
        factory((root.pdfjsWebPDFRenderingQueue = {}));
    }
}(this, function(exports) {

    var CLEANUP_TIMEOUT = 30000;

    var RenderingStates = {
        INITIAL  : 0,
        RUNNING  : 1,
        PAUSED   : 2,
        FINISHED : 3
    };

    /**
     * Controls rendering of the views for pages and thumbnails.
     * @class
     */
    var PDFRenderingQueue = (function PDFRenderingQueueClosure() {
        /**
         * @constructs
         */
        function PDFRenderingQueue() {
            this.pdfViewer = null;
            this.pdfThumbnailViewer = null;
            this.onIdle = null;

            this.highestPriorityPage = null;
            this.idleTimeout = null;
            this.printing = false;
            this.isThumbnailViewEnabled = false;
        }

        PDFRenderingQueue.prototype = /** @lends PDFRenderingQueue.prototype */ {
            /**
             * @param {PDFViewer} pdfViewer
             */
            setViewer : function PDFRenderingQueue_setViewer(pdfViewer) {
                this.pdfViewer = pdfViewer;
            },

            /**
             * @param {PDFThumbnailViewer} pdfThumbnailViewer
             */
            setThumbnailViewer :
                function PDFRenderingQueue_setThumbnailViewer(pdfThumbnailViewer) {
                    this.pdfThumbnailViewer = pdfThumbnailViewer;
                },

            /**
             * @param {IRenderableView} view
             * @returns {boolean}
             */
            isHighestPriority : function PDFRenderingQueue_isHighestPriority(view) {
                return this.highestPriorityPage === view.renderingId;
            },

            renderHighestPriority : function
                PDFRenderingQueue_renderHighestPriority(currentlyVisiblePages) {
                if (this.idleTimeout) {
                    clearTimeout(this.idleTimeout);
                    this.idleTimeout = null;
                }

                // Pages have a higher priority than thumbnails, so check them first.
                if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
                    return;
                }
                // No pages needed rendering so check thumbnails.
                if (this.pdfThumbnailViewer && this.isThumbnailViewEnabled) {
                    if (this.pdfThumbnailViewer.forceRendering()) {
                        return;
                    }
                }

                if (this.printing) {
                    // If printing is currently ongoing do not reschedule cleanup.
                    return;
                }

                if (this.onIdle) {
                    this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT);
                }
            },

            getHighestPriority : function
                PDFRenderingQueue_getHighestPriority(visible, views, scrolledDown) {
                // The state has changed figure out which page has the highest priority to
                // render next (if any).
                // Priority:
                // 1 visible pages
                // 2 if last scrolled down page after the visible pages
                // 2 if last scrolled up page before the visible pages
                var visibleViews = visible.views;

                var numVisible = visibleViews.length;
                if (numVisible === 0) {
                    return false;
                }
                for (var i = 0; i < numVisible; ++i) {
                    var view = visibleViews[i].view;
                    if (!this.isViewFinished(view)) {
                        return view;
                    }
                }

                // All the visible views have rendered, try to render next/previous pages.
                if (scrolledDown) {
                    var nextPageIndex = visible.last.id;
                    // ID's start at 1 so no need to add 1.
                    if (views[nextPageIndex] &&
                        !this.isViewFinished(views[nextPageIndex])) {
                        return views[nextPageIndex];
                    }
                } else {
                    var previousPageIndex = visible.first.id - 2;
                    if (views[previousPageIndex] &&
                        !this.isViewFinished(views[previousPageIndex])) {
                        return views[previousPageIndex];
                    }
                }
                // Everything that needs to be rendered has been.
                return null;
            },

            /**
             * @param {IRenderableView} view
             * @returns {boolean}
             */
            isViewFinished : function PDFRenderingQueue_isViewFinished(view) {
                return view.renderingState === RenderingStates.FINISHED;
            },

            /**
             * Render a page or thumbnail view. This calls the appropriate function
             * based on the views state. If the view is already rendered it will return
             * false.
             * @param {IRenderableView} view
             */
            renderView : function PDFRenderingQueue_renderView(view) {
                var state = view.renderingState;
                switch (state) {
                    case RenderingStates.FINISHED:
                        return false;
                    case RenderingStates.PAUSED:
                        this.highestPriorityPage = view.renderingId;
                        view.resume();
                        break;
                    case RenderingStates.RUNNING:
                        this.highestPriorityPage = view.renderingId;
                        break;
                    case RenderingStates.INITIAL:
                        this.highestPriorityPage = view.renderingId;
                        var continueRendering = function() {
                            this.renderHighestPriority();
                        }.bind(this);
                        view.draw().then(continueRendering, continueRendering);
                        break;
                }
                return true;
            },
        };

        return PDFRenderingQueue;
    })();

    exports.RenderingStates = RenderingStates;
    exports.PDFRenderingQueue = PDFRenderingQueue;
}));
