(function($) {

    $.tools = $.tools || {};

    $.tools.tabs = {

        conf: {
            tabs: 'a.navLink',
            current: 'current',
            onBeforeClick: null,
            onClick: null,
            effect: 'default',
            initialIndex: 0,
            event: 'click',
            api: false,
            rotate: false
        },

        addEffect: function(name, fn) {
            effects[name] = fn;
        }
    };

    var effects = {

        'default': function(i, done) {
            this.getPanes().hide().eq(i).show();
            done.call();
        },

        fade: function(i, done) {
            var conf = this.getConf(),
            speed = conf.fadeOutSpeed,
            panes = this.getPanes();

            if (speed) {
                panes.fadeOut(speed);
            } else {
                panes.hide();
            }

            panes.eq(i).fadeIn(conf.fadeInSpeed, done);
        },

        slide: function(i, done) {
            this.getPanes().slideUp(200);
            this.getPanes().eq(i).slideDown(400, done);
        },

        ajax: function(i, done) {
            this.getPanes().eq(0).load(this.getTabs().eq(i).attr("href"), done);
        }

    };

    var w;

    $.tools.tabs.addEffect("horizontal", function(i, done) {

        if (!w) {
            w = this.getPanes().eq(0).width();
        }

        this.getCurrentPane().animate({
            width: 0
        }, function() {
            $(this).hide();
        });

        this.getPanes().eq(i).animate({
            width: w
        }, function() {
            $(this).show();
            done.call();
        });

    });

    function Tabs(tabs, panes, conf) {

        var self = this,
        $self = $(this),
        current;

        $.each(conf, function(name, fn) {
            if ($.isFunction(fn)) {
                $self.bind(name, fn);
            }
        });

        $.extend(this, {
            click: function(i, e) {

                var pane = self.getCurrentPane();
                var tab = tabs.eq(i);

                if (typeof i == 'string' && i.replace("#", "")) {
                    tab = tabs.filter("[href*=" + i.replace("#", "") + "]");
                    i = Math.max(tabs.index(tab), 0);
                }

                if (conf.rotate) {
                    var last = tabs.length - 1;
                    if (i < 0) {
                        return self.click(last, e);
                    }
                    if (i > last) {
                        return self.click(0, e);
                    }
                }

                if (!tab.length) {
                    if (current >= 0) {
                        return self;
                    }
                    i = conf.initialIndex;
                    tab = tabs.eq(i);
                }

                if (i === current) {
                    return self;
                }

                e = e || $.Event();
                e.type = "onBeforeClick";
                $self.trigger(e, [i]);
                if (e.isDefaultPrevented()) {
                    return;
                }

                effects[conf.effect].call(self, i, function() {

                    e.type = "onClick";
                    $self.trigger(e, [i]);
                });

                e.type = "onStart";
                $self.trigger(e, [i]);
                if (e.isDefaultPrevented()) {
                    return;
                }

                current = i;
                tabs.removeClass(conf.current);
                tab.addClass(conf.current);

                return self;
            },

            getConf: function() {
                return conf;
            },

            getTabs: function() {
                return tabs;
            },

            getPanes: function() {
                return panes;
            },

            getCurrentPane: function() {
                return panes.eq(current);
            },

            getCurrentTab: function() {
                return tabs.eq(current);
            },

            getIndex: function() {
                return current;
            },

            next: function() {
                return self.click(current + 1);
            },

            prev: function() {
                return self.click(current - 1);
            },

            bind: function(name, fn) {
                $self.bind(name, fn);
                return self;
            },

            onBeforeClick: function(fn) {
                return this.bind("onBeforeClick", fn);
            },

            onClick: function(fn) {
                return this.bind("onClick", fn);
            },

            unbind: function(name) {
                $self.unbind(name);
                return self;
            }

        });

        tabs.each(function(i) {
            $(this).bind(conf.event, function(e) {
                self.click(i, e);
                return false;
            });
        });

        if (location.hash) {
            self.click(location.hash);
        } else {
            if (conf.initialIndex === 0 || conf.initialIndex > 0) {
                self.click(conf.initialIndex);
            }
        }

        panes.find("a[href^=#]").click(function(e) {
            self.click($(this).attr("href"), e);
        });
    }

    $.fn.tabs = function(query, conf) {

        var el = this.eq(typeof conf == 'number' ? conf: 0).data("tabs");
        if (el) {
            return el;
        }

        if ($.isFunction(conf)) {
            conf = {
                onBeforeClick: conf
            };
        }

        var globals = $.extend({}, $.tools.tabs.conf),
        len = this.length;
        conf = $.extend(globals, conf);

        this.each(function(i) {
            var root = $(this);

            var els = root.find(conf.tabs);

            if (!els.length) {
                els = root.children();
            }

            var panes = query.jquery ? query: root.children(query);

            if (!panes.length) {
                panes = len == 1 ? $(query) : root.parent().find(query);
            }

            el = new Tabs(els, panes, conf);
            root.data("tabs", el);

        });

        return conf.api ? el: this;
    };

})(jQuery);
