/**
 * *    Json key/value autocomplete for jQuery
 * *    Provides a transparent way to have key/value autocomplete
 * *    Copyright (C) 2008 Ziadin Givan www.CodeAssembly.com
 * *
 * *    This program is free software: you can redistribute it and/or modify
 * *    it under the terms of the GNU Lesser General Public License as published by
 * *    the Free Software Foundation, either version 3 of the License, or
 * *    (at your option) any later version.
 * *
 * *    This program is distributed in the hope that it will be useful,
 * *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 * *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * *    GNU General Public License for more details.
 * *
 * *    You should have received a copy of the GNU Lesser General Public License
 * *    along with this program.  If not, see http://www.gnu.org/licenses/
 * *
 * *    Examples
 * *    $("input#example").autocomplete("autocomplete.php");//using default parameters
 * *    $("input#example").autocomplete("autocomplete.php",{minChars:3,timeout:3000,validSelection:false,parameters:{'myparam':'myvalue'},before : function(input,text) {},after : function(input,text) {}});
 * *    minChars = Minimum characters the input must have for the ajax request to be made
 * *    timeOut = Number of miliseconds passed after user entered text to make the ajax request
 * *    validSelection = If set to true then will invalidate (set to empty) the value field if the text is not selected (or modified) from the list of items.
 * *    parameters = Custom parameters to be passed
 * *    after, before = a function that will be called before/after the ajax request
 * */
jQuery.fn.autocomplete = function(url, settings ) {
  return this.each(

    /*
     * for each matched element
     */
    function() {

      /*
       * textInput
       * The user supplied partial location string is stored in a hidden text field
       * The original field (name=birthplace) is renamed to (name=birthplace_text) as we will be storing the chosen response in the location field.
       */
      var textInput = $(this);
      textInput
	.after('<input type=hidden name="' + textInput.attr("name") + '"/>')
	.attr("name", textInput.attr("name") + "_text");

      /*
       * birthplaceidInput
       * reference as $('#frmHIOrderBirthplace').next().next();
       */
      var birthplaceidInput = $(this)
	.next(); /* birthplaceid */
      birthplaceidInput
	.after(
	  '<input type="hidden" id="frmHIOrderBirthplaceid" name="birthplaceid" />'
	);

      /*
       * birthplacenameInput
       * reference as $('#frmHIOrderBirthplace').next().next().next();
       */
      var birthplacenameInput = $(this)
	.next()  /* birthplaceid */
	.next(); /* birthplacename */
      birthplacenameInput
	.after(
	  '<input type="hidden" id="frmHIOrderBirthplacename" name="birthplacename" />'
	);

      /*
       * valueInput
       * The autosuggest response list will be stored in a <ul></ul> list
       */
      var valueInput = $(this)
	.next()  /* birthplaceid */
	.next()  /* birthplacename */
	.next(); /* value */
      valueInput
	.after(
	  '<ul class="autocomplete"></ul>'
	);

      /*
       * This defines where the drop down list is displayed
       */
      var list = valueInput
	.next() /* ephemeral list */
	.css({
	  width: textInput.width()+110 /* additional offset as a trial to prevent wrapping */
	});

      /*
       * itemids
       * cache the acsatlas id for placename cross reference
       */
      var itemids = new Array();

      /*
       * itemplaces
       * cache the placenames
       */
      var itemplaces = new Array();

      var oldText = '';
      var typingTimeout;
      var size = 0;
      var selected = 0;

      /*
       * default settings (JSON format)
       * can be overridden
       */
      settings = jQuery.extend( {
	/*
	 * minChars
	 * minimum number of characters to collect before calling server
	 */
	minChars : 2,
	/*
	 * timeout
	 * delay before sending selection to server (ms)
	 */
	timeout: 250,
	/*
	 * after
	 * function to be called after selection is sent to the server
	 */
	after : null, /* function(){ alert( textInput.val() ); }, */
	/*
	 * before
	 * function to be called before selection is sent to the server
	 */
	before : null,
	/*
	 * validSelection
	 * (true) invalidate (set to zero) partial string if not selected
	 * (false) leave partial string as it is (uncleared)
	 */
	validSelection : true,
	/*
	 * parameters
	 * custom parameters sent to the server
	 */
	parameters : {
	  'inputName' : valueInput.attr('name'),
	  'inputId' : textInput.attr('id')
	}
      } , settings);

      /*
       * getData
       * this is where the actual call is made
       */
      function getData(text) {
	window.clearInterval(typingTimeout);
	if (text != oldText && (settings.minChars != null && text.length >= settings.minChars)) {
	  clear();
	  /*
	   * if defined, call the before action
	   */
	  if (settings.before == "function") {
	    settings.before(textInput,text);
	  }
	  /*
	   * trigger the AJAX loading animated gif
	   */
	  textInput.addClass('autocomplete-loading');
	  settings.parameters.text = text;
	  settings.parameters = {
	    'state' : getBirthCountry(),
	    'q' : text
	  };
	  /*
	   * call the remote service
	   */
	  $.getJSON(
	    /*
	     * autosuggest URL including JSONP callback
	     * e.g. http://<url>?callback=?
	     */
	    url,
	    /*
	     * querystring to pass to autosuggest URL
	     * state = $('#frmHIOrderBirthcountry').val()
	     * partial = text
	     */
	    settings.parameters,
	    /*
	     * success callback function
	     */
	    function(data) {
	      /* clear holding array */
	      var items = '';
	      /* start to process response */
	      if (data) {
		/* how many rows have been returned */
		size = data.length;
		/* iterate over all options */
		for (i = 0; i < data.length; i++) {
		  /*
		   * for each returned item
		   */
		  for ( key in data[i] ) {
		    //get key => value
		    items += '<li value="' + key + '" style="margin-bottom:0px;list-style-type:none;">';
		    items += data[i][key]['place'] + ', ' + data[i][key]['region']; /* acsatlasid */
		    items += '</li>';
		    /* consider creating an object here */
		    itemids[i] = key;
		    itemplaces[i] = data[i][key]['place'];
		  }
		  list.html(items);
		  list
		    /*
		     * show
		     * display the list
		     */
		    .show()
		    /*
		     * children
		     * manage the list items
		     */
		    .children()
		    /*
		     * hover
		     * set the selected class
		     */
		    .hover(
		      function() {
			$(this)
			  .addClass("selected")
			  .siblings()
			  .removeClass("selected");
		      },
		      function() {
			$(this)
			  .removeClass("selected");
		      }
		    )
		    /*
		     * click
		     * set the selected value and close the list
		     */
		    .click(
		      function () {
			valueInput.val( $(this).attr('value') );
			textInput.val( $(this).text() );
			/* auto fill the ephemeral fields */
			acsatlasid = parseInt( $(this).attr('value') );
			for( var i = 0; i < itemplaces.length; i++ ) {
			  if( itemids[i] == acsatlasid ) {
			    birthplaceidInput.next().val( acsatlasid );
			    birthplacenameInput.next().val( itemplaces[i] );
			  }
			}
			clear();
		      }
		    );
		}/* end for */
		/*
		 * if defined call the after function
		 */
		if (settings.after == "function") {
		  settings.after(textInput,text);
		}
	      }/* end if(data) */
	      /* remove the AJAX spinner */
	      textInput.removeClass('autocomplete-loading');
	    });
	  oldText = text;
	}
      }

      /*
       * clear
       * hide the suggestion list
       */
      function clear() {
	list.hide();
	size = 0;
	selected = 0;
	itemids.length = 0;
	itemplaces.length = 0;
      }

      textInput.keydown(
	function(e) {
	  window.clearInterval(typingTimeout);
	  /*
	   * escape
	   * clear the selection and hide the list
	   */
	  if(e.which == 27) {
	    clear();
	  }
	  /*
	   * delete / backspace
	   * manage strng alteration and resubmit for trial
	   * - displayed field shows "place, region"
	   * - partial contains partial only
	   */
	  else if (e.which == 46 || e.which == 8) {
	    clear();
//	    if (settings.validSelection) {
//	      valueInput.val('');
//	    }
	    /* test code follows */
	    valueInput.val( textInput.val() ); /* swap back search string */
	    getData(textInput.val()); /* resubmit query */
	  }
	  else if(e.which == 13) {
	    /*
	     * <enter>
	     * when <enter> is pressed then we need to
	     * - clear the partial location
	     * - hide the suggestion list
	     */
	    if ( list.css("display") == "none") {
	      /*
	       * if the list is not visible then make a new request
	       * otherwise hide the list
	       */
	      getData(textInput.val());
	    } else {
	      /* list is visible */
	      clear();
	    }
	    e.preventDefault();
	    return false;
	  }
	  else if(e.which == 40 || e.which == 9 || e.which == 38) {
	    //move up, down
	    switch(e.which) {
	    case 40: /* DOWN */
	    case 9:  /* TAB */
	      selected = selected >= size - 1 ? 0 : selected + 1;
	      break;
	    case 38: /* UP */
	      selected = selected <= 0 ? size - 1 : selected - 1;
	      break;
	    default:
	      break;
	    }
	    //set selected item and input values
	    textInput.val(
	      list.children()
		.removeClass('selected')
		.eq(selected)
		.addClass('selected')
		.text()
	    );
	    valueInput.val(
	      list.children()
		.eq(selected)
		.attr('value')
	    );
	  }
	  else {
	    //invalidate previous selection
	    if (settings.validSelection) {
	      valueInput.val('');
	    }
	    typingTimeout = window.setTimeout(
	      function() {
		getData(textInput.val());
	      }, settings.timeout);
	  }
	});
    });
};

