/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2012, 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 Renderer = require("../virtual_renderer").VirtualRenderer;
    var Editor = require("../editor").Editor;
    var Range = require("../range").Range;
    var event = require("../lib/event");
    var lang = require("../lib/lang");
    var dom = require("../lib/dom");

    var $singleLineEditor = function (el) {
        var renderer = new Renderer(el);

        renderer.$maxLines = 4;

        var editor = new Editor(renderer);

        editor.setHighlightActiveLine(false);
        editor.setShowPrintMargin(false);
        editor.renderer.setShowGutter(false);
        editor.renderer.setHighlightGutterLine(false);

        editor.$mouseHandler.$focusWaitTimout = 0;
        editor.$highlightTagPending = true;

        return editor;
    };

    var AcePopup = function (parentNode) {
        var el = dom.createElement("div");
        var popup = new $singleLineEditor(el);

        if (parentNode)
            parentNode.appendChild(el);
        el.style.display = "none";
        popup.renderer.content.style.cursor = "default";
        popup.renderer.setStyle("ace_autocomplete");

        popup.setOption("displayIndentGuides", false);
        popup.setOption("dragDelay", 150);

        var noop = function () {
        };

        popup.focus = noop;
        popup.$isFocused = true;

        popup.renderer.$cursorLayer.restartTimer = noop;
        popup.renderer.$cursorLayer.element.style.opacity = 0;

        popup.renderer.$maxLines = 8;
        popup.renderer.$keepTextAreaAtCursor = false;

        popup.setHighlightActiveLine(false);
        // set default highlight color
        popup.session.highlight("");
        popup.session.$searchHighlight.clazz = "ace_highlight-marker";

        popup.on("mousedown", function (e) {
            var pos = e.getDocumentPosition();
            popup.selection.moveToPosition(pos);
            selectionMarker.start.row = selectionMarker.end.row = pos.row;
            e.stop();
        });

        var lastMouseEvent;
        var hoverMarker = new Range(-1, 0, -1, Infinity);
        var selectionMarker = new Range(-1, 0, -1, Infinity);
        selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
        popup.setSelectOnHover = function (val) {
            if (!val) {
                hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
            } else if (hoverMarker.id) {
                popup.session.removeMarker(hoverMarker.id);
                hoverMarker.id = null;
            }
        };
        popup.setSelectOnHover(false);
        popup.on("mousemove", function (e) {
            if (!lastMouseEvent) {
                lastMouseEvent = e;
                return;
            }
            if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
                return;
            }
            lastMouseEvent = e;
            lastMouseEvent.scrollTop = popup.renderer.scrollTop;
            var row = lastMouseEvent.getDocumentPosition().row;
            if (hoverMarker.start.row != row) {
                if (!hoverMarker.id)
                    popup.setRow(row);
                setHoverMarker(row);
            }
        });
        popup.renderer.on("beforeRender", function () {
            if (lastMouseEvent && hoverMarker.start.row != -1) {
                lastMouseEvent.$pos = null;
                var row = lastMouseEvent.getDocumentPosition().row;
                if (!hoverMarker.id)
                    popup.setRow(row);
                setHoverMarker(row, true);
            }
        });
        popup.renderer.on("afterRender", function () {
            var row = popup.getRow();
            var t = popup.renderer.$textLayer;
            var selected = t.element.childNodes[row - t.config.firstRow];
            if (selected == t.selectedNode)
                return;
            if (t.selectedNode)
                dom.removeCssClass(t.selectedNode, "ace_selected");
            t.selectedNode = selected;
            if (selected)
                dom.addCssClass(selected, "ace_selected");
        });
        var hideHoverMarker = function () {
            setHoverMarker(-1)
        };
        var setHoverMarker = function (row, suppressRedraw) {
            if (row !== hoverMarker.start.row) {
                hoverMarker.start.row = hoverMarker.end.row = row;
                if (!suppressRedraw)
                    popup.session._emit("changeBackMarker");
                popup._emit("changeHoverMarker");
            }
        };
        popup.getHoveredRow = function () {
            return hoverMarker.start.row;
        };

        event.addListener(popup.container, "mouseout", hideHoverMarker);
        popup.on("hide", hideHoverMarker);
        popup.on("changeSelection", hideHoverMarker);

        popup.session.doc.getLength = function () {
            return popup.data.length;
        };
        popup.session.doc.getLine = function (i) {
            var data = popup.data[i];
            if (typeof data == "string")
                return data;
            return (data && data.value) || "";
        };

        var bgTokenizer = popup.session.bgTokenizer;
        bgTokenizer.$tokenizeRow = function (row) {
            var data = popup.data[row];
            var tokens = [];
            if (!data)
                return tokens;
            if (typeof data == "string")
                data = {value: data};
            if (!data.caption)
                data.caption = data.value || data.name;

            var last = -1;
            var flag, c;
            for (var i = 0; i < data.caption.length; i++) {
                c = data.caption[i];
                flag = data.matchMask & (1 << i) ? 1 : 0;
                if (last !== flag) {
                    tokens.push({type: data.className || "" + ( flag ? "completion-highlight" : ""), value: c});
                    last = flag;
                } else {
                    tokens[tokens.length - 1].value += c;
                }
            }

            if (data.meta) {
                var maxW = popup.renderer.$size.scrollerWidth / popup.renderer.layerConfig.characterWidth;
                var metaData = data.meta;
                if (metaData.length + data.caption.length > maxW - 2) {
                    // trim meta to fit this popup and add ellipsis
                    metaData = metaData.substr(0, maxW - data.caption.length - 3) + "\u2026"
                }
                tokens.push({type: "rightAlignedText", value: metaData});
            }
            return tokens;
        };
        bgTokenizer.$updateOnChange = noop;
        bgTokenizer.start = noop;

        popup.session.$computeWidth = function () {
            return this.screenWidth = 0;
        };

        popup.$blockScrolling = Infinity;

        // public
        popup.isOpen = false;
        popup.isTopdown = false;

        popup.data = [];
        popup.setData = function (list) {
            popup.setValue(lang.stringRepeat("\n", list.length), -1);
            popup.data = list || [];
            popup.setRow(0);
        };
        popup.getData = function (row) {
            return popup.data[row];
        };

        popup.getRow = function () {
            return selectionMarker.start.row;
        };
        popup.setRow = function (line) {
            line = Math.max(0, Math.min(this.data.length, line));
            if (selectionMarker.start.row != line) {
                popup.selection.clearSelection();
                selectionMarker.start.row = selectionMarker.end.row = line || 0;
                popup.session._emit("changeBackMarker");
                popup.moveCursorTo(line || 0, 0);
                if (popup.isOpen)
                    popup._signal("select");
            }
        };

        popup.on("changeSelection", function () {
            if (popup.isOpen)
                popup.setRow(popup.selection.lead.row);
            popup.renderer.scrollCursorIntoView();
        });

        popup.hide = function () {
            this.container.style.display = "none";
            this._signal("hide");
            popup.isOpen = false;
        };
        popup.show = function (pos, lineHeight, topdownOnly) {
            var el = this.container;
            var screenHeight = window.innerHeight;
            var screenWidth = window.innerWidth;
            var renderer = this.renderer;
            // var maxLines = Math.min(renderer.$maxLines, this.session.getLength());
            var maxH = renderer.$maxLines * lineHeight * 1.4;
            var top = pos.top + this.$borderSize;
            var allowTopdown = top > screenHeight / 2 && !topdownOnly;
            if (allowTopdown && top + lineHeight + maxH > screenHeight) {
                renderer.$maxPixelHeight = top - 2 * this.$borderSize;
                el.style.top = "";
                el.style.bottom = screenHeight - top + "px";
                popup.isTopdown = false;
            } else {
                top += lineHeight;
                renderer.$maxPixelHeight = screenHeight - top - 0.2 * lineHeight;
                el.style.top = top + "px";
                el.style.bottom = "";
                popup.isTopdown = true;
            }

            el.style.display = "";
            this.renderer.$textLayer.checkForSizeChanges();

            var left = pos.left;
            if (left + el.offsetWidth > screenWidth)
                left = screenWidth - el.offsetWidth;

            el.style.left = left + "px";

            this._signal("show");
            lastMouseEvent = null;
            popup.isOpen = true;
        };

        popup.getTextLeftOffset = function () {
            return this.$borderSize + this.renderer.$padding + this.$imageSize;
        };

        popup.$imageSize = 0;
        popup.$borderSize = 1;

        return popup;
    };

    dom.importCssString("\
.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
    background-color: #CAD6FA;\
    z-index: 1;\
}\
.ace_editor.ace_autocomplete .ace_line-hover {\
    border: 1px solid #abbffe;\
    margin-top: -1px;\
    background: rgba(233,233,253,0.4);\
}\
.ace_editor.ace_autocomplete .ace_line-hover {\
    position: absolute;\
    z-index: 2;\
}\
.ace_editor.ace_autocomplete .ace_scroller {\
   background: none;\
   border: none;\
   box-shadow: none;\
}\
.ace_rightAlignedText {\
    color: gray;\
    display: inline-block;\
    position: absolute;\
    right: 4px;\
    text-align: right;\
    z-index: -1;\
}\
.ace_editor.ace_autocomplete .ace_completion-highlight{\
    color: #000;\
    text-shadow: 0 0 0.01em;\
}\
.ace_editor.ace_autocomplete {\
    width: 280px;\
    z-index: 200000;\
    background: #fbfbfb;\
    color: #444;\
    border: 1px lightgray solid;\
    position: fixed;\
    box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
    line-height: 1.4;\
}");

    exports.AcePopup = AcePopup;

});
