/* ***** 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 oop = require("./lib/oop");
    var Range = require("./range").Range;
    var Search = require("./search").Search;
    var EditSession = require("./edit_session").EditSession;
    var SearchHighlight = require("./search_highlight").SearchHighlight;

    /**
     * @class Occur
     *
     * Finds all lines matching a search term in the current [[Document
     * `Document`]] and displays them instead of the original `Document`. Keeps
     * track of the mapping between the occur doc and the original doc.
     *
     **/


    /**
     * Creates a new `Occur` object.
     *
     * @constructor
     **/
    function Occur() {
    }

    oop.inherits(Occur, Search);

    (function () {

        /**
         * Enables occur mode. expects that `options.needle` is a search term.
         * This search term is used to filter out all the lines that include it
         * and these are then used as the content of a new [[Document
         * `Document`]]. The current cursor position of editor will be translated
         * so that the cursor is on the matching row/column as it was before.
         * @param {Editor} editor
         * @param {Object} options options.needle should be a String
         * @return {Boolean} Whether occur activation was successful
         *
         **/
        this.enter = function (editor, options) {
            if (!options.needle) return false;
            var pos = editor.getCursorPosition();
            this.displayOccurContent(editor, options);
            var translatedPos = this.originalToOccurPosition(editor.session, pos);
            editor.moveCursorToPosition(translatedPos);
            return true;
        }

        /**
         * Disables occur mode. Resets the [[Sessions `EditSession`]] [[Document
         * `Document`]] back to the original doc. If options.translatePosition is
         * truthy also maps the [[Editors `Editor`]] cursor position accordingly.
         * @param {Editor} editor
         * @param {Object} options options.translatePosition
         * @return {Boolean} Whether occur deactivation was successful
         *
         **/
        this.exit = function (editor, options) {
            var pos = options.translatePosition && editor.getCursorPosition();
            var translatedPos = pos && this.occurToOriginalPosition(editor.session, pos);
            this.displayOriginalContent(editor);
            if (translatedPos)
                editor.moveCursorToPosition(translatedPos);
            return true;
        }

        this.highlight = function (sess, regexp) {
            var hl = sess.$occurHighlight = sess.$occurHighlight || sess.addDynamicMarker(
                    new SearchHighlight(null, "ace_occur-highlight", "text"));
            hl.setRegexp(regexp);
            sess._emit("changeBackMarker"); // force highlight layer redraw
        }

        this.displayOccurContent = function (editor, options) {
            // this.setSession(session || new EditSession(""))
            this.$originalSession = editor.session;
            var found = this.matchingLines(editor.session, options);
            var lines = found.map(function (foundLine) {
                return foundLine.content;
            });
            var occurSession = new EditSession(lines.join('\n'));
            occurSession.$occur = this;
            occurSession.$occurMatchingLines = found;
            editor.setSession(occurSession);
            this.$useEmacsStyleLineStart = this.$originalSession.$useEmacsStyleLineStart;
            occurSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart;
            this.highlight(occurSession, options.re);
            occurSession._emit('changeBackMarker');
        }

        this.displayOriginalContent = function (editor) {
            editor.setSession(this.$originalSession);
            this.$originalSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart;
        }

        /**
         * Translates the position from the original document to the occur lines in
         * the document or the beginning if the doc {row: 0, column: 0} if not
         * found.
         * @param {EditSession} session The occur session
         * @param {Object} pos The position in the original document
         * @return {Object} position in occur doc
         **/
        this.originalToOccurPosition = function (session, pos) {
            var lines = session.$occurMatchingLines;
            var nullPos = {row: 0, column: 0};
            if (!lines) return nullPos;
            for (var i = 0; i < lines.length; i++) {
                if (lines[i].row === pos.row)
                    return {row: i, column: pos.column};
            }
            return nullPos;
        }

        /**
         * Translates the position from the occur document to the original document
         * or `pos` if not found.
         * @param {EditSession} session The occur session
         * @param {Object} pos The position in the occur session document
         * @return {Object} position
         **/
        this.occurToOriginalPosition = function (session, pos) {
            var lines = session.$occurMatchingLines;
            if (!lines || !lines[pos.row])
                return pos;
            return {row: lines[pos.row].row, column: pos.column};
        }

        this.matchingLines = function (session, options) {
            options = oop.mixin({}, options);
            if (!session || !options.needle) return [];
            var search = new Search();
            search.set(options);
            return search.findAll(session).reduce(function (lines, range) {
                var row = range.start.row;
                var last = lines[lines.length - 1];
                return last && last.row === row ?
                    lines :
                    lines.concat({row: row, content: session.getLine(row)});
            }, []);
        }

    }).call(Occur.prototype);

    var dom = require('./lib/dom');
    dom.importCssString(".ace_occur-highlight {\n\
    border-radius: 4px;\n\
    background-color: rgba(87, 255, 8, 0.25);\n\
    position: absolute;\n\
    z-index: 4;\n\
    -moz-box-sizing: border-box;\n\
    -webkit-box-sizing: border-box;\n\
    box-sizing: border-box;\n\
    box-shadow: 0 0 4px rgb(91, 255, 50);\n\
}\n\
.ace_dark .ace_occur-highlight {\n\
    background-color: rgb(80, 140, 85);\n\
    box-shadow: 0 0 4px rgb(60, 120, 70);\n\
}\n", "incremental-occur-highlighting");

    exports.Occur = Occur;

});
