(function($){
    var methods = {
        init:function(options){
            /*
             * context: function, jQuery object, selector
             * city: function, jQuery object, selector
             * state: function, jQuery object, selector
             * setup: function :: Initial actions
             * ajax_call: function :: Retrieve state/city info
             */
            var settings = {
                context:function(target){
                    return target.closest('form');
                },
                city:function(context,target){
                    var prefix = target.attr('name').replace('zip','');
                    return context.find('[name^=' + prefix + 'city]');
                },
                state:function(context,target){
                    var prefix = target.attr('name').replace('zip','');
                    return context.find('[name^=' + prefix + 'state]');
                },
                setup:function(context,target,city,state){
                    if (city.is('select')) {
                        city.find('option, optgroup').remove();
                    }
                    
                    if (state.is('select')) {
                        state.find('option, optgroup').remove();
                    }
                    
                    city.add(state).attr('readonly','readonly')
                        .attr('title','Enter a valid zip code to change the city')
                        .css({'background-color':'#e6e6e6'})
                        //.parent()
                        .css({visibility:'hidden'});
                    
                    if (typeof $.tools !== 'undefined' && typeof $.tools.validator !== 'undefined') {
                        $.tools.validator.fn(target,function(el,value) {
                            var change_target = function(){
                                if (target.hasClass('waiting-call')) {
                                    target.change();
                                }
                            };
                            
                            if (target.hasClass('ajax-call') && city.length && state.length && city.val() && state.val()) {
                                return true;
                            }
                            
                            if (target.hasClass('disabled')) {
                                return "Confirmating zip code";
                            }
                            
                            if (target.hasClass('ajax-call')) {
                                return "Invalid zip code";
                            }
                            
                            target.addClass('waiting-call');
                            setTimeout(change_target,500);
                            
                            return "Confirmating zip code...";
                        });
                    }
                },
                triggers:{
                    keydown:function(context,target,city,state,ajax_call){
                        target.removeClass('ajax-call');
                        target.removeClass('waiting-call');
                    },
                    change:function(context,target,city,state,ajax_call){
                        city.add(state)
                            //.parent()
                            .css({visibility:'hidden'});
                        
                        if (city.is('select')) {
                            city.find('option').remove();
                        }
                        else {
                            city.val('');
                        }
                        
                        if (state.is('select')) {
                            state.find('option').remove();
                        }
                        else {
                            state.val('');
                        }
                        
                        if (!target.val().match(/[0-9]{5}/ig)) {
                            return;
                        }
                        
                        target
                            .removeClass('ajax-call')
                            .removeClass('waiting-call')
                            .addClass('disabled')
                            .attr('disabled','disabled')
                            .trigger('keyup');
                        
                        if (typeof ajax_call === 'function') {
                            ajax_call(context,target,city,state);
                        }
                    }
                },
                ajax_call:function(context,target,city,state){
                    $.ajax({
                        url:'/search/state-by-zip/' + target.val() + '/',
                        dataType:'html',
                        success:function(data){
                            data = data.split('|'); // state short name|state name|city name
                            target
                                .addClass('ajax-call')
                                .removeClass('disabled')
                                .removeAttr('disabled');
                            
                            if (data.length < 3) {
                                target.trigger('keyup');
                                return;
                            }
                            
                            city.add(state)
                                //.parent()
                                .css({visibility:'visible'});
                            
                            if (city.is('select')) {
                                city.html('<option></option>')
                                .find('option')
                                .text(data[2]).val(data[2]);
                            }
                            else {
                                city.val(data[2]);
                            }
                            
                            if (state.is('select')) {
                                state.html('<option></option>')
                                .find('option')
                                .text(data[0]).val(data[0]);
                            }
                            else {
                                state.val(data[0]);
                            }
                            
                            target.add(city).add(state).trigger('keyup');
                        },
                        error:function(){
                            target
                                .addClass('ajax-call')
                                .removeClass('disabled')
                                .removeAttr('disabled')
                                .add(city)
                                .add(state)
                                .trigger('keyup');
                        }
                    });
                }
            },
            /*
             * Auxiliar function to bind multiple actions into a loop
             *
             * JSLint complains about creating functions into a loop
             *
             * The extra arguments are those needed by the trigger
             * Damn, this one is ugly
             */
            binder = function (object,trigger,action,context,target,city,state,ajax_call) {
                if (typeof action !== 'function') {
                    return;
                }
                
                object.bind(trigger,function(){
                    action(context,target,city,state,ajax_call);
                });
            };
            
            if (options) {
                $.extend(settings,options);
            }
            
            return this.each(function(){
                var $this = $(this),
                context = (typeof settings.context === 'function') ? settings.context($this) : $(settings.context),
                city = (typeof settings.city === 'function') ? settings.city(context,$this) : $(settings.city),
                state = (typeof settings.state === 'function') ? settings.state(context,$this) : $(settings.state),
                trigger;
                
                if (typeof settings.setup === 'function') {
                    settings.setup(context,$this,city,state);
                }
                
                for (trigger in settings.triggers) {
                    binder(
                        $this, // Object to apply trigger to
                        trigger, // Name of the trigger
                        settings.triggers[trigger], // Function to fire by the trigger
                        
                        context,
                        $this,
                        city,
                        state,
                        settings.ajax_call
                    );
                }
                
                if ($this.val().length) {
                    $this.change();
                }
            });
        }
    };
    
    $.fn.ajax_zip = function(method) {
        if (methods[method]) {
            return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
        }
        else if (typeof method === 'object' || ! method) {
            return methods.init.apply( this, arguments );
        }
        else {
            $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
        }
    };
}(jQuery));
