jQuery.fn.zip_complete = function(url, settings , default_value)
{
    return this.each( function() {
        var textInput = $(this);
        //create a new hidden input that will be used for holding the return value when posting the form, then swap names with the original input
        //TODO: should be stored in textItem.data
        if (textInput.attr('name').split('_')[1] != 'text') {
            textInput.after('<input type="hidden" name="' + textInput.attr("name") + '" value="' + default_value  + '"/>').attr("name", textInput.attr("name") + "_text");
        } else if (textInput.data('events')) {
            textInput.data('events').keydown = undefined;
        }
        var valueInput = textInput.next();
        valueInput.after('<ul class="autocomplete" style="z-index: 100"></ul>');
        var pos = $(textInput).position();
        var list = valueInput.next().css({'position':"absolute", 'top': $(textInput).position().top + $(textInput).outerHeight()-3, 'left': $(textInput).position().left, 'min-width': textInput.width()+7});
        var oldText = '';
        var typingTimeout = 100;
        var size = 0;
        var selected = 0;
        var outData = null;
        settings = jQuery.extend(
        {
            minChars : 3,
            timeout: 300,
            after: null,
            before: null,
            display_warning: true,
            callback: null,
            validator: /.*/,
            validSelection: true,
            completion_data: null,
            use_simple_list_name: false,
            parameters: {
                'inputName': valueInput.attr('name'),
                'inputId': textInput.attr('id'),
                'behavior': 'paa'
            },
            trans_select_city: 'Select your city:',
            trans_select_zip: 'Select your zip code:'
        } , settings);
        function setInputValue(value) {
            textInput.val(value);
            textInput.triggerHandler('value_set');
        }

        function displayWarning(message) {
            if (settings.display_warning) {
                var name = $('#'+textInput.attr('id')+'_label').text().split(':')[0].toLowerCase();
                list.html('<li id="lp">' + name + ': ' + message + '</li>');
            } else {
                list.hide();
                textInput.triggerHandler('warning');
            }
        }

        function getData(text, focus_field, dont_blur) {
            if (!settings.validator.test(text) && text) {
                displayWarning(settings.trans_unrecognized_keyword);
                if (settings.display_warning) {
                    list.css({'position':"absolute", 'top': $(textInput).position().top + $(textInput).outerHeight()-3, 'left': $(textInput).position().left, 'min-width': textInput.width()+7});
                    list.show();
                }
                return;
            }
            window.clearInterval(typingTimeout);
            if (text.length < 3) {
                clear();
            }
            if (settings.minChars != null && text.length >= settings.minChars) {
                clear();
                if (settings.before) {
                    settings.before(textInput,text);
                }
                textInput.addClass('autocomplete-loading');
                settings.parameters.text = text;
                var process_data = function(data) {
                    // abort if user already typed more to eliminate race condition with another autocomplete calls
                    if (textInput.val().length > settings.parameters.text.length) {
                        return;
                    }
                    outData = data;
                    var items = '';
                    if (!data.err_happened) {
                        if (data.completions.length > 0 || settings.callback) {
                            size = data.completions.length;
                            for (i = 0; i < size; i++) {
                                k =[];
                                if (settings.use_simple_list_name && typeof(data.completions[i][1]) != 'undefined') {
                                    // looks like recently zip codes are returning in sligthly different form
                                    // one additional settings will do the trick
                                    if (settings.zip_code_complete) {
                                        tmp = data.completions[i][1].split(',');
                                        k = tmp[0];
                                    } else {
                                        k = data.completions[i][1].split('');
                                    }
                                } else {
                                    var str_sr = '';
                                    if (typeof(data.completions[i].sec_regions) != 'undefined' && data.completions[i].sec_regions.length > 1) {
                                        var sec_regs = data.completions[i].sec_regions;
                                        var cleaned_sec_regs = [];
                                        for (sec_reg in sec_regs) {
                                            if (sec_regs[sec_reg] != data.completions[i].city_name) {
                                                cleaned_sec_regs.push(sec_regs[sec_reg]);
                                            }
                                        }
                                        str_sr = ' (' + cleaned_sec_regs.slice(0, 2).join('; ');
                                        if (cleaned_sec_regs.length > 2) {
                                            str_sr += ' and ' + (cleaned_sec_regs.length - 2) + ' more';
                                        }
                                        str_sr += ')';
                                    }
                                    k = (data.completions[i].list_name+str_sr).split('');
                                }
                                var q=[];
                                q[0]="<strong>";
                                for(var j=0; j<text.length; j++) {
                                  q[j+1]=k[j];
                                }
                                for(var w=text.length+1; w<k.length+2; w++) {
                                    if(w == text.length+1) {
                                        q[w]="</strong>";
                                    } else {
                                        q[w]=k[w-2];
                                    }
                                }
                                var suggestionText = q.join("");
                                items += '<li id="lp' + i + '" class="paa_completion_list">' + suggestionText + '</li>';
                            }
                            if (size > 0 && data.completions[0].sys_level == 6) {
                                list_header = '<div class="autocompleteGroup">'+settings.trans_select_zip+'</div>';
                            } else {
                                list_header = '<div class="autocompleteGroup">'+settings.trans_select_city+'</div>';
                            }
                            list.html(list_header+items);
                            list.css({'position':"absolute", 'top': $(textInput).position().top + $(textInput).outerHeight()-3, 'left': $(textInput).position().left, 'min-width': textInput.width()+7});
                            list.show();
                            list.children('li').mouseover( function() {
                                $(this).addClass("selected");
                            });
                            list.children('li').mouseout( function() {
                                $(this).removeClass("selected");
                            });
                            list.children('li').click( function() {
                                var completion = data.completions[parseInt($(this).attr("id").replace("lp",""))];
                                if (typeof(completion) != 'undefined' && typeof(completion.is_ambiguous) != 'undefined') {
                                    var is_ambiguous = true;
                                } else {
                                    var is_ambiguous = false;
                                }
                                var sys_path = completion.sys_path;
                                if (is_ambiguous === 'false') {
                                    setInputValue( $(this).text() );
                                    valueInput.val( sys_path );
                                    clear();
                                    if (focus_field) {
                                        textInput.focus();
                                    }
                                } else {
                                    setInputValue($(this).text());
                                    valueInput.val('');
                                    clear();
                                    if (focus_field) {
                                        textInput.focus();
                                    }
                                }
                                if (settings.callback) {
                                    settings.callback(completion, dont_blur);
                                }
                            });
                            if(data.completions.length == 1)
                            {
                                list.children().click();
                            } else if (data.completions.length == 0 && settings.callback)
                            {
                                displayWarning(settings.trans_unrecognized_keyword);
                            }
                            if (settings.after) {
                                settings.after(textInput,text);
                            }
                        }
                        textInput.removeClass('autocomplete-loading');
                    }
                };
                $.getJSON(url, settings.parameters, process_data);
                oldText = text;
            }
        }

        function clear() {
            list.hide();
            size = 0;
            selected = -1;
        }

        var idInter;

        var clearListAfterBackspaceKey = function() {
            if(list.css('display')!='none') {
                clear();
                clearInterval(idInter);
            } else {
                clearInterval(idInter);
            }
        };

        textInput.unbind('initiate_field');
        textInput.bind('initiate_field', function (dont_blur) {
            getData(textInput.val(), false, dont_blur);
        });

        textInput.keydown(function(e) {
            if(e.which == 9) {
                clear();
            } else {
                window.clearInterval(typingTimeout);
            }

            if(e.which == 27) { // escape
                clear();
            } else if (e.which == 46 || e.which == 8) { // delete and backspace
                if(textInput.val().length <= settings.minChars) {
                    idInter = setTimeout(clearListAfterBackspaceKey, 500);
                }
            } else if((e.which == 40 || e.which == 38) && textInput.val()!="") { // move up, down
                switch(e.which)
                {
                    case 40:
                        selected = selected >= size ? 0 : selected + 1;
                        break;
                    case 38:
                          selected = selected <= 0 ? size : selected - 1;
                          break;
                    default: break;
                }
                // set selected item and input values
                var selected_option = parseInt(list.children().eq(selected).attr('id').replace("lp", ""));
                if (!isNaN(selected_option)) {
                    if (outData.completions[selected_option]['is_ambigous'] === 'false') {
                          setInputValue(list.children().removeClass('selected').eq(selected).addClass('selected').text());
                          valueInput.val(outData.completions[selected_option]['sys_path']);
                    } else {
                          setInputValue(list.children().removeClass('selected').eq(selected).addClass('selected').text());
                          valueInput.val('');
                    }
                    if (settings.callback) {
                        settings.callback(outData.completions[selected_option], true);
                    }
                }
            } else if(e.which==13 && textInput.val()!="" && !settings.callback) {
                $("#search_form").submit();
            }
        });
        textInput.unbind('keypress');
        textInput.bind('keypress', function(e) {
            var found = [false];
            $([0,27,46,8,9,40,38,13]).each(function() {
                if(this == e.which) {
                    found[0] = true;
                }
            });
            if (found[0]) {
                return;
            }
            if (settings.validSelection) {
                valueInput.val('');
            }
            typingTimeout = window.setTimeout(function() {
                getData(textInput.val(),true);
            }, settings.timeout);
        });
    });
};

function hideDefaultInputText(e) {
    var oldText = null;
    var focusNo = null;
    $(e).focus(function() {
        if(focusNo==null) {
            focusNo+=1;
            oldText = $(this).val();
            $(this).val("");
        } else {
            if($(this).val()==oldText) {
                $(this).val("");
            }
        }
    });
    $(e).blur(function() {
        if($(this).val()==="") {
            $(this).val(oldText);
        }
    });
}

function checkValueAndSubmit() {
    if($("#query").val() == searchForText) { //searchForText is defined in base.html template
        $("#query").val("");
    }
    if($("#near_to").val() == areaText) { //areaText is defined in base.html template
        $("#near_to").val("");
    }
    $("#search_form").submit();
}

function ZipFieldForm(settings) {
    var initial_city_val = '',
        initial_zip_val = '',

        set_city_field_data = function (data, dont_blur) {
            city_field.data('data', data);
            if (data.sys_level == 6) {
                city_field.data('value', data.city_name);
                city_field.val(data.city_name);
                zip_field.data('value', data.name);
                zip_field.val(data.name).change();
                init_zip_field();
                zip_field.triggerHandler('initiate_field');
            } else {
                city_field.data('value', data.name);
                city_field.val(data.name);
                init_zip_field();
                if (initial_zip_val.length > 0){
                    zip_field.data('value',initial_city_val);
                    zip_field.val(initial_zip_val);
                    zip_field.triggerHandler('initiate_field');
                    initial_zip_val = '';
                }
            }

            if(settings.create_zip_selector_on_default)
            {
                create_zip_selector();
            }

            city_field[0].selected = true;
            if(!dont_blur) {
                city_field.blur();
                zip_field.focus();
            }
        },

        init_zip_field = function () {
            if(typeof(prefix) == 'undefined') {
                var prefix = '';
            }
            zip_field.zip_complete(prefix + "/xhr/completeregion/paa/" + settings.suffix + city_field.data('data').sys_path + '/',{
                    timeout:400,
                    callback: set_zip_field_data,
                    validator: /\d+/,
                    minChars: 2,
                    display_warning: false,
                    parameters : {
                        'inputName': 'zip',
                        'inputId': zip_field.attr('id')
                    },
                    use_simple_list_name: true,
                    zip_code_complete: true,
                    trans_select_city: settings.trans_select_city,
                    trans_select_zip: settings.trans_select_zip
                }, zip_field.val());

            zip_field.bind('warning', function() {
                $(this)[0].selected = false;
                set_form_state();
            });

            zip_field.bind('value_set', function() {
                $(this).data('value',zip_field.val());
                set_form_state();
            });

            zip_field.keyup(function() {
                set_form_state();
            });

            zip_field.blur(function() {
                set_form_state();
            });
        },

        set_zip_field_data = function (data) {
            zip_field.data('value', data.name);
            zip_field.data('data', data);
            zip_field.val(data.name);
            set_form_state();
        },

        field_is_valid = function (field) {

            if (field.val() == '') {
                return false;
            }

            if (typeof(field.data('value')) != 'undefined') {
                if (field.val() != field.data('value')) {
                    return false;
                } else {
                    return true;
                }
            }

            field_errors = $('#'+field.attr('id')+'_errors');
            if (typeof(field_errors) != 'undefined' || field_errors.length > 0) {
                return false;
            }
            return true;
        },

        show_map_controls = function() {
            geo_location_checkbox.show();
            settings.label_for_geo_location_checkbox.show();
        },

        hide_map_controls = function() {
            geo_location_checkbox.triggerHandler('hide_map');
            geo_location_checkbox.hide();
            settings.label_for_geo_location_checkbox.hide();
        },

        set_form_state = function() {
            if (!field_is_valid(city_field)) {
                city_field[0].selected = false;
                $('#sec_regions').hide();
                restore_zip_input();
                zip_field.removeData('data');
                zip_field.removeData('value');
                zip_field.val('');
                clear_zip_validation_error();
                settings.zip_box.hide();
                settings.address_box.hide();
                $('input:hidden[name=city]').val('');
                if (initial_address_val) {
                    address_field.val(initial_address_val);
                    initial_address_val = '';
                }
                hide_map_controls();
            } else {
                var show_zip = true;
                if (settings.zip_box.is(':visible')) {
                    show_zip = false;
                }
                settings.zip_box.show();
                settings.address_box.show();
                $('input:hidden[name=city]').val(city_field.val());
                show_map_controls();
                if (field_is_valid(zip_field)) {
                    $('input:hidden[name=zip]').val(zip_field.val());
                    clear_zip_validation_error(true);
                    show_secondary_region_note();
                    zip_field[0].selected = true;
                } else {
                    if (initial_zip_val.length > 0) {
                        zip_field.val(initial_zip_val);
                        zip_field.data('value', initial_zip_val);
                    }
                    $('#sec_regions').hide();
                    zip_field[0].selected = false;
                    if (show_zip) {
                        if ((zip_field.val().length != settings.zipcode_length) && (zip_field.val().length > 0)) {
                            show_zip_validation_error();
                        }
                    } else {
                        if (zip_field.val().length != settings.zipcode_length) {
                            show_zip_validation_error();
                        }
                    }
                    $('input:hidden[name=zip]').val('');
                }
            }
        },

        show_secondary_region_note = function() {
            if (typeof(zip_field.data('data')) != 'undefined' &&
                typeof(zip_field.data('data').sec_regions) != 'undefined' &&
                zip_field.data('data').sec_regions.length > 0) {
                sec_reg_html = '';
                var sec_regions = zip_field.data('data').sec_regions;
                for (sec_region in sec_regions) {
                    var city_name = sec_regions[sec_region];
                    if (city_name != city_field.val()) {
                        sec_reg_html += '<span class="sec_region" style="display:block">* '+city_name+' </span>';
                    }
                }
                if (sec_reg_html.length > 0) {
                    var zipHeader = 'Zip code ' + zip_field.val() + ' is also part of: <br />';
                    $('#sec_regions').html(zipHeader + sec_reg_html);
                    $('#sec_regions').show();
                    $('#sec_regions').attr('style', 'display:block');
                }
            }
        },

        show_zip_validation_error = function(force){
            var error_note = '<div class="zip_error_note">' + settings.trans_error_note + '<a class="zip_error_button">' +  settings.trans_select_from_list + '</a></div>';
            settings.zip_box.addClass('zip_error');
            $('.zip_error_note').remove();
            $('#zip_error_slot').append(error_note);
            $('.zip_error_button').bind('click' , function() {
                show_zip_typing_error();
                create_zip_selector();
            });
        },

        clear_zip_validation_error = function(force) {
            if(settings.zip_box.hasClass('showError') && !force) { // don't remove errors rendered by view
                return;
            }
            settings.zip_box.removeClass('zip_error');
            settings.zip_box.removeClass('showError');
            $('.zip_error_note').remove();
        },

        show_zip_typing_error = function() {
            var error_note = '<div class="zip_error_note">' + settings.trans_error_note + '<a class="zip_error_button">' + settings.trans_type_it + '</a></div>';
            $('.zip_error_note').remove();
            $('#zip_error_slot').append(error_note);
            $('.zip_error_button').bind('click', restore_zip_input);
        },

        create_zip_selector = function() {
            var options = '<option>' + settings.trans_select_code + '</option>',
                options_data = city_field.data('data').zips,
                len = options_data.length,
                i;

            for (i = 0; i < len; i++) {
                options += '<option value = "' + options_data[i] + '">' + options_data[i] + '</option>';
            }

            select_field = '<select id="zip_select" name = "' + zip_field.attr('name')  + '">' + options + '</select>';

            zip_field.before(select_field);

            $('#zip_select').change(function() {
                restore_zip_input();
                clear_zip_validation_error();
                zip_field.data('value',$(this).val());
                zip_field.val($(this).val());
                $('input:hidden[name=zip]').val(zip_field.val());
                zip_field.triggerHandler('keydown');
                set_form_state();
            });
            zip_field.hide();
        },

        restore_zip_input = function() {
            $('#zip_select').remove();
            clear_zip_validation_error();
            show_zip_validation_error();
            zip_field.show();
        };

    if (typeof(settings) == 'undefined' || !settings) {
        throw "No settings defined.";
    }

    if (typeof(settings.city_field_id) == 'undefined' || !settings.city_field_id) {
        throw "city_field_id must be defined in settings";
    }

    if (typeof(settings.address_field_id) == 'undefined' || !settings.address_field_id) {
        throw "address_field_id must be defined in settings";
    }

    if (typeof(settings.zip_field_id) == 'undefined' || !settings.zip_field_id) {
        throw "zip_field_id must be defined in settings";
    }

    if (typeof(settings.address_box) == 'undefined' || !settings.address_box) {
        throw "address_box must be defined in settings";
    }

    if (typeof(settings.zip_box) == 'undefined' || !settings.zip_box) {
        throw "zip_box must be defined in settings";
    }

    if(typeof(settings.suffix) == 'undefined') {
        settings.suffix = '';
    }

    if (typeof(settings.trans_unrecognized_keyword) == 'undefined') {
        settings.trans_unrecognized_keyword = "<strong>We are sorry</strong><br />The word you've typed has not been recognized. Please check your spelling.";
    }

    if (typeof(settings.trans_error_note) == 'undefined') {
        settings.trans_error_note = "Zip code you've entered is not valid for your city.";
    }

    if (typeof(settings.trans_type_it) == 'undefined') {
        settings.trans_type_it  = 'Please select from a list or type it.';
    }

    if (typeof(settings.trans_select_from_list) == 'undefined') {
        settings.trans_select_from_list = 'Please type it again or select from a list.';
    }

    if (typeof(settings.trans_select_code) == 'undefined') {
        settings.trans_select_code = "select code";
    }

    if (typeof(settings.trans_select_zip) == 'undefined') {
        settings.trans_select_zip = "Select your zip code:";
    }

    if (typeof(settings.trans_select_city) == 'undefined') {
        settings.trans_select_city = "Select your city:";
    }

    if (typeof(settings.zipcode_length) == 'undefined') {
        settings.zipcode_length = 5;
    }

    if(typeof(settings.create_zip_selector_on_default) == 'undefined') {
        settings.create_zip_selector_on_default = false;
    }

    if (typeof(settings.skip_blur_validation) == 'undefined') {
        settings.skip_blur_validation = false;
    }

    var local_prefix = (typeof(settings.prefix) == 'undefined' || !settings.prefix) ? '' : settings.prefix,
        geo_location_checkbox = $('#'+settings.geo_location_checkbox_id),
        city_field = $('#'+settings.city_field_id),
        zip_field = $('#'+settings.zip_field_id),
        address_field = $('#'+settings.address_field_id);

    if (typeof(city_field) == 'undefined') {
        throw "can't find city field with id "+settings.city_field_id;
    }
    else if (typeof(zip_field) == 'undefined') {
        throw "can't find zip field with id "+settings.zip_field_id;
    }
    else if (typeof(address_field) == 'undefined') {
        throw "can't find address field with id "+settings.address_field_id;
    }

    initial_city_val = city_field.val();
    initial_zip_val = zip_field.val();
    initial_address_val = address_field.val();
    city_field.zip_complete(local_prefix+"/xhr/completeregion/paa/" + settings.suffix,{
            timeout:400,
            callback: set_city_field_data,
            validator: /.*/,
            minChars:3,
            display_warning: false,
            trans_select_city: settings.trans_select_city,
            trans_select_zip: settings.trans_select_zip
        }, city_field.val());

    if (typeof(initial_city_val) !='undefined' && initial_city_val.length > 0) {
        city_field.val(initial_city_val);
        city_field.triggerHandler('initiate_field', [true]);
        initial_city_val = '';
    }

    if (geo_location_checkbox.attr('checked')) {
        show_map_contols();
    } else {
        hide_map_controls();
    }

    set_form_state();

    city_field.keyup(function() {
        set_form_state();
    });

    city_field.keyup(function() {
        set_form_state();
    });

    city_field.bind('value_set', function() {
        $(this).data('value',$(this).val());
        set_form_state();
    });

    city_field.bind('warning', function() {
        $(this)[0].selected = false;
        if('validate' in $(this)[0]) {
            $(this)[0].validate(true);
        }
    });
}

