/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function (require, exports, module) {
    "use strict";

    var XHTML_NS = "http://www.w3.org/1999/xhtml";

    exports.getDocumentHead = function (doc) {
        if (!doc)
            doc = document;
        return doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement;
    }

    exports.createElement = function (tag, ns) {
        return document.createElementNS ?
            document.createElementNS(ns || XHTML_NS, tag) :
            document.createElement(tag);
    };

    exports.hasCssClass = function (el, name) {
        var classes = (el.className + "").split(/\s+/g);
        return classes.indexOf(name) !== -1;
    };

    /*
     * Add a CSS class to the list of classes on the given node
     */
    exports.addCssClass = function (el, name) {
        if (!exports.hasCssClass(el, name)) {
            el.className += " " + name;
        }
    };

    /*
     * Remove a CSS class from the list of classes on the given node
     */
    exports.removeCssClass = function (el, name) {
        var classes = el.className.split(/\s+/g);
        while (true) {
            var index = classes.indexOf(name);
            if (index == -1) {
                break;
            }
            classes.splice(index, 1);
        }
        el.className = classes.join(" ");
    };

    exports.toggleCssClass = function (el, name) {
        var classes = el.className.split(/\s+/g), add = true;
        while (true) {
            var index = classes.indexOf(name);
            if (index == -1) {
                break;
            }
            add = false;
            classes.splice(index, 1);
        }
        if (add)
            classes.push(name);

        el.className = classes.join(" ");
        return add;
    };


    /*
     * Add or remove a CSS class from the list of classes on the given node
     * depending on the value of <tt>include</tt>
     */
    exports.setCssClass = function (node, className, include) {
        if (include) {
            exports.addCssClass(node, className);
        } else {
            exports.removeCssClass(node, className);
        }
    };

    exports.hasCssString = function (id, doc) {
        var index = 0, sheets;
        doc = doc || document;

        if (doc.createStyleSheet && (sheets = doc.styleSheets)) {
            while (index < sheets.length)
                if (sheets[index++].owningElement.id === id) return true;
        } else if ((sheets = doc.getElementsByTagName("style"))) {
            while (index < sheets.length)
                if (sheets[index++].id === id) return true;
        }

        return false;
    };

    exports.importCssString = function importCssString(cssText, id, doc) {
        doc = doc || document;
        // If style is already imported return immediately.
        if (id && exports.hasCssString(id, doc))
            return null;

        var style;

        if (id)
            cssText += "\n/*# sourceURL=ace/css/" + id + " */";

        if (doc.createStyleSheet) {
            style = doc.createStyleSheet();
            style.cssText = cssText;
            if (id)
                style.owningElement.id = id;
        } else {
            style = exports.createElement("style");
            style.appendChild(doc.createTextNode(cssText));
            if (id)
                style.id = id;

            exports.getDocumentHead(doc).appendChild(style);
        }
    };

    exports.importCssStylsheet = function (uri, doc) {
        if (doc.createStyleSheet) {
            doc.createStyleSheet(uri);
        } else {
            var link = exports.createElement('link');
            link.rel = 'stylesheet';
            link.href = uri;

            exports.getDocumentHead(doc).appendChild(link);
        }
    };

    exports.getInnerWidth = function (element) {
        return (
            parseInt(exports.computedStyle(element, "paddingLeft"), 10) +
            parseInt(exports.computedStyle(element, "paddingRight"), 10) +
            element.clientWidth
        );
    };

    exports.getInnerHeight = function (element) {
        return (
            parseInt(exports.computedStyle(element, "paddingTop"), 10) +
            parseInt(exports.computedStyle(element, "paddingBottom"), 10) +
            element.clientHeight
        );
    };

    exports.scrollbarWidth = function (document) {
        var inner = exports.createElement("ace_inner");
        inner.style.width = "100%";
        inner.style.minWidth = "0px";
        inner.style.height = "200px";
        inner.style.display = "block";

        var outer = exports.createElement("ace_outer");
        var style = outer.style;

        style.position = "absolute";
        style.left = "-10000px";
        style.overflow = "hidden";
        style.width = "200px";
        style.minWidth = "0px";
        style.height = "150px";
        style.display = "block";

        outer.appendChild(inner);

        var body = document.documentElement;
        body.appendChild(outer);

        var noScrollbar = inner.offsetWidth;

        style.overflow = "scroll";
        var withScrollbar = inner.offsetWidth;

        if (noScrollbar == withScrollbar) {
            withScrollbar = outer.clientWidth;
        }

        body.removeChild(outer);

        return noScrollbar - withScrollbar;
    };

    if (typeof document == "undefined") {
        exports.importCssString = function () {
        };
        return;
    }

    if (window.pageYOffset !== undefined) {
        exports.getPageScrollTop = function () {
            return window.pageYOffset;
        };

        exports.getPageScrollLeft = function () {
            return window.pageXOffset;
        };
    }
    else {
        exports.getPageScrollTop = function () {
            return document.body.scrollTop;
        };

        exports.getPageScrollLeft = function () {
            return document.body.scrollLeft;
        };
    }

    if (window.getComputedStyle)
        exports.computedStyle = function (element, style) {
            if (style)
                return (window.getComputedStyle(element, "") || {})[style] || "";
            return window.getComputedStyle(element, "") || {};
        };
    else
        exports.computedStyle = function (element, style) {
            if (style)
                return element.currentStyle[style];
            return element.currentStyle;
        };

    /*
     * Optimized set innerHTML. This is faster than plain innerHTML if the element
     * already contains a lot of child elements.
     *
     * See http://blog.stevenlevithan.com/archives/faster-than-innerhtml for details
     */
    exports.setInnerHtml = function (el, innerHtml) {
        var element = el.cloneNode(false);//document.createElement("div");
        element.innerHTML = innerHtml;
        el.parentNode.replaceChild(element, el);
        return element;
    };

    if ("textContent" in document.documentElement) {
        exports.setInnerText = function (el, innerText) {
            el.textContent = innerText;
        };

        exports.getInnerText = function (el) {
            return el.textContent;
        };
    }
    else {
        exports.setInnerText = function (el, innerText) {
            el.innerText = innerText;
        };

        exports.getInnerText = function (el) {
            return el.innerText;
        };
    }

    exports.getParentWindow = function (document) {
        return document.defaultView || document.parentWindow;
    };

});
