/*
 * mtcTabs- A jQuery plugin for responsive tabs
 * Version : 1.0
 * Author : Paul McAvoy
 */

/*
 * Things to improve on:
 * Acitve state stays if clicked and window is resized
 * Force slide on accordion unless differently specified
 * Use of chosen, might need to allow for chosen options to be applied
 * collapsible option
 * height stay same when animating
 * ability to disable some of the tabs programmatically
 * destroy on breakpoint
 * move nav title to element within
 * change animtion of accordion to open and close at sametime - option
 */

;(function ($, window, document, undefined) {

    "use strict";

    // Create the defaults once
    var plugin_name = 'mtcTabs';

    // The actual plugin constructor
    function Plugin(element, options) {
        this.element = element;
        this.$this = $(this.element);
        this.defaults = {
            active: 1,
            animation_type: 'fade', // fade, slide
            content_selector: '.tabContent',
            content_wrap_selector: '.tabsContentWrap',
            easing: 'linear',
            mode: 'tabs', // tabs, accordion, select
            responsive: [],
            speed: 400,
            afterChange: '',
            beforeChange: '',
            onChange: '',
            onDestroy: '',
            onInit: ''
        };
        this.all_settings = $.extend(true, {}, this.defaults, options);
        this.settings = $.extend({}, this.defaults, options);
        this._defaults = this.defaults;
        this._name = plugin_name;
        this.active_index = this.settings.active;

        this.init();
    }

    // Avoid Plugin.prototype conflicts
    $.extend(Plugin.prototype, {

        checkResponsive: function () {
            // set some vars
            var plugin = this;
            // set the default as tablet
            this.settings = this.all_settings;
            // iterate through settings and find the right one
            if (this.all_settings.responsive) {

                // loop through responsive settings
                $.each(this.all_settings.responsive, function (i, value) {
                    // find one that is active
                    if (matchesMediaQuery(0, value.breakpoint)) {
                        plugin.settings = value.settings;
                    }
                });
            }
        },

        readResponsive: function () {
            // prepare the responsive array
            var plugin = this,
                previous_breakpoints_settings = this.all_settings;

            if (this.all_settings.responsive) {
                // loop through responsive settings
                $.each(this.all_settings.responsive, function (i, value) {
                    // extend breakpoints settings to include ones from previous
                    value.settings = $.extend(true, {}, previous_breakpoints_settings, value.settings);
                    // get rid of responsive duplicate
                    delete value.settings.responsive;
                    // previous now become the current ones
                    previous_breakpoints_settings = value.settings;
                });
            }
        },

        init: function () {
            // set some vars
            var plugin = this,
                $this = plugin.$this;
            // call responsive settings
            plugin.readResponsive();
            plugin.checkResponsive();

            // add class to plugin object div
            $this.addClass('mtcTabsInitialised');
            // wrap inner content
            $this.wrapInner($('<div />', {
                class: plugin.settings.content_wrap_selector.replace('.', '')
            }));

            // update active to 0 index
            plugin.active_index = plugin.settings.active - 1;

            // build navigation and set open element
            plugin.setOpen();
            plugin.buildNavigation();

            plugin.runCallbackFunction('onInit');

            // resize function
            $(window).on('resize.mtcTabs', debouncer(function () {
                plugin.resize();
            }));
        },

        resize: function () {
            // set some vars
            var plugin = this,
                $this = plugin.$this,
                cached_mode = plugin.settings.mode;
            // update responsive settings
            plugin.checkResponsive();

            // if mode changes. rebuild the nav
            if (cached_mode !== plugin.settings.mode) {
                // if accordion mode is in action run approriate destroy method
                if (cached_mode === 'accordion') {
                    plugin.destroy(cached_mode);
                }
                // if select mode is in action run approriate destroy method
                if (cached_mode === 'select') {
                    plugin.destroy(cached_mode);
                }
                // destroy navigation element
                plugin.destroy('navigation');
                plugin.buildNavigation();
            }
        },

        buildNavigation: function () {
            var plugin = this;

            // fire function depending on which mode is set
            switch (plugin.settings.mode) {
                case 'accordion':
                    plugin.accordionNav();
                break;
                case 'select':
                    plugin.selectNav();
                break;
                default:
                    // tabs is the default
                    plugin.tabNav();
                break;
            }

            // add unique id to content section
            plugin.$this.find(plugin.settings.content_selector).each(function (i) {
                // add unique ids to element
                $(this).attr('data-tab-id', '#tab_' + i);
            });

            plugin.switchTrigger();
        },

        setOpen: function () {
            // some vars
            var plugin = this,
                $this = plugin.$this,
                content_container = $this.find(plugin.settings.content_selector);

            // set acitve element
            if (plugin.settings.active > 0) {
                content_container.eq(plugin.active_index).addClass('open').show();
            }
        },

        tabNav: function () {
            // some vars
            var plugin = this,
                $this = plugin.$this,
                content_container = $this.find(plugin.settings.content_selector),
                tab_nav = $('<nav />', {
                    class: 'tabsNav'
                }).prepend('<ul />'); // create nav element and prepend ul to it

            // add nav to the page
            $this.prepend(tab_nav);
            // add links to the nav element
            content_container.each(function (i) {
                var tab_title = $(this).find('.tabTitle').html(); // get titles from tabs

                // find nav and nav elements to it.
                tab_nav
                    .find('ul')
                    .append('<li><a href="#tab_' + i + '">' + tab_title + '</a></li>');
                // add active class to specified active nav element
                tab_nav.find('li').eq(plugin.getActive()).addClass('active');
            });
        },

        tabTrigger: function (nav_element) {
            // some vars
            var plugin = this;

            // on click of the tabs nav element
            nav_element.find('a').on('click', function (e) {
                // preventDefault
                e.preventDefault();

                var clicked = $(this),
                    element_id = clicked.attr('href'),
                    content_container = clicked.closest('.tabsNav').next();

                // do below only if this nav element isn't the active one
                if (!clicked.parent().hasClass('active')) {
                    // set active nav element
                    clicked.parent().addClass('active').siblings().removeClass('active');
                    // run beforeChange function before running any animation
                    plugin.runCallbackFunction('beforeChange');
                    // fire function depending on which mode is set
                    plugin.switchAnimationType(element_id, content_container);
                }

                $('ul.faqs li h4').show();
                $('ul.faqs li .answer:not(:first)').hide();
            });
        },

        accordionNav: function (active) {
            // some vars
            var plugin = this,
                content_container = plugin.$this.find(plugin.settings.content_selector);

            // add nav element to before each content_container
            content_container.each(function (i) {
                var $this = $(this),
                    tab_title = $this.find('.tabTitle').html(), // get titles from tabs
                    tab_nav = $('<nav />', {
                        class: 'tabsNav'
                    }); // create nav element

                // wrap tab content in container
                $this.wrap('<div class="accordionWrap" />');
                // build nav element
                tab_nav
                    .insertBefore($this)
                    .prepend('<a href="#tab_' + i + '">' + tab_title + '</a>');
                // add active class to specified active nav element
                if (i === plugin.getActive()) {
                    tab_nav.addClass('active');
                }
            });
        },

        accordionTrigger: function (nav_element) {
            // set some vars
            var plugin = this;

            // on click of the tabs nav element
            nav_element.find('a').on('click', function (e) {
                // preventDefault
                e.preventDefault();

                var clicked = $(this),
                    element_id = clicked.attr('href'),
                    content_container = clicked.closest('.accordionWrap').parent();

                // do below only if this nav element isn't the active one
                if (!clicked.parent().hasClass('active')) {
                    // set active nav element
                    content_container.find('.tabsNav').removeClass('active');
                    clicked.parent().addClass('active');
                    // run beforeChange function before running any animation
                    plugin.runCallbackFunction('beforeChange');
                    // fire function depending on which mode is set
                    plugin.switchAnimationType(element_id, content_container);
                }

                $('ul.faqs li h4').show();
                $('ul.faqs li .answer:not(:first)').hide();
            });
        },

        selectNav: function (active) {
            // some vars
            var plugin = this,
                $this = plugin.$this,
                content_container = $this.find(plugin.settings.content_selector),
                tab_nav = $('<nav />', {
                    class: 'tabsNav'
                }).prepend('<select />'); // create nav element and prepend select to it

            // add nav to the page
            $this.prepend(tab_nav);
            // add links to the nav element
            content_container.each(function (i) {
                var tab_title = $(this).find('.title').text(); // get titles from tabs

                // find nav and nav elements to it.
                tab_nav
                    .find('select')
                    .append('<option value="#tab_' + i + '">' +  tab_title +  '</option>');
                // set active option
                tab_nav
                    .find('option')
                    .eq(plugin.getActive())
                    .attr('selected', 'selected');
            });

            // initiate chosen
            tab_nav.find('select').chosen({
                disable_search_threshold: 8
            });
        },

        selectTrigger: function (nav_element) {
            // set some vars
            var plugin = this;

            // on change of the select
            nav_element.find('select').on('change', function (e) {
                // preventDefault
                e.preventDefault();

                var clicked = $(this),
                    element_id = clicked.val(),
                    content_container = clicked.closest('.tabsNav').next();

                // run beforeChange function before running any animation
                plugin.runCallbackFunction('beforeChange');
                // fire function depending on which mode is set
                plugin.switchAnimationType(element_id, content_container);
            });
        },

        switchTrigger: function () {
            // set some vars
            var plugin = this,
                $this = plugin.$this,
                nav_element = $this.find('.tabsNav');

            switch (plugin.settings.mode) {
                case 'accordion':
                    plugin.accordionTrigger(nav_element);
                break;
                case 'select':
                    plugin.selectTrigger(nav_element);
                break;
                default:
                    plugin.tabTrigger(nav_element);
                break;
            }
        },

        switchAnimationType: function (element_id, content_container) {
            // set some vars
            var plugin = this;

            // force accordion animation_type to slide unless otherwise specified
            if (content_container.find('.accordionWrap').length) {
                this.settings.animation_type = 'slide';
            }

            switch (plugin.settings.animation_type) {
                case 'slide':
                    plugin.slide(element_id, content_container);
                break;
                default:
                    // fade is the default
                    plugin.fade(element_id, content_container);
                break;
            }
        },

        slide: function (element_id, content_container) {
            // set some vars
            var plugin = this;
            // slideFade all siblings
            content_container
                .find('.open').removeClass('open')
                .stop(true, true)
                .slideFadeToggle(plugin.settings.speed, plugin.settings.easing, function () {
                    // slideFade clicked element
                    content_container
                        .find('[data-tab-id="' + element_id + '"]')
                        .stop(true, true)
                        .slideFadeToggle(plugin.settings.speed, plugin.settings.easing)
                        .addClass('open');

                        // run afterChange function
                        plugin.runCallbackFunction('afterChange', element_id);
                });

            plugin.runCallbackFunction('onChange');
        },

        fade: function (element_id, content_container) {
            // set some vars
            var plugin = this;

            // hide open content
            content_container
                .find('.open')
                .removeClass('open')
                .hide();

            // fade in clicked element
            content_container
                .find('[data-tab-id="' + element_id + '"]')
                .stop(true, true)
                .fadeIn(plugin.settings.speed, plugin.settings.easing, function () {
                        // run afterChange function
                        plugin.runCallbackFunction('afterChange', element_id);
                        window.setTimeout(function(){
                            $(window).trigger('resize')
                        },10);
                })
                .addClass('open');

                plugin.runCallbackFunction('onChange');
        },

        getActive: function () {
            // set some vars
            var plugin = this,
                active = plugin.$this.find('.open');

            // determine the mode and change how the active element its found
            switch (plugin.settings.mode) {
                case 'accordion':
                    return plugin.active_index = plugin.$this.find('.open').parent('.accordionWrap').index();
                default:
                    // tabs and select have same markup
                    return plugin.active_index = plugin.$this.find('.open').index();
            }
        },

        runCallbackFunction: function (functionName, element_id) {
            // set some vars
            var plugin = this;
            // check if a function and run it
            if (typeof plugin.settings[functionName] === 'function') {
                plugin.settings[functionName](element_id);
            }
        },

        destroy: function (mode) {
            // set some vars
            var plugin = this,
                $this = plugin.$this,
                content_container = $this.find(plugin.settings.content_selector),
                current_mode = plugin.settings.mode;

            switch (mode) {
                case 'navigation':
                    $this.find('.tabsNav a, .tabsNav select').off('click change');
                    $this.find('.tabsNav').remove();
                    break;
                case 'accordion':
                    content_container.unwrap();
                    break;
                case 'select':
                    $this.find('.tabsNav select').chosen('destroy');
                    break;
                default:
                    // remove data-tab-id added by plugin
                    content_container.removeAttr('data-tab-id style');
                    // remove plugin data from trigger
                    $this.removeData('plugin_' + plugin_name);
                    // destroy navigation
                    plugin.destroy('navigation');
                    /* if current mode does not equal tabs then we need
                     * to run the destroy for that mode
                     */
                    if (current_mode !== 'tabs') {
                        plugin.destroy(current_mode);
                    }
                    // unwrap inner content
                    content_container.unwrap();
                    // remove intialised class
                    $this.removeClass('mtcTabsInitialised');

                    $(window).off('resize.mtcTabs');

                    plugin.runCallbackFunction('onDestroy');
                    break;
            }
        }
    });

    // A really lightweight plugin wrapper around the constructor,
    // preventing against multiple instantiations
    $.fn[plugin_name] = function (options) {
        return this.each(function () {
            var plugin, _name;
            plugin = $.data(this, 'plugin_' + plugin_name);

            if (typeof options === 'string') {
                if (plugin !== null) {
                    if (typeof plugin[_name = options] === 'function') {
                        return plugin[_name]();
                    } else {
                        return void 0;
                    }
                } else {
                    return void 0;
                }
            } else if (!plugin) {
                $.data(this, 'plugin_' + plugin_name, new Plugin(this, options));
            }
        });
    };

}(jQuery, window, document));
