function msg( str ) {
  var m = $('msg');
  if ( m ) {
    m.innerHTML = m.innerHTML + ", " + str;
  }
}

//-------------------- mTextSuggest.js

TextSuggest = Class.create();

TextSuggest.prototype = {

  initialize: function(anId, postProcessor, url, options) {
    this.id = anId;
    this.postFunction = postProcessor;
    var browser = navigator.userAgent.toLowerCase();
    this.isIE = browser.indexOf("msie") != -1;
    this.isOpera = browser.indexOf("opera")!= -1;
    this.textInput = $(this.id);
    this.suggestions = [];
    this.setOptions(options);
    this.initAjax(url);
    this.lastRequestString = "";
    this.safariRequestString = "";
    this.safariCaretOffset = 0;
    this.injectSuggestBehavior();
  },

  initAjax: function(url) {
    ajaxEngine.registerRequest( this.id + '_request', url );
    ajaxEngine.registerAjaxObject( this.id + '_updater', this );
  },

  setOptions: function(options) {
    this.options = {
      suggestDivClassName : 'suggestDiv',
      suggestionClassName : 'suggestion',
      matchClassName : 'match',
      matchTextWidth : true,
      selectionColor : '#FFE770', /* AEC3C3 */
      matchAnywhere : false,
      ignoreCase : false,
      listHeight : 250
    }.extend(options || {});
  },

  injectSuggestBehavior: function() {
    if ( this.isIE ) {
      this.textInput.autocomplete = "off";
    }
	var body = document.getElementsByTagName('body')[0];
	
    var keyEventHandler = new TextSuggestKeyHandler(this);
    new Insertion.Bottom( body,
        '<input type="text" id="'+this.id+'_preventtsubmit'+'" style="display:none"/>' );
    new Insertion.Bottom( body,
        '<input type="hidden" name="'+this.id+'_hidden'+'" id="'+this.id+'_hidden'+'"/>' );
    new Insertion.Bottom( body,
        '<iframe style="display:none; left:0px; position:absolute; '
            +'top:0px" src="about:blank" frameborder="0" id="'+this.id+'_iframe" scrolling="no"></iframe>' );

    //
    this.createSuggestionsDiv();
  },

  handleTextInput: function() {
    var previousRequest    = this.lastRequestString;
    this.lastRequestString = this.textInput.value;

    //
    if ( this.lastRequestString == "" ) {
      this.hideSuggestions();
    }
    else if ( this.lastRequestString != previousRequest ) {
      this.sendRequestForSuggestions();
    }
  },

  moveSelectionUp: function() {
    if ( this.selectedIndex > 0 ) {
      this.updateSelection(this.selectedIndex - 1);
    }
  },

  moveSelectionDown: function() {
    if ( this.selectedIndex < (this.suggestions.length - 1)  ) {
      this.updateSelection(this.selectedIndex + 1);
    }
  },

  updateSelection: function(n) {

    //
    var span = $( this.id + "_" + this.selectedIndex );
    if ( span ){
      span.style.backgroundColor = "";
    }

    //
    this.selectedIndex = n;
    span = $( this.id + "_" + this.selectedIndex );
    span.style.backgroundColor = this.options.selectionColor;

    try{
      var top = span.offsetTop - span.offsetParent.scrollTop;
msg( "top=" + top );
      if ( top < 0 ) {
        span.offsetParent.scrollTop = span.offsetTop;
      }
      if ( ( top + span.offsetHeight ) > this.options.listHeight ) {
        span.offsetParent.scrollTop = span.offsetTop - ( this.options.listHeight - span.offsetHeight );
      }
    } catch (e) {
msg( "error=" + e );
    }
  },

  sendRequestForSuggestions: function() {

    if ( this.handlingRequest ) {
msg( "pending" );
      this.pendingRequest = true;
      return;
    }

    this.handlingRequest = true;
    this.callRicoAjaxEngine();
  },

  callRicoAjaxEngine: function() {
    var callParms = [];
    callParms.push( this.id + '_request');
    callParms.push( 'id=' + this.id);
    callParms.push( 'query=' + urlEncode( this.lastRequestString ));
msg('{query=' + urlEncode( this.lastRequestString )+'}'); 
    callParms.push( 'match_anywhere=' + this.options.matchAnywhere);
    callParms.push( 'ignore_case=' + this.options.ignoreCase);

    var additionalParms = this.options.requestParameters || [];
    for( var i=0 ; i < additionalParms.length ; i++ ) {
      callParms.push(additionalParms[i]);
    }

    ajaxEngine.sendRequest.apply( ajaxEngine, callParms );
msg('request sent');
  },

  ajaxUpdate: function( ajaxResponse ) {
msg("got response");
    this.createSuggestions( ajaxResponse );

    if ( this.suggestions.length == 0 ) {
      this.hideSuggestions();
      $( this.id + "_hidden" ).value = "";
    }
    else {
      this.updateSuggestionsDiv();
      this.showSuggestions();
      this.updateSelection(0);
    }

    this.handlingRequest = false;

    if ( this.pendingRequest ) {
      this.pendingRequest    = false;
      this.lastRequestString = this.textInput.value;
      this.sendRequestForSuggestions();
    }
  },

  createSuggestions: function(ajaxResponse) {
    this.suggestions = [];
    var entries = ajaxResponse.getElementsByTagName('entry');
    for ( var i = 0 ; i < entries.length ; i++ ) {
      var strText = this.getElementContent(entries[i].getElementsByTagName('text')[0]);
      var strValue = this.getElementContent(entries[i].getElementsByTagName('value')[0]);
      this.suggestions.push( { text: strText, value: strValue } );
    }
  },

  setInputFromSelection: function() {
    if ( this.suggestions && this.suggestions.length > 0 ) {
      var hiddenInput = $( this.id + "_hidden" );
      var suggestion = this.suggestions[ this.selectedIndex ];

      this.textInput.value = suggestion.text;
      hiddenInput.value = suggestion.value;
    }
    this.hideSuggestions();
  },

  showSuggestions: function() {
    var divStyle = this.suggestionsDiv.style;
    if ( divStyle.display == '' )
        return;
    this.positionSuggestionsDiv();
    divStyle.display = '';
    this.suggestionsIframe.style.display = '';
  },

  positionSuggestionsDiv: function() {

    //
    var textPos = RicoUtil.toDocumentPosition(this.textInput);

    //
    var divStyle = this.suggestionsDiv.style;
    divStyle.top  = (textPos.y + this.textInput.offsetHeight) + "px";
    divStyle.left = textPos.x + "px";
    if ( this.options.matchTextWidth ) {
      divStyle.width = (this.textInput.offsetWidth- this.padding()) + "px";
    }
    divStyle.height = this.options.listHeight;

    //
    var iframeStyle = this.suggestionsIframe.style;
    iframeStyle.top = divStyle.top;
    iframeStyle.left = divStyle.left;
    iframeStyle.width = divStyle.width;
    iframeStyle.height = this.options.listHeight;
  },

  padding: function() {
    try{
      var styleFunc = RicoUtil.getElementsComputedStyle;
      var lPad = styleFunc( this.suggestionsDiv, "paddingLeft", "padding-left" );
      var rPad = styleFunc( this.suggestionsDiv, "paddingRight", "padding-right" );
      var lBorder = styleFunc( this.suggestionsDiv, "borderLeftWidth", "border-left-width" );
      var rBorder = styleFunc( this.suggestionsDiv, "borderRightWidth", "border-right-width" );

      lPad = isNaN(lPad) ? 0 : lPad;
      rPad = isNaN(rPad) ? 0 : rPad;
      lBorder = isNaN(lBorder) ? 0 : lBorder;
      rBorder = isNaN(rBorder) ? 0 : rBorder;

      return parseInt(lPad) + parseInt(rPad) + parseInt(lBorder) + parseInt(rBorder);
    } catch (e) {
      return 0;
    }
  },

  hideSuggestions: function() {
    this.suggestionsDiv.style.display = 'none';
    this.suggestionsIframe.style.display = 'none';
  },

  dummyFunction: function(e) {
msg( "dummy["+e+"]" );
    return false;
  },

  createSuggestionsDiv: function() {
    this.suggestionsDiv = document.createElement("div");
    this.suggestionsDiv.className = this.options.suggestDivClassName;

    var divStyle = this.suggestionsDiv.style;
    divStyle.position = 'absolute';
    divStyle.zIndex   = 200;
    divStyle.display  = 'none';
    divStyle.overflow = 'auto';
    divStyle.height = this.options.listHeight + 'px';

    //
    var body = document.getElementsByTagName('body')[0];
    body.appendChild(this.suggestionsDiv);
    //this.textInput.parentNode.appendChild(this.suggestionsDiv);

    //
    this.suggestionsIframe = document.getElementById(this.id + '_iframe');
    this.suggestionsIframe.style.zIndex = 100;

    //
    this.suggestionsDiv.onclick = this.dummyFunction.bindAsEventListener(this);
    this.suggestionsIframe.onclick = this.dummyFunction.bindAsEventListener(this);
  },

  updateSuggestionsDiv: function() {
    this.suggestionsDiv.innerHTML = "";
    var suggestLines = this.createSuggestionSpans();
    for ( var i = 0 ; i < suggestLines.length ; i++ ) {
      this.suggestionsDiv.appendChild(suggestLines[i]);
    }
    this.suggestionsIframe.style.height = this.suggestionsDiv.style.height;
  },

  createSuggestionSpans: function() {
    var regExpFlags = "";
    if ( this.options.ignoreCase )
        regExpFlags = 'i';
    var startRegExp = "^";
    if ( this.options.matchAnywhere )
        startRegExp = '';

    var regExp  = new RegExp( startRegExp + this.lastRequestString, regExpFlags );

    var suggestionSpans = [];
    for ( var i = 0 ; i < this.suggestions.length ; i++ ) {
      suggestionSpans.push( this.createSuggestionSpan( i, regExp ) );
    }

    return suggestionSpans;
  },

  createSuggestionSpan: function( n, regExp ) {
    var suggestion = this.suggestions[n];

    var suggestionSpan = document.createElement("span");
    suggestionSpan.className = this.options.suggestionClassName;
    suggestionSpan.style.width = '100%';
    suggestionSpan.style.display = 'block';
    suggestionSpan.id = this.id + "_" + n;
    suggestionSpan.onmouseover = this.mouseoverHandler.bindAsEventListener(this);
    //suggestionSpan.onclick = this.itemClickHandler.bindAsEventListener(this);
    suggestionSpan.onmousedown = this.itemClickHandler.bindAsEventListener(this);

    var textValues = this.splitTextValues( suggestion.text,
        this.lastRequestString.length,
        regExp );

    var textMatchSpan = document.createElement("span");
    textMatchSpan.id = this.id + "_match_" + n;
    textMatchSpan.className = this.options.matchClassName;
    textMatchSpan.onmouseover = this.mouseoverHandler.bindAsEventListener(this);
    //textMatchSpan.onclick = this.itemClickHandler.bindAsEventListener(this);
    textMatchSpan.onmousedown = this.itemClickHandler.bindAsEventListener(this);

    textMatchSpan.appendChild( document.createTextNode(textValues.mid) );

    suggestionSpan.appendChild( document.createTextNode( textValues.start ) );
    suggestionSpan.appendChild( textMatchSpan );
    suggestionSpan.appendChild( document.createTextNode( textValues.end ) );

    return suggestionSpan;
  },

  mouseoverHandler: function(e) {
msg( "mouseOver=" + ( e.srcElement ? e.srcElement.id : e.target.id ) );
    var src = e.srcElement ? e.srcElement : e.target;
    var index = parseInt( src.id.substring( src.id.lastIndexOf( '_' ) + 1 ) );
    this.updateSelection( index );
  },

  itemClickHandler: function(e) {
    try {

      //
      this.mouseoverHandler( e );
      this.hideSuggestions();
      this.textInput.focus();

      //
      this.setInputFromSelection();

      //
      if ( this.postFunction ) {
        this.textInput.blur();
        this.postFunction.call( this,
            this.textInput.value,
            $F( this.id + "_hidden" ) );
	  }
    } catch( ee ) {
msg( "click=" + ee );
    }
  },

  splitTextValues: function( text, len, regExp ) {
    var startPos  = text.search(regExp);
    var matchText = text.substring( startPos, startPos + len );
    var startText = startPos == 0 ? "" : text.substring(0, startPos);
    var endText   = text.substring( startPos + len );
    return { start: startText, mid: matchText, end: endText };
  },

  getElementContent: function(element) {
    return element.firstChild.data;
  }
}

TextSuggestKeyHandler = Class.create();

TextSuggestKeyHandler.prototype = {

  initialize: function( textSuggest ) {
    this.textSuggest = textSuggest;
    this.input = this.textSuggest.textInput;
    this.addKeyHandling();
  },

  addKeyHandling: function() {
    if ( this.input ) {
      this.input.onkeyup = this.keyupHandler.bindAsEventListener(this);
      this.input.onkeydown = this.keydownHandler.bindAsEventListener(this);
      this.input.onblur = this.onblurHandler.bindAsEventListener(this);
      if ( this.isOpera ) {
        this.input.onkeypress = this.keyupHandler.bindAsEventListener(this);
      }
    } else {
      alert( "대상 텍스트 필드가 없습니다." );
    }
  },

  keydownHandler: function(e) {
msg("down ["+e.keyCode+"]");
    var leftArrow   = 37;
    var upArrow   = 38;
    var rightArrow   = 39;
    var downArrow = 40;
    var backspace = 8;
    var deleteKey = 46;

    if ( e.keyCode == upArrow ) {
      this.textSuggest.moveSelectionUp();
      setTimeout( this.moveCaretToEnd.bind(this), 1 );
    }
    else if ( e.keyCode == downArrow ){
      this.textSuggest.moveSelectionDown();
    }
  },

  keyupHandler: function(e) {
msg("up ["+e.keyCode+"]");
    if ( this.input.length == 0 && this.isOpera == false ) {
      this.textSuggest.hideSuggestions();
    }

    if ( this.handledSpecialKeys(e) == false ) {
      this.textSuggest.handleTextInput();
    }
  },

  handledSpecialKeys: function(e) {
    var leftArrow = 37;
    var enterKey = 13;
    var upArrow = 38;
    var downArrow = 40;
    var rightArrow = 39;

    if ( e.keyCode == upArrow || e.keyCode == downArrow ) {
      return true;
    }
    else if ( e.keyCode == enterKey ) {

      //
      this.textSuggest.setInputFromSelection();

      //
      if ( this.textSuggest.postFunction ) {
        this.textSuggest.textInput.blur();
        this.textSuggest.postFunction.call( this.textSuggest,
            this.textSuggest.textInput.value,
            $F( this.textSuggest.id + "_hidden" ) );
      }
      return true;
    }

    return false;
  },

  moveCaretToEnd: function() {
    var pos = this.input.value.length;
    if ( this.input.setSelectionRange ) {
      this.input.setSelectionRange( pos, pos );
    }
    else if( this.input.createTextRange ){
      var m = this.input.createTextRange();
      m.moveStart( 'character', pos );
      m.collapse();
      m.select();
    }
  },

  onblurHandler: function(e) {
    if ( this.textSuggest.suggestionsDiv.style.display == '' ) {
      this.textSuggest.setInputFromSelection();
    }
    this.textSuggest.hideSuggestions();
  }
};
