/* ***** 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 ***** */

if (typeof process !== "undefined") {
    require("amd-loader");
}

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

var EditSession = require("../edit_session").EditSession;
var Tokenizer = require("../tokenizer").Tokenizer;
var JavaScriptMode = require("./javascript").Mode;
var assert = require("../test/assertions");

module.exports = {
    setUp : function() {    
        this.mode = new JavaScriptMode();
    },

    "test: getTokenizer() (smoke test)" : function() {
        var tokenizer = this.mode.getTokenizer();

        assert.ok(tokenizer instanceof Tokenizer);

        var tokens = tokenizer.getLineTokens("'juhu'", "start").tokens;
        assert.equal("string", tokens[0].type);
    },

    "test: toggle comment lines should prepend '//' to each line" : function() {
        var session = new EditSession(["  abc", "cde", "fg"]);
        session.setTabSize(1);

        this.mode.toggleCommentLines("start", session, 0, 1);
        assert.equal(["//   abc", "// cde", "fg"].join("\n"), session.toString());
    },

    "test: toggle comment on commented lines should remove leading '//' chars" : function() {
        var session = new EditSession(["//  abc", "//cde", "fg"]);
        session.setTabSize(1);

        this.mode.toggleCommentLines("start", session, 0, 1);
        assert.equal([" abc", "cde", "fg"].join("\n"), session.toString());
    },
    
    "test: toggle comment on all empty lines" : function() {
        var session = new EditSession(["  ", " ", "  "]);
        session.setTabSize(1);

        this.mode.toggleCommentLines("start", session, 0, 1);
        assert.equal([" //  ", " // ", "  "].join("\n"), session.toString());
    },
    
    "test: toggle comment with empty lines" : function() {
        var session = new EditSession([
            "        abc",
            "",
            "    cde", 
            "    fg"]);
        
        var initial = session.toString();
        this.mode.toggleCommentLines("start", session, 0, 3);
        assert.equal([
            "    //     abc",
            "",
            "    // cde", 
            "    // fg"].join("\n"),
            session.toString()
        );
        this.mode.toggleCommentLines("start", session, 0, 3);
        assert.equal(initial, session.toString());
    },

    "test: toggle comment lines twice should return the original text" : function() {
        var session = new EditSession(["  abc", "cde", "fg"]);

        this.mode.toggleCommentLines("start", session, 0, 2);
        this.mode.toggleCommentLines("start", session, 0, 2);
        assert.equal(["  abc", "cde", "fg"].join("\n"), session.toString());
    },

    "test: toggle comment on multiple lines with one commented line prepend '//' to each line" : function() {
        var session = new EditSession(["  //  abc", "  //cde", "    fg"]);
        session.setTabSize(1);
        this.mode.toggleCommentLines("start", session, 0, 2);
        assert.equal(["  // //  abc", "  // //cde", "  //   fg"].join("\n"), session.toString());
    },

    "test: toggle comment on a comment line with leading white space": function() {
        var session = new EditSession(["//cde", "  //fg"]);

        this.mode.toggleCommentLines("start", session, 0, 1);
        assert.equal(["cde", "  fg"].join("\n"), session.toString());
    },

    "test: toggle comment lines should take tabsize into account" : function() {
        var session = new EditSession(["  //  abc", "  // cde", "//    fg"]);
        session.setTabSize(2);
        this.mode.toggleCommentLines("start", session, 0, 2);
        assert.equal(["    abc", "  cde", "    fg"].join("\n"), session.toString());
        session.setTabSize(4);
        this.mode.toggleCommentLines("start", session, 0, 2);
        assert.equal(["//     abc", "//   cde", "//     fg"].join("\n"), session.toString());
        this.mode.toggleCommentLines("start", session, 0, 2);
        assert.equal(["    abc", "  cde", "    fg"].join("\n"), session.toString());
        
        session.insert({row: 0, column: 0}, " ");
        this.mode.toggleCommentLines("start", session, 0, 2);
        assert.equal(["//      abc", "//   cde", "//     fg"].join("\n"), session.toString());        
    },
    //there doesn't seem to be any way to make this work
    "!test: togglecomment on line with one space" : function() {
        var session = new EditSession([" abc", "  // cde", "//    fg"]);
        var initialValue = session + "";
        session.setTabSize(4);
        this.mode.toggleCommentLines("start", session, 0, 0);
        this.mode.toggleCommentLines("start", session, 0, 0);
        assert.equal(initialValue, session.toString());
    },

    "test: auto indent after opening brace" : function() {
        assert.equal("  ", this.mode.getNextLineIndent("start", "if () {", "  "));
    },
    
    "test: auto indent after case" : function() {
        assert.equal("  ", this.mode.getNextLineIndent("start", "case 'juhu':", "  "));
    },

    "test: no auto indent in object literal" : function() {
        assert.equal("", this.mode.getNextLineIndent("start", "{ 'juhu':", "  "));
    },

    "test: no auto indent after opening brace in multi line comment" : function() {
        assert.equal("", this.mode.getNextLineIndent("start", "/*if () {", "  "));
        assert.equal("  ", this.mode.getNextLineIndent("comment", "  abcd", "  "));
    },

    "test: no auto indent after opening brace in single line comment" : function() {
        assert.equal("", this.mode.getNextLineIndent("start", "//if () {", "  "));
        assert.equal("  ", this.mode.getNextLineIndent("start", "  //if () {", "  "));
    },

    "test: no auto indent should add to existing indent" : function() {
        assert.equal("      ", this.mode.getNextLineIndent("start", "    if () {", "  "));
        assert.equal("    ", this.mode.getNextLineIndent("start", "    cde", "  "));
        assert.equal("    ", this.mode.getNextLineIndent("start", "function foo(items) {", "    "));
    },

    "test: special indent in doc comments" : function() {
        assert.equal(" * ", this.mode.getNextLineIndent("doc-start", "/**", " "));
        assert.equal("   * ", this.mode.getNextLineIndent("doc-start", "  /**", " "));
        assert.equal(" * ", this.mode.getNextLineIndent("doc-start", " *", " "));
        assert.equal("    * ", this.mode.getNextLineIndent("doc-start", "    *", " "));
        assert.equal("  ", this.mode.getNextLineIndent("doc-start", "  abc", " "));
    },

    "test: no indent after doc comments" : function() {
        assert.equal("", this.mode.getNextLineIndent("doc-start", "   */", "  "));
    },

    "test: trigger outdent if line is space and new text starts with closing brace" : function() {
        assert.ok(this.mode.checkOutdent("start", "   ", " }"));
        assert.ok(!this.mode.checkOutdent("start", " a  ", " }"));
        assert.ok(!this.mode.checkOutdent("start", "", "}"));
        assert.ok(!this.mode.checkOutdent("start", "   ", "a }"));
        assert.ok(!this.mode.checkOutdent("start", "   }", "}"));
    },

    "test: auto outdent should indent the line with the same indent as the line with the matching opening brace" : function() {
        var session = new EditSession(["  function foo() {", "    bla", "    }"], new JavaScriptMode());
        this.mode.autoOutdent("start", session, 2);
        assert.equal("  }", session.getLine(2));
    },

    "test: no auto outdent if no matching brace is found" : function() {
        var session = new EditSession(["  function foo()", "    bla", "    }"]);
        this.mode.autoOutdent("start", session, 2);
        assert.equal("    }", session.getLine(2));
    }
};

});

if (typeof module !== "undefined" && module === require.main) {
    require("asyncjs").test.testcase(module.exports).exec();
}
