/*global define, module*/

/* Detect-zoom
 * -----------
 * Cross Browser Zoom and Pixel Ratio Detector
 * Version 1.0.4 | Apr 1 2013
 * dual-licensed under the WTFPL and MIT license
 * Maintained by https://github/tombigel
 * Original developer https://github.com/yonran
 */

//AMD and CommonJS initialization copied from https://github.com/zohararad/audio5js
(function (root, ns, factory) {
    'use strict';

    if (typeof (module) !== 'undefined' && module.exports) { // CommonJS
        module.exports = factory(ns, root);
    } else if (typeof (define) === 'function' && define.amd) { // AMD
        define('detect-zoom', function () {
            return factory(ns, root);
        });
    } else {
        root[ns] = factory(ns, root);
    }

}(window, 'detectZoom', function () {
    'use strict';

    /**
     * Use devicePixelRatio if supported by the browser
     * @return {Number}
     * @private
     */
    var devicePixelRatio = function () {
        return window.devicePixelRatio || 1;
    };

    /**
     * Use a binary search through media queries to find zoom level in Firefox
     * @param property
     * @param unit
     * @param a
     * @param b
     * @param maxIter
     * @param epsilon
     * @return {Number}
     */
    var mediaQueryBinarySearch = function (property, unit, a, b, maxIter, epsilon) {
        var binarySearch = function(a, b, maxIter) {
            var mid = (a + b) / 2;
            if (maxIter <= 0 || b - a < epsilon) {
                return mid;
            }
            var query = '(' + property + ':' + mid + unit + ')';
            if (matchMedia(query).matches) {
                return binarySearch(mid, b, maxIter - 1);
            } else {
                return binarySearch(a, mid, maxIter - 1);
            }
        };
        var matchMedia;
        var head, style, div;
        if (window.matchMedia) {
            matchMedia = window.matchMedia;
        } else {
            head = document.getElementsByTagName('head')[0];
            style = document.createElement('style');
            head.appendChild(style);

            div = document.createElement('div');
            div.className = 'mediaQueryBinarySearch';
            div.style.display = 'none';
            document.body.appendChild(div);

            matchMedia = function (query) {
                style.sheet.insertRule('@media ' + query + '{.mediaQueryBinarySearch ' + '{text-decoration: underline} }', 0);
                var matched = getComputedStyle(div, null).textDecoration === 'underline';
                style.sheet.deleteRule(0);
                return {matches: matched};
            };
        }
        var ratio = binarySearch(a, b, maxIter);
        if (div) {
            head.removeChild(style);
            document.body.removeChild(div);
        }
        return ratio;

    };

    /**
     * Generate detection function
     * @private
     */
    var detectFunction = (function () {
        // first the fallback
        var func = function () {
            return {
                zoom: 1,
                devicePxPerCssPx: 1
            };
        };

        // IE 8 and 9: no trick needed!
        if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
            func = function () {
                var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100) / 100;
                return {
                    zoom: zoom,
                    devicePxPerCssPx: zoom * devicePixelRatio()
                };
            };
        }

        // IE10+ / Touch
        // thanks https://github.com/stefanvanburen
        // TODO: Test this function!!!
        // Chrome returns the full height of the document... not just the viewport for the offsetHeight
        // and the height of the window (duh) for window.innerHeight.
        else if (window.navigator.msMaxTouchPoints) {
            func = function () {
                var zoom = Math.round((document.documentElement.offsetHeight / window.innerHeight) * 100) / 100;
                return {
                    zoom: zoom,
                    devicePxPerCssPx: zoom * devicePixelRatio()
                };
            };
        }


        // Mobile Webkit
        // the trick: window.innerWIdth is in CSS pixels, while
        // screen.width and screen.height are in system pixels.
        // And there are no scrollbars to mess up the measurement.
        else if ('orientation' in window) {
            func = function () {
                var deviceWidth = (Math.abs(window.orientation) === 90) ? screen.height : screen.width;
                var zoom = deviceWidth / window.innerWidth;
                return {
                    zoom: zoom,
                    devicePxPerCssPx: zoom * devicePixelRatio()
                };
            };
        }

        // Desktop Webkit
        // the trick: an element's clientHeight is in CSS pixels, while you can
        // set its line-height in system pixels using font-size and
        // -webkit-text-size-adjust:none.
        // device-pixel-ratio: http://www.webkit.org/blog/55/high-dpi-web-sites/
        // 
        // Previous trick (used before http://trac.webkit.org/changeset/100847):
        // documentElement.scrollWidth is in CSS pixels, while
        // document.width was in system pixels. Note that this is the
        // layout width of the document, which is slightly different from viewport
        // because document width does not include scrollbars and might be wider
        // due to big elements.
        // 
        else if ('-webkit-text-size-adjust' in document.body.style || 'text-size-adjust' in document.body.style) {
            func = function () {
                var important = function (str) {
                    return str.replace(/;/g, ' !important;');
                };

                var div = document.createElement('div');
                div.innerHTML = '1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>0';
                div.setAttribute('style', important('font: 100px/1em sans-serif; -webkit-text-size-adjust: none; text-size-adjust: none; height: auto; width: 1em; padding: 0; overflow: visible;'));

                // The container exists so that the div will be laid out in its own flow
                // while not impacting the layout, viewport size, or display of the
                // webpage as a whole.
                // Add !important and relevant CSS rule resets
                // so that other rules cannot affect the results.
                var container = document.createElement('div');
                container.setAttribute('style', important('width:0; height:0; overflow:hidden; visibility:hidden; position: absolute;'));
                container.appendChild(div);

                document.body.appendChild(container);
                var zoom = 1000 / div.clientHeight;
                zoom = Math.round(zoom * 100) / 100;
                document.body.removeChild(container);

                return{
                    zoom: zoom,
                    devicePxPerCssPx: zoom * devicePixelRatio()
                };
            };
        }

        else {
            // FF 4.0+
            // This one's a bit more expensive, so we'll do it almost last...
            //
            // no real trick; device-pixel-ratio is the ratio of device dpi / css dpi.
            // (Note that this is a different interpretation than Webkit's device
            // pixel ratio, which is the ratio device dpi / system dpi).
            //
            // Also, for Mozilla, there is no difference between the zoom factor and the device ratio.
            //
            // The pixel ratio is present starting in version 18, so if it's there, use it...
            var ff = function () {
                var zoom = mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001);
                zoom = Math.round(zoom * 100) / 100;
                return {
                    zoom: zoom,
                    devicePxPerCssPx: window.devicePixelRatio || zoom
                };
            };

            if (ff().zoom > 0.001) {
                func = ff;
            }

            // Chrome & Safari
            // This is actually last, as it's the most error prone... Known not to work well with
            // the web inspector open on the side and generally ends up a few pixels off anyway.
            else if(window.outerWidth && window.innerWidth){
                func = function() {
                    var zoom = Math.round((window.outerWidth / window.innerWidth)*100) / 100;
                    return {
                        zoom: zoom,
                        devicePxPerCssPx: zoom * devicePixelRatio()
                    };      
                };
            }

            // Opera
            // I'm not sure why this one needs to be different... but it's almost the same,
            // so we'll stick it down here, as well.
            //
            // works starting Opera 11.11
            // the trick: outerWidth is the viewport width including scrollbars in
            // system px, while innerWidth is the viewport width including scrollbars
            // in CSS px
            else if (window.top.outerWidth && window.top.innerWidth) {
                func = function () {
                    var zoom = window.top.outerWidth / window.top.innerWidth;
                    zoom = Math.round(zoom * 100) / 100;
                    return {
                        zoom: zoom,
                        devicePxPerCssPx: zoom * devicePixelRatio()
                    };
                };
            }
        }

        return func;
    }());


    return ({

        /**
         * Ratios.zoom shorthand
         * @return {Number} Zoom level
         */
        zoom: function () {
            return detectFunction().zoom;
        },

        /**
         * Ratios.devicePxPerCssPx shorthand
         * @return {Number} devicePxPerCssPx level
         */
        device: function () {
            return detectFunction().devicePxPerCssPx;
        }
    });
}));
