/* 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_document_properties', ['exports',
            'pdfjs-web/ui_utils', 'pdfjs-web/overlay_manager'], factory);
    } else if (typeof exports !== 'undefined') {
        factory(exports, require('./ui_utils.js'), require('./overlay_manager.js'));
    } else {
        factory((root.pdfjsWebPDFDocumentProperties = {}), root.pdfjsWebUIUtils,
            root.pdfjsWebOverlayManager);
    }
}(this, function(exports, uiUtils, overlayManager) {

    var getPDFFileNameFromURL = uiUtils.getPDFFileNameFromURL;
    var mozL10n = uiUtils.mozL10n;
    var OverlayManager = overlayManager.OverlayManager;

    /**
     * @typedef {Object} PDFDocumentPropertiesOptions
     * @property {string} overlayName - Name/identifier for the overlay.
     * @property {Object} fields - Names and elements of the overlay's fields.
     * @property {HTMLButtonElement} closeButton - Button for closing the overlay.
     */

    /**
     * @class
     */
    var PDFDocumentProperties = (function PDFDocumentPropertiesClosure() {
        /**
         * @constructs PDFDocumentProperties
         * @param {PDFDocumentPropertiesOptions} options
         */
        function PDFDocumentProperties(options) {
            this.fields = options.fields;
            this.overlayName = options.overlayName;
            this.container = options.container;

            this.rawFileSize = 0;
            this.url = null;
            this.pdfDocument = null;

            // Bind the event listener for the Close button.
            if (options.closeButton) {
                options.closeButton.addEventListener('click', this.close.bind(this));
            }

            this.dataAvailablePromise = new Promise(function(resolve) {
                this.resolveDataAvailable = resolve;
            }.bind(this));

            OverlayManager.register(this.overlayName, this.container,
                this.close.bind(this));
        }

        PDFDocumentProperties.prototype = {
            /**
             * Open the document properties overlay.
             */
            open : function PDFDocumentProperties_open() {
                Promise.all([OverlayManager.open(this.overlayName),
                    this.dataAvailablePromise]).then(function() {
                    this._getProperties();
                }.bind(this));
            },

            /**
             * Close the document properties overlay.
             */
            close : function PDFDocumentProperties_close() {
                OverlayManager.close(this.overlayName);
            },

            /**
             * Set the file size of the PDF document. This method is used to
             * update the file size in the document properties overlay once it
             * is known so we do not have to wait until the entire file is loaded.
             *
             * @param {number} fileSize - The file size of the PDF document.
             */
            setFileSize : function PDFDocumentProperties_setFileSize(fileSize) {
                if (fileSize > 0) {
                    this.rawFileSize = fileSize;
                }
            },

            /**
             * Set a reference to the PDF document and the URL in order
             * to populate the overlay fields with the document properties.
             * Note that the overlay will contain no information if this method
             * is not called.
             *
             * @param {Object} pdfDocument - A reference to the PDF document.
             * @param {string} url - The URL of the document.
             */
            setDocumentAndUrl :
                function PDFDocumentProperties_setDocumentAndUrl(pdfDocument, url) {
                    this.pdfDocument = pdfDocument;
                    this.url = url;
                    this.resolveDataAvailable();
                },

            /**
             * @private
             */
            _getProperties : function PDFDocumentProperties_getProperties() {
                if (!OverlayManager.active) {
                    // If the dialog was closed before dataAvailablePromise was resolved,
                    // don't bother updating the properties.
                    return;
                }
                // Get the file size (if it hasn't already been set).
                this.pdfDocument.getDownloadInfo().then(function(data) {
                    if (data.length === this.rawFileSize) {
                        return;
                    }
                    this.setFileSize(data.length);
                    this._updateUI(this.fields['fileSize'], this._parseFileSize());
                }.bind(this));

                // Get the document properties.
                this.pdfDocument.getMetadata().then(function(data) {
                    var content = {
                        'fileName'         : getPDFFileNameFromURL(this.url),
                        'fileSize'         : this._parseFileSize(),
                        'title'            : data.info.Title,
                        'author'           : data.info.Author,
                        'subject'          : data.info.Subject,
                        'keywords'         : data.info.Keywords,
                        'creationDate'     : this._parseDate(data.info.CreationDate),
                        'modificationDate' : this._parseDate(data.info.ModDate),
                        'creator'          : data.info.Creator,
                        'producer'         : data.info.Producer,
                        'version'          : data.info.PDFFormatVersion,
                        'pageCount'        : this.pdfDocument.numPages
                    };

                    // Show the properties in the dialog.
                    for (var identifier in content) {
                        this._updateUI(this.fields[identifier], content[identifier]);
                    }
                }.bind(this));
            },

            /**
             * @private
             */
            _updateUI : function PDFDocumentProperties_updateUI(field, content) {
                if (field && content !== undefined && content !== '') {
                    field.textContent = content;
                }
            },

            /**
             * @private
             */
            _parseFileSize : function PDFDocumentProperties_parseFileSize() {
                var fileSize = this.rawFileSize, kb = fileSize / 1024;
                if (!kb) {
                    return;
                } else if (kb < 1024) {
                    return mozL10n.get('document_properties_kb', {
                        size_kb : (+kb.toPrecision(3)).toLocaleString(),
                        size_b  : fileSize.toLocaleString()
                    }, '{{size_kb}} KB ({{size_b}} bytes)');
                } else {
                    return mozL10n.get('document_properties_mb', {
                        size_mb : (+(kb / 1024).toPrecision(3)).toLocaleString(),
                        size_b  : fileSize.toLocaleString()
                    }, '{{size_mb}} MB ({{size_b}} bytes)');
                }
            },

            /**
             * @private
             */
            _parseDate : function PDFDocumentProperties_parseDate(inputDate) {
                // This is implemented according to the PDF specification, but note that
                // Adobe Reader doesn't handle changing the date to universal time
                // and doesn't use the user's time zone (they're effectively ignoring
                // the HH' and mm' parts of the date string).
                var dateToParse = inputDate;
                if (dateToParse === undefined) {
                    return '';
                }

                // Remove the D: prefix if it is available.
                if (dateToParse.substring(0, 2) === 'D:') {
                    dateToParse = dateToParse.substring(2);
                }

                // Get all elements from the PDF date string.
                // JavaScript's Date object expects the month to be between
                // 0 and 11 instead of 1 and 12, so we're correcting for this.
                var year = parseInt(dateToParse.substring(0, 4), 10);
                var month = parseInt(dateToParse.substring(4, 6), 10) - 1;
                var day = parseInt(dateToParse.substring(6, 8), 10);
                var hours = parseInt(dateToParse.substring(8, 10), 10);
                var minutes = parseInt(dateToParse.substring(10, 12), 10);
                var seconds = parseInt(dateToParse.substring(12, 14), 10);
                var utRel = dateToParse.substring(14, 15);
                var offsetHours = parseInt(dateToParse.substring(15, 17), 10);
                var offsetMinutes = parseInt(dateToParse.substring(18, 20), 10);

                // As per spec, utRel = 'Z' means equal to universal time.
                // The other cases ('-' and '+') have to be handled here.
                if (utRel === '-') {
                    hours += offsetHours;
                    minutes += offsetMinutes;
                } else if (utRel === '+') {
                    hours -= offsetHours;
                    minutes -= offsetMinutes;
                }

                // Return the new date format from the user's locale.
                var date = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
                var dateString = date.toLocaleDateString();
                var timeString = date.toLocaleTimeString();
                return mozL10n.get('document_properties_date_string',
                    {date : dateString, time : timeString},
                    '{{date}}, {{time}}');
            }
        };

        return PDFDocumentProperties;
    })();

    exports.PDFDocumentProperties = PDFDocumentProperties;
}));
