/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */

"use strict";

var PDFViewer = {
    queryParams: {},

    element: null,

    previousPageButton: null,
    nextPageButton: null,
    pageNumberInput: null,
    scaleInput: null,
    
    willJumpToPage: false,

    pdf: null,

    url: 'compressed.tracemonkey-pldi-09.pdf',
    pageNumber: 1,
    numberOfPages: 1,

    scale: 1.0,

    pageWidth: function() {
        return 816 * PDFViewer.scale;
    },

    pageHeight: function() {
        return 1056 * PDFViewer.scale;
    },
    
    lastPagesDrawn: [],
    
    visiblePages: function() {  
        var pageHeight = PDFViewer.pageHeight() + 20; // Add 20 for the margins.      
        var windowTop = window.pageYOffset;
        var windowBottom = window.pageYOffset + window.innerHeight;
        var pageStartIndex = Math.floor(windowTop / pageHeight);
        var pageStopIndex = Math.ceil(windowBottom / pageHeight);

        var pages = [];  

        for (var i = pageStartIndex; i <= pageStopIndex; i++) {
            pages.push(i + 1);
        }

        return pages;
    },
  
    createPage: function(num) {
        var anchor = document.createElement('a');
        anchor.name = '' + num;
    
        var div = document.createElement('div');
        div.id = 'pageContainer' + num;
        div.className = 'page';
        div.style.width = PDFViewer.pageWidth() + 'px';
        div.style.height = PDFViewer.pageHeight() + 'px';
        
        PDFViewer.element.appendChild(anchor);
        PDFViewer.element.appendChild(div);
    },
    
    removePage: function(num) {
        var div = document.getElementById('pageContainer' + num);
        
        if (div && div.hasChildNodes()) {
            while (div.childNodes.length > 0) {
                div.removeChild(div.firstChild);
            }
        }
    },

    drawPage: function(num) {
        if (PDFViewer.pdf) {
            var page = PDFViewer.pdf.getPage(num);
            var div = document.getElementById('pageContainer' + num);
            
            if (div && !div.hasChildNodes()) {
                var canvas = document.createElement('canvas');
                canvas.id = 'page' + num;
                canvas.mozOpaque = true;

                // Canvas dimensions must be specified in CSS pixels. CSS pixels
                // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
                canvas.width = PDFViewer.pageWidth();
                canvas.height = PDFViewer.pageHeight();

                var ctx = canvas.getContext('2d');
                ctx.save();
                ctx.fillStyle = 'rgb(255, 255, 255)';
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                ctx.restore();

                var gfx = new CanvasGraphics(ctx);
                var fonts = [];
        
                // page.compile will collect all fonts for us, once we have loaded them
                // we can trigger the actual page rendering with page.display
                page.compile(gfx, fonts);
                
                var fontsReady = true;
                
                // Inspect fonts and translate the missing one
                var fontCount = fonts.length;
                
                for (var i = 0; i < fontCount; i++) {
                    var font = fonts[i];
                    
                    if (Fonts[font.name]) {
                        fontsReady = fontsReady && !Fonts[font.name].loading;
                        continue;
                    }

                    new Font(font.name, font.file, font.properties);
                    
                    fontsReady = false;
                }

                var pageInterval;
                var delayLoadFont = function() {
                    for (var i = 0; i < fontCount; i++) {
                        if (Fonts[font.name].loading) {
                            return;
                        }
                    }
                    
                    clearInterval(pageInterval);

                    PDFViewer.drawPage(num);
                }

                if (!fontsReady) {
                    pageInterval = setInterval(delayLoadFont, 10);
                    return;
                }

                page.display(gfx);
                div.appendChild(canvas);
            }
        }
    },

    changeScale: function(num) {
        while (PDFViewer.element.childNodes.length > 0) {
            PDFViewer.element.removeChild(PDFViewer.element.firstChild);
        }

        PDFViewer.scale = num / 100;

        if (PDFViewer.pdf) {
            for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
                PDFViewer.createPage(i);
            }

            if (PDFViewer.numberOfPages > 0) {
                PDFViewer.drawPage(1);
            }
        }

        PDFViewer.scaleInput.value = Math.floor(PDFViewer.scale * 100) + '%';
    },

    goToPage: function(num) {
        if (1 <= num && num <= PDFViewer.numberOfPages) {
            PDFViewer.pageNumber = num;
            PDFViewer.pageNumberInput.value = PDFViewer.pageNumber;
            PDFViewer.willJumpToPage = true;

            document.location.hash = PDFViewer.pageNumber;

            PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ?
                'disabled' : '';
            PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ?
                'disabled' : '';
        }
    },
  
    goToPreviousPage: function() {
        if (PDFViewer.pageNumber > 1) {
            PDFViewer.goToPage(--PDFViewer.pageNumber);
        }
    },
  
    goToNextPage: function() {
        if (PDFViewer.pageNumber < PDFViewer.numberOfPages) {
            PDFViewer.goToPage(++PDFViewer.pageNumber);
        }
    },
  
    open: function(url) {
        PDFViewer.url = url;
        document.title = url;
    
        var req = new XMLHttpRequest();
        req.open('GET', url);
        req.mozResponseType = req.responseType = 'arraybuffer';
        req.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
    
        req.onreadystatechange = function() {
            if (req.readyState === 4 && req.status === req.expected) {
                var data = req.mozResponseArrayBuffer ||
                    req.mozResponse ||
                    req.responseArrayBuffer ||
                    req.response;

                PDFViewer.pdf = new PDFDoc(new Stream(data));
                PDFViewer.numberOfPages = PDFViewer.pdf.numPages;
                document.getElementById('numPages').innerHTML = PDFViewer.numberOfPages.toString();
          
                for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
                    PDFViewer.createPage(i);
                }

                if (PDFViewer.numberOfPages > 0) {
                    PDFViewer.drawPage(1);
                }
            }
        };
    
        req.send(null);
    }
};

window.onload = function() {

    // Parse the URL query parameters into a cached object.
    PDFViewer.queryParams = function() {
        var qs = window.location.search.substring(1);
        var kvs = qs.split('&');
        var params = {};
        for (var i = 0; i < kvs.length; ++i) {
            var kv = kvs[i].split('=');
            params[unescape(kv[0])] = unescape(kv[1]);
        }
        
        return params;
    }();

    PDFViewer.element = document.getElementById('viewer');

    PDFViewer.pageNumberInput = document.getElementById('pageNumber');
    PDFViewer.pageNumberInput.onkeydown = function(evt) {
        var charCode = evt.charCode || evt.keyCode;

        // Up arrow key.
        if (charCode === 38) {
            PDFViewer.goToNextPage();
            this.select();
        }
        
        // Down arrow key.
        else if (charCode === 40) {
            PDFViewer.goToPreviousPage();
            this.select();
        }

        // All other non-numeric keys (excluding Left arrow, Right arrow,
        // Backspace, and Delete keys).
        else if ((charCode < 48 || charCode > 57) &&
            charCode !== 8 &&   // Backspace
            charCode !== 46 &&  // Delete
            charCode !== 37 &&  // Left arrow
            charCode !== 39     // Right arrow
        ) {
            return false;
        }

        return true;
    };
    PDFViewer.pageNumberInput.onkeyup = function(evt) {
        var charCode = evt.charCode || evt.keyCode;

        // All numeric keys, Backspace, and Delete.
        if ((charCode >= 48 && charCode <= 57) ||
            charCode === 8 ||   // Backspace
            charCode === 46     // Delete
        ) {
            PDFViewer.goToPage(this.value);
        }
        
        this.focus();
    };

    PDFViewer.previousPageButton = document.getElementById('previousPageButton');
    PDFViewer.previousPageButton.onclick = function(evt) {
        if (this.className.indexOf('disabled') === -1) {
            PDFViewer.goToPreviousPage();
        }
    };
    PDFViewer.previousPageButton.onmousedown = function(evt) {
        if (this.className.indexOf('disabled') === -1) {
             this.className = 'down';
        }
    };
    PDFViewer.previousPageButton.onmouseup = function(evt) {
        this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
    };
    PDFViewer.previousPageButton.onmouseout = function(evt) {
        this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
    };
    
    PDFViewer.nextPageButton = document.getElementById('nextPageButton');
    PDFViewer.nextPageButton.onclick = function(evt) {
        if (this.className.indexOf('disabled') === -1) {
            PDFViewer.goToNextPage();
        }
    };
    PDFViewer.nextPageButton.onmousedown = function(evt) {
        if (this.className.indexOf('disabled') === -1) {
            this.className = 'down';
        }
    };
    PDFViewer.nextPageButton.onmouseup = function(evt) {
        this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
    };
    PDFViewer.nextPageButton.onmouseout = function(evt) {
        this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
    };

    PDFViewer.scaleInput = document.getElementById('scale');
    PDFViewer.scaleInput.buttonElement = document.getElementById('scaleComboBoxButton');
    PDFViewer.scaleInput.buttonElement.listElement = document.getElementById('scaleComboBoxList');
    PDFViewer.scaleInput.onchange = function(evt) {
        PDFViewer.changeScale(parseInt(this.value));
    };

    PDFViewer.scaleInput.buttonElement.onclick = function(evt) {
        this.listElement.style.display = (this.listElement.style.display === 'block') ? 'none' : 'block';
    };
    PDFViewer.scaleInput.buttonElement.onmousedown = function(evt) {
        if (this.className.indexOf('disabled') === -1) {
            this.className = 'down';
        }
    };
    PDFViewer.scaleInput.buttonElement.onmouseup = function(evt) {
        this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
    };
    PDFViewer.scaleInput.buttonElement.onmouseout = function(evt) {
        this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
    };
    
    var listItems = PDFViewer.scaleInput.buttonElement.listElement.getElementsByTagName('LI');

    for (var i = 0; i < listItems.length; i++) {
        var listItem = listItems[i];
        listItem.onclick = function(evt) {
            PDFViewer.changeScale(parseInt(this.innerHTML));
            PDFViewer.scaleInput.buttonElement.listElement.style.display = 'none';
        };
    }

    PDFViewer.pageNumber = parseInt(PDFViewer.queryParams.page) || PDFViewer.pageNumber;
    PDFViewer.scale = parseInt(PDFViewer.scaleInput.value) / 100 || 1.0;
    PDFViewer.open(PDFViewer.queryParams.file || PDFViewer.url);

    window.onscroll = function(evt) {        
        var lastPagesDrawn = PDFViewer.lastPagesDrawn;
        var visiblePages = PDFViewer.visiblePages();
        
        var pagesToDraw = [];
        var pagesToKeep = [];
        var pagesToRemove = [];
        
        var i;

        // Determine which visible pages were not previously drawn.
        for (i = 0; i < visiblePages.length; i++) {
            if (lastPagesDrawn.indexOf(visiblePages[i]) === -1) {
                pagesToDraw.push(visiblePages[i]);
                PDFViewer.drawPage(visiblePages[i]);
            } else {
                pagesToKeep.push(visiblePages[i]);
            }
        }

        // Determine which previously drawn pages are no longer visible.
        for (i = 0; i < lastPagesDrawn.length; i++) {
            if (visiblePages.indexOf(lastPagesDrawn[i]) === -1) {
                pagesToRemove.push(lastPagesDrawn[i]);
                PDFViewer.removePage(lastPagesDrawn[i]);
            }
        }
        
        PDFViewer.lastPagesDrawn = pagesToDraw.concat(pagesToKeep);
        
        // Update the page number input with the current page number.
        if (!PDFViewer.willJumpToPage && visiblePages.length > 0) {
            PDFViewer.pageNumber = PDFViewer.pageNumberInput.value = visiblePages[0];
            PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ?
                'disabled' : '';
            PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ?
                'disabled' : '';
        } else {
            PDFViewer.willJumpToPage = false;
        }
    };
};