/**
 * slideshow
 *
 * @description Provides an interface to create a CND slideshow application
 *              instance. Assumes related content (items in a list). This is
 *              specifically for setting up the List page to have a slideshow
 *              and eventually perhaps a slideshow dynamic feature, but it is
 *              bound to the idea of item/list relationships, not for use with
 *              mediaItems or other slideshow-based applications.
 * @version     1.2.4
 * @requires    jquery.js, cn.js, cn.schemaparser.js, cn.slideshow.js
 * @example     $('#items-container').slideshow({ // To initialize
 *                  useIntro : true,
 *                  useHistory : true
 *              });
 *              api = $('#items-container').slideshow(); // To get the API
 * @copyright   2010 Conde Nast Digital. All Rights Reserved.
 * @author      Eric Shepherd
 * @date        April 2010
 * @history     0.9.1       EBS Beta
 *              1.0.0       EBS Production
 *              1.0.1       EBS Added support for legacy photo schema, if mediaItems are not available
 *              1.1.0       EBS Using new preloading API from cnp.slideshow.js
 *              1.1.1       EBS Wrapped success callback in timeout so observers fire only after all clients are subscribed
 *              1.2.0       EBS Removed timer (use CN.Observer), using new paths for most plugin includes, firing custom event when done
 *              1.2.1       EBS Fixed typos in plugin loading method
 *              1.2.2       EBS Changed path to dependency on cn-fe-content for pluckhelper
 *              1.2.3       EBS Changed path to pluck3 directory for pluckhelper
 *              1.2.4        RC Added failsafe thumbnail when it's not delivered by schema parser
 */

/*global jQuery, CN, location */ /* for jsLint */

(function($) {

    var buildDom,
        loadPlugins,
        initPluckHelper,
        initSlideshow,
        buildParams,
        addSlides,
        configureScrollPane,
        Slideshow,
        pluginsLoaded = [];

    /*********************
       jQuery namespace
    *********************/
    $.slideshow = {
        version : '1.2.4',
        conf    : {
            contentId             : CN.site.contentId,       // The list that contains the item relationships
            initClass             : 'slideshow-initialized', // Init class
            itemsClassSuffix      : 'items',                 // Passed to slideshow and attached to "slideshow-" classname
            itemClassSuffix       : 'item',                  // Passed to slideshow and attached to "slideshow-" classname
            itemsElement          : '.items',                // Used if we are recycling the existing dom instead of creating a new element
            transition            : 'photoflash',            // Default transition for the main slideshow
            showInterstitialTimer : 5,                       // How long to show the interstitial in seconds
            playTimer             : 5,                       // How long to play each slide in seconds
            useHistory            : false,                   // Whether or not to use the history plugin
            useIntro              : false,                   // Whether or not to show a separate intro page
            plugins               : [],                      // Which plugins to use (array of plugin names)
            api                   : false,                   // Whether to return the API
            global                : false,                   // Whether to publicly expose the slideshow in global var CN.page.slideshow
            usePhotoSchema        : false,                   // Provides legacy support for photo schema rather than mediaItems
            onlyPreload           : false,                   // Indicates whether to preload images (false) or how many to preload (number)
            notify                : false                    // Optional custom event name to fire when api is ready
        }
    };

    /**
     * Constructs a CN.slideshow instance with the provided configuration
     * @constructor
     * @param       $el  {jQuery} A jQuery element in which to build a slideshow
     * @param       conf {Object} Configuration overrides for this slideshow instance
     */
    Slideshow = function($el, conf) {
        this.slideshow = null; // API slideshow instance
        this.commands  = null; // API commands object

        var that = this;

        buildDom($el, conf);

        loadPlugins(conf);

                // Builds the slideshow
        this.slideshow = new CN.slideshow.Slides($el, {
            transition            : conf.transition,
            showInterstitialTimer : conf.showInterstitialTimer,
            playTimer             : conf.playTimer,
            useHistory            : conf.useHistory,
            useIntro              : conf.useIntro
        });

                // Only expose if necessary for backward compatibility
        if (conf.global) {
            CN.page.slideshow = this.slideshow;
        }

        if (conf.plugins.indexOf('pluck') !== -1) {
            initPluckHelper(this.slideshow, conf);
        }

                // Gets the slideshow data
        jQuery.ajax({
            type     : 'GET',
            url      : '/services/ajax?mainContent=false&relatedContent=true&documentId=' + conf.contentId,
            dataType : 'json',
            success  : function(json) {
                initSlideshow(that, json, $el, conf);
            },
            error : function(xhr, textStatus) {
                        // Makes sure we remove the loading stuff if data fails, so the basic HTML version is still usable
                jQuery('.slideshow-loading').fadeOut('slow'); // we don't remove, because the div is used later to overlay the controls
                jQuery('.slideshow-loading-message').fadeOut('slow');
                CN.debug.error('Data load failed for slideshow, probably 404 or malformed data: ' + textStatus);
            }
        });
    };

    /**
     * The slideshow plugin
     * @param conf {Object} Configuration for this slideshow instance
     * @return     {Mixed} Either this instance's API or the jQuery object for this instance
     */
    $.prototype.slideshow = function(conf) {
        var api = $(this).data('slideshow'),
            globals;

        if (api) {
            return api;
        }

        globals = $.extend(true, {}, $.slideshow.conf);
        conf    = $.extend(true, globals, conf);

        this.each(function() {
            $(this).data('slideshow', api = new Slideshow($(this), conf));
        });

        return conf.api ? api : this;
    };

    /*********************
       Private Functions
    *********************/

    /**
     * Creates or co-opts the necessary dom elements for the slideshow
     * TODO: This needs to be tested on a non-list page, the else might not work.
     * @param $el  {Object} The jQuery element containing the slideshow
     * @param conf {Object} This instance's configuration object
     */
    buildDom = function($el, conf) {
        if ($el.find(conf.itemsElement).length) {
            $el.find(conf.itemsElement).addClass('slideshow-' + conf.itemsClassSuffix);
        } else {
            $el.children().wrapAll('<div class="slideshow-' + conf.itemsClassSuffix + '">');
        }
    };

    /**
     * Loads the requested plugins, tracking them in the pluginsLoaded array
     * @param conf {Object} This instance's configuration object
     */
    loadPlugins = function(conf) {

        if (conf.plugins.indexOf('viewlarger') !== -1 && pluginsLoaded.indexOf('viewlarger') === -1) {
            jQuery('head').append('<script type="text/javascript" src="/js/cn-fe-slideshow/cn.slideshow.viewlarger.js"></script>');
            pluginsLoaded.push('viewlarger');
        }

        if (conf.plugins.indexOf('pluck') !== -1 && pluginsLoaded.indexOf('pluck') === -1) {
            jQuery('head').append('<script type="text/javascript" src="/js/cn-fe-content/pluck3/cn.pluckhelper.js"></script>');
            jQuery('head').append('<script type="text/javascript" src="/js/cn-fe-slideshow/pluck3/cn.slideshow.pluckhelper.js"></script>');
            pluginsLoaded.push('pluck');
        }
    };

    /**
     * Creates a pluck helper for this slideshow
     * @param slideshow  {Slides} The slideshow to create a pluck helper for
     */
    initPluckHelper = function(slideshow) {
        var pluckHelper = new CN.slideshow.plugin.PluckHelper(slideshow, '#content');

        slideshow.onSlideChangeComplete.subscribe(function() {
            pluckHelper.initNewSlide();
        });

        slideshow.onSlideshowReady.subscribe(function() {
            pluckHelper.initNewSlide(); // technically a duplicate on the first slide
        });
    };

    /**
     * Builds the initialization parameters for this slideshow,
     * accessing the page's config vars from the DCT.
     * @param $el  {Object} The jQuery element containing the slideshow
     * @return     {Object} A set of initialization parameters
     */
    buildParams = function($el) {
        var initParams = {
            goForward     : { trigger: $el.find('.next a') },
            goBackward    : { trigger: $el.find('.previous a') },
            playSlideshow : { trigger: $el.find('.start a'), sliderEl: $el.find('.stop') },
            stopSlideshow : { trigger: $el.find('.stop a') }
        };

        if (CN.page.config.viewLarger === true) {
            initParams.viewLarger = { trigger: $el.find('.viewlarger a'), transition: 'fade' };
        }

        if (CN.page.config.carousel === true) {
            initParams.carouselNavigation = { container: $el.find('#item-navigation-container') };
        }

        if (CN.page.config.viewAll === true) {
            initParams.viewAllNavigation = { trigger: $el.find('.viewall a') };
        }

        if (CN.page.config.imagelist === true) {
            initParams.listNavigation = { container: $el.find('#item-navigation-container') };
        }

        return initParams;
    };

    /**
     * Adds the given slides to the given slideshow
     * @param slideshow  {Slides} The slideshow to add slides into
     * @param items      {Array}  An array of items to add into the slideshow
     * @param conf       {Object} The config object for this instance
     */
    addSlides = function(slideshow, items, conf) {
        var i,
            il,
            thumbnail,
            photo;

        // Adds the slides into the slideshow
        for (i = 0, il = items.length; i < il; i++) {

            if (conf.usePhotoSchema) {
                photo = CN.schemaParser.getInstance().parse(items[i].item).photo;
            } else {
                photo = CN.schemaParser.getInstance().parse(items[i].item).mediaItems(null, 'photo', 0)[0];
            }

            thumbnail = CN.isUndefined(photo.thumbnail) ? '/images/default-thumbnail.gif' : photo.thumbnail();
            slideshow.append(new CN.slideshow.Slide(items[i]).setThumbnail(thumbnail));
        }
    };

    /**
     * Configures the scrollpane plugin for the given slideshow
     * @param slideshow {Slides} The slideshow to set up scrollpane for
     * @param $el       {Object} The jQuery element containing the slideshow
     */
    configureScrollPane = function(slideshow, $el) {
                // Scroll pane - these need to be set up before dataReady is called
                // onSlideshowReady only if we have a JSP-delivered first slide
        if (jQuery('html').hasClass('slideshow-showinitial-true')) {
            slideshow.onSlideshowReady.subscribe(function() {
                try {
                    $el.find('.item-inner').jScrollPane({
                        scrollbarWidth: 14,
                        showArrows: true
                    });
                } catch(e) {} // Catches occasional scrollpane plugin exception
            });
        }

        slideshow.onSlideChangeComplete.subscribe(function() {
            try {
                $el.find('.item-inner').jScrollPane({
                    scrollbarWidth: 14,
                    showArrows: true
                });
            } catch(e) {} // Catches occasional scrollpane plugin exception
        });
    };

    /**
     * Initializes the slideshow following the ajax request
     * @param that      {Object} The reference to the instance of this plugin
     * @param json      {Object} JSON from the request to use for the slideshow
     * @param $el       {Object} The jQuery element containing the slideshow
     * @param conf      {Object} This instance's configuration object
     */
    initSlideshow = function(that, json, $el, conf) {
        var items      = json.relatedContent || {},
            initParams = buildParams($el),
            commands   = that.commands = CN.slideshow.controller.init(that.slideshow, initParams);

        that.slideshow.setData(items);
        addSlides(that.slideshow, items, conf);
        configureScrollPane(that.slideshow, $el);
        CN.slideshow.util.dataReady(that.slideshow, {
            start : conf.onlyPreload ?
                        that.slideshow.currentSlideIndex + 1 : // OR drop the +1 to load one extra, the one before?
                        null,
            end   : conf.onlyPreload ?
                        that.slideshow.currentSlideIndex + conf.onlyPreload :
                        null
        });

                // Consumer marketing slide replay function
        jQuery('#slideshow-cm-replay').click(function() {
            commands.jumpToSlideCommand.execute(1);
            return false;
        });

                // Preloads the appropriate images when the slides change
        if (conf.onlyPreload) {

            that.slideshow.onForwardComplete.subscribe(function() {
                if (that.slideshow.preloadedSlides.indexOf(that.slideshow.currentSlideIndex + 2) === -1) {
                    CN.slideshow.util.preloadImages(
                        that.slideshow,
                        that.slideshow.currentSlideIndex + 2,
                        that.slideshow.currentSlideIndex + 1 + conf.onlyPreload
                    );
                }
            });

            that.slideshow.onBackwardComplete.subscribe(function() {
                if (that.slideshow.preloadedSlides.indexOf(that.slideshow.currentSlideIndex) === -1) {
                    CN.slideshow.util.preloadImages(
                        that.slideshow,
                        that.slideshow.currentSlideIndex + 1 - conf.onlyPreload,
                        that.slideshow.currentSlideIndex
                    );
                }
            });

            that.slideshow.onJumpToSlideComplete.subscribe(function() {
                if (that.slideshow.preloadedSlides.indexOf(that.slideshow.currentSlideIndex) === -1 || that.slideshow.preloadedSlides.indexOf(that.slideshow.currentSlideIndex + 2) === -1) {
                    CN.slideshow.util.preloadImages(
                        that.slideshow,
                        that.slideshow.currentSlideIndex + 1 - (parseInt(conf.onlyPreload / 2, 10)),
                        that.slideshow.currentSlideIndex + 1 + (parseInt(conf.onlyPreload / 2, 10))
                    );
                }
            });
        }

        if (conf.notify) {
            $(window).trigger(conf.notify);
        }
    };

})(jQuery);

