/* ***** 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('ace/mode/html_completions', ['require', 'exports', 'module', 'ace/token_iterator'], function (require, exports, module) {


    var TokenIterator = require("../token_iterator").TokenIterator;

    var commonAttributes = [
        "accesskey",
        "class",
        "contenteditable",
        "contextmenu",
        "dir",
        "draggable",
        "dropzone",
        "hidden",
        "id",
        "lang",
        "spellcheck",
        "style",
        "tabindex",
        "title",
        "translate"
    ];

    var eventAttributes = [
        "onabort",
        "onblur",
        "oncancel",
        "oncanplay",
        "oncanplaythrough",
        "onchange",
        "onclick",
        "onclose",
        "oncontextmenu",
        "oncuechange",
        "ondblclick",
        "ondrag",
        "ondragend",
        "ondragenter",
        "ondragleave",
        "ondragover",
        "ondragstart",
        "ondrop",
        "ondurationchange",
        "onemptied",
        "onended",
        "onerror",
        "onfocus",
        "oninput",
        "oninvalid",
        "onkeydown",
        "onkeypress",
        "onkeyup",
        "onload",
        "onloadeddata",
        "onloadedmetadata",
        "onloadstart",
        "onmousedown",
        "onmousemove",
        "onmouseout",
        "onmouseover",
        "onmouseup",
        "onmousewheel",
        "onpause",
        "onplay",
        "onplaying",
        "onprogress",
        "onratechange",
        "onreset",
        "onscroll",
        "onseeked",
        "onseeking",
        "onselect",
        "onshow",
        "onstalled",
        "onsubmit",
        "onsuspend",
        "ontimeupdate",
        "onvolumechange",
        "onwaiting"
    ];

    var globalAttributes = commonAttributes.concat(eventAttributes);

    var attributeMap = {
        "html": ["manifest"],
        "head": [],
        "title": [],
        "base": ["href", "target"],
        "link": ["href", "hreflang", "rel", "media", "type", "sizes"],
        "meta": ["http-equiv", "name", "content", "charset"],
        "style": ["type", "media", "scoped"],
        "script": ["charset", "type", "src", "defer", "async"],
        "noscript": ["href"],
        "body": ["onafterprint", "onbeforeprint", "onbeforeunload", "onhashchange", "onmessage", "onoffline", "onpopstate", "onredo", "onresize", "onstorage", "onundo", "onunload"],
        "section": [],
        "nav": [],
        "article": ["pubdate"],
        "aside": [],
        "h1": [],
        "h2": [],
        "h3": [],
        "h4": [],
        "h5": [],
        "h6": [],
        "header": [],
        "footer": [],
        "address": [],
        "main": [],
        "p": [],
        "hr": [],
        "pre": [],
        "blockquote": ["cite"],
        "ol": ["start", "reversed"],
        "ul": [],
        "li": ["value"],
        "dl": [],
        "dt": [],
        "dd": [],
        "figure": [],
        "figcaption": [],
        "div": [],
        "a": ["href", "target", "ping", "rel", "media", "hreflang", "type"],
        "em": [],
        "strong": [],
        "small": [],
        "s": [],
        "cite": [],
        "q": ["cite"],
        "dfn": [],
        "abbr": [],
        "data": [],
        "time": ["datetime"],
        "code": [],
        "var": [],
        "samp": [],
        "kbd": [],
        "sub": [],
        "sup": [],
        "i": [],
        "b": [],
        "u": [],
        "mark": [],
        "ruby": [],
        "rt": [],
        "rp": [],
        "bdi": [],
        "bdo": [],
        "span": [],
        "br": [],
        "wbr": [],
        "ins": ["cite", "datetime"],
        "del": ["cite", "datetime"],
        "img": ["alt", "src", "height", "width", "usemap", "ismap"],
        "iframe": ["name", "src", "height", "width", "sandbox", "seamless"],
        "embed": ["src", "height", "width", "type"],
        "object": ["param", "data", "type", "height", "width", "usemap", "name", "form", "classid"],
        "param": ["name", "value"],
        "video": ["src", "autobuffer", "autoplay", "loop", "controls", "width", "height", "poster"],
        "audio": ["src", "autobuffer", "autoplay", "loop", "controls"],
        "source": ["src", "type", "media"],
        "track": ["kind", "src", "srclang", "label", "default"],
        "canvas": ["width", "height"],
        "map": ["name"],
        "area": ["shape", "coords", "href", "hreflang", "alt", "target", "media", "rel", "ping", "type"],
        "svg": [],
        "math": [],
        "table": ["summary"],
        "caption": [],
        "colgroup": ["span"],
        "col": ["span"],
        "tbody": [],
        "thead": [],
        "tfoot": [],
        "tr": [],
        "td": ["headers", "rowspan", "colspan"],
        "th": ["headers", "rowspan", "colspan", "scope"],
        "form": ["accept-charset", "action", "autocomplete", "enctype", "method", "name", "novalidate", "target"],
        "fieldset": ["disabled", "form", "name"],
        "legend": [],
        "label": ["form", "for"],
        "input": ["type", "accept", "alt", "autocomplete", "checked", "disabled", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "height", "list", "max", "maxlength", "min", "multiple", "pattern", "placeholder", "readonly", "required", "size", "src", "step", "width", "files", "value"],
        "button": ["autofocus", "disabled", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "name", "value", "type"],
        "select": ["autofocus", "disabled", "form", "multiple", "name", "size"],
        "datalist": [],
        "optgroup": ["disabled", "label"],
        "option": ["disabled", "selected", "label", "value"],
        "textarea": ["autofocus", "disabled", "form", "maxlength", "name", "placeholder", "readonly", "required", "rows", "cols", "wrap"],
        "keygen": ["autofocus", "challenge", "disabled", "form", "keytype", "name"],
        "output": ["for", "form", "name"],
        "progress": ["value", "max"],
        "meter": ["value", "min", "max", "low", "high", "optimum"],
        "details": ["open"],
        "summary": [],
        "command": ["type", "label", "icon", "disabled", "checked", "radiogroup", "command"],
        "menu": ["type", "label"],
        "dialog": ["open"]
    };

    var allElements = Object.keys(attributeMap);

    function hasType(token, type) {
        var tokenTypes = token.type.split('.');
        return type.split('.').every(function (type) {
            return (tokenTypes.indexOf(type) !== -1);
        });
    }

    function findTagName(session, pos) {
        var iterator = new TokenIterator(session, pos.row, pos.column);
        var token = iterator.getCurrentToken();
        if (!token || !hasType(token, 'tag') && !(hasType(token, 'text') && token.value.match('/'))) {
            do {
                token = iterator.stepBackward();
            } while (token && (hasType(token, 'string') || hasType(token, 'operator') || hasType(token, 'attribute-name') || hasType(token, 'text')));
        }
        if (token && hasType(token, 'tag-name') && !iterator.stepBackward().value.match('/'))
            return token.value;
    }

    var HtmlCompletions = function () {

    };

    (function () {

        this.getCompletions = function (state, session, pos, prefix) {
            var token = session.getTokenAt(pos.row, pos.column);

            if (!token)
                return [];
            if (hasType(token, "tag-name") || (token.value == '<' && hasType(token, "text")))
                return this.getTagCompletions(state, session, pos, prefix);
            if (hasType(token, 'text') || hasType(token, 'attribute-name'))
                return this.getAttributeCompetions(state, session, pos, prefix);

            return [];
        };

        this.getTagCompletions = function (state, session, pos, prefix) {
            var elements = allElements;
            if (prefix) {
                elements = elements.filter(function (element) {
                    return element.indexOf(prefix) === 0;
                });
            }
            return elements.map(function (element) {
                return {
                    value: element,
                    meta: "tag"
                };
            });
        };

        this.getAttributeCompetions = function (state, session, pos, prefix) {
            var tagName = findTagName(session, pos);
            if (!tagName)
                return [];
            var attributes = globalAttributes;
            if (tagName in attributeMap) {
                attributes = attributes.concat(attributeMap[tagName]);
            }
            if (prefix) {
                attributes = attributes.filter(function (attribute) {
                    return attribute.indexOf(prefix) === 0;
                });
            }
            return attributes.map(function (attribute) {
                return {
                    caption: attribute,
                    snippet: attribute + '="$0"',
                    meta: "attribute"
                };
            });
        };

    }).call(HtmlCompletions.prototype);

    exports.HtmlCompletions = HtmlCompletions;
});
