text: /* ***** 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 TokenIterator = require("../token_iterator").TokenIterator;
  var Range = require("../range").Range;
  
  
  function BracketMatch() {
  
      this.findMatchingBracket = function(position, char) {
          if (position.column == 0) return null;
  
          var charBeforeCursor = char || this.getLine(position.row).charAt(position.column-1);
          if (charBeforeCursor == "") return null;
  
          var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/);
          if (!match)
              return null;
  
          if (match[1])
              return this.$findClosingBracket(match[1], position);
          else
              return this.$findOpeningBracket(match[2], position);
      };
      
      this.getBracketRange = function(pos) {
          var line = this.getLine(pos.row);
          var before = true, range;
  
          var chr = line.charAt(pos.column-1);
          var match = chr && chr.match(/([\(\[\{])|([\)\]\}])/);
          if (!match) {
              chr = line.charAt(pos.column);
              pos = {row: pos.row, column: pos.column + 1};
              match = chr && chr.match(/([\(\[\{])|([\)\]\}])/);
              before = false;
          }
          if (!match)
              return null;
  
          if (match[1]) {
              var bracketPos = this.$findClosingBracket(match[1], pos);
              if (!bracketPos)
                  return null;
              range = Range.fromPoints(pos, bracketPos);
              if (!before) {
                  range.end.column++;
                  range.start.column--;
              }
              range.cursor = range.end;
          } else {
              var bracketPos = this.$findOpeningBracket(match[2], pos);
              if (!bracketPos)
                  return null;
              range = Range.fromPoints(bracketPos, pos);
              if (!before) {
                  range.start.column++;
                  range.end.column--;
              }
              range.cursor = range.start;
          }
          
          return range;
      };
  
      this.$brackets = {
          ")": "(",
          "(": ")",
          "]": "[",
          "[": "]",
          "{": "}",
          "}": "{"
      };
  
      this.$findOpeningBracket = function(bracket, position, typeRe) {
          var openBracket = this.$brackets[bracket];
          var depth = 1;
  
          var iterator = new TokenIterator(this, position.row, position.column);
          var token = iterator.getCurrentToken();
          if (!token)
              token = iterator.stepForward();
          if (!token)
              return;
          
           if (!typeRe){
              typeRe = new RegExp(
                  "(\\.?" +
                  token.type.replace(".", "\\.").replace("rparen", ".paren")
                  + ")+"
              );
          }
          
          // Start searching in token, just before the character at position.column
          var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2;
          var value = token.value;
          
          while (true) {
          
              while (valueIndex >= 0) {
                  var chr = value.charAt(valueIndex);
                  if (chr == openBracket) {
                      depth -= 1;
                      if (depth == 0) {
                          return {row: iterator.getCurrentTokenRow(),
                              column: valueIndex + iterator.getCurrentTokenColumn()};
                      }
                  }
                  else if (chr == bracket) {
                      depth += 1;
                  }
                  valueIndex -= 1;
              }
  
              // Scan backward through the document, looking for the next token
              // whose type matches typeRe
              do {
                  token = iterator.stepBackward();
              } while (token && !typeRe.test(token.type));
  
              if (token == null)
                  break;
                  
              value = token.value;
              valueIndex = value.length - 1;
          }
          
          return null;
      };
  
      this.$findClosingBracket = function(bracket, position, typeRe) {
          var closingBracket = this.$brackets[bracket];
          var depth = 1;
  
          var iterator = new TokenIterator(this, position.row, position.column);
          var token = iterator.getCurrentToken();
          if (!token)
              token = iterator.stepForward();
          if (!token)
              return;
  
          if (!typeRe){
              typeRe = new RegExp(
                  "(\\.?" +
                  token.type.replace(".", "\\.").replace("lparen", ".paren")
                  + ")+"
              );
          }
  
          // Start searching in token, after the character at position.column
          var valueIndex = position.column - iterator.getCurrentTokenColumn();
  
          while (true) {
  
              var value = token.value;
              var valueLength = value.length;
              while (valueIndex < valueLength) {
                  var chr = value.charAt(valueIndex);
                  if (chr == closingBracket) {
                      depth -= 1;
                      if (depth == 0) {
                          return {row: iterator.getCurrentTokenRow(),
                              column: valueIndex + iterator.getCurrentTokenColumn()};
                      }
                  }
                  else if (chr == bracket) {
                      depth += 1;
                  }
                  valueIndex += 1;
              }
  
              // Scan forward through the document, looking for the next token
              // whose type matches typeRe
              do {
                  token = iterator.stepForward();
              } while (token && !typeRe.test(token.type));
  
              if (token == null)
                  break;
  
              valueIndex = 0;
          }
          
          return null;
      };
  }
  exports.BracketMatch = BracketMatch;
  
  });
  