(function (window, document, $, bam) {

  /**
   * LiveLookIn application
   * @author David Furfero <david.furfero@mlb.com>
   */
  bam.namespace('widget').LiveLookIn = (function () {
    
    var tracking       = bam.tracking,
        cookies        = bam.cookies,
        ON_BEFORE_SEND = 'onBeforeSend',
        ON_SUCCESS     = 'onSuccess',
        ON_EMPTY       = 'onEmpty',
        ON_ERROR       = 'onError',
        ON_COMPLETE    = 'onComplete',
        self,
        poller,
        nowAvailable,
        nowPlaying,
        autoplayCookie;
    
    /**
     * Omniture tracking events
     */
    function trackImpression (medium) {
      tracking.track({
        async:{
          isDynamic    : false,
          compName     : 'Live Look-In Messenger',
          compActivity : 'Live Look-In Impression',
          actionGen    : false
        }
      });
    }
    
    function trackPlay (medium, isActionGen, linkOrigin) {
      tracking.track({
        async: {
          isDynamic:    false,
          compName:     'Live Look-In',
          compActivity: 'Live Look-In Play',
          actionGen:    isActionGen,
          linkOrigin:   isActionGen && linkOrigin
        }
      });
    }
    
    function trackEnableAutoplay () {
      tracking.track({
        async: {
          isDynamic:    false,
          compName:     'Live Look-In',
          compActivity: 'Live Look-In Auto Play Enabled',
          actionGen:    true
        }
      });
    }
    
    function trackDisableAutoplay () {
      tracking.track({
        async: {
          isDynamic:    false,
          compName:     'Live Look-In',
          compActivity: 'Live Look-In Auto Play Disabled',
          actionGen:    true
        }
      });
    }
    
    /**
     * Handle autoplay callback
     */
    function autoplay (evt, medium) {
      self.play(medium, false);
    }

    /**
     * Check cookie for user autoplay preference
     */
    function hasAutoplayEnabled () {
      return !cookies.get(autoplayCookie);
    }

    /**
     * Enable autoplay behavior
     */
    function enableAutoplay () {

      if (hasAutoplayEnabled()) {
        return;
      }

      self.bind(ON_SUCCESS, autoplay);

      cookies.remove(autoplayCookie);
      
      if (self.cfg.enableTracking) {
        trackEnableAutoplay();
      }
    }

    /**
     * Disable autoplay behavior
     */
    function disableAutoplay () {

      if (!hasAutoplayEnabled()) {
        return;
      }

      var now = new Date();

      self.unbind(ON_SUCCESS, autoplay);

      cookies.set({
        name:    autoplayCookie,
        value:   '1',
        expires: new Date(
          now.getFullYear(),
          now.getMonth(),
          now.getDate() + self.cfg.autoplayCookieDuration
        )
      });

      if (self.cfg.enableTracking) {
        trackDisableAutoplay();
      }
    }
    
    /**
     * Create autoplay toggle button
     */
    function renderAutoplayToggle () {

      var toggle = $('<a href="#"/>');
      
      /**
       * When autoplay is disabled, display "enable" text on autoplay toggle
       */
      function setToggleToEnable () {
        return toggle.addClass(self.cfg.autoplayDisabledCSS).attr('title', 'Click to enable video autoplay');
      }

      /**
       * When autoplay is enabled, display "disable" text on autoplay toggle
       */
      function setToggleToDisable () {
        return toggle.removeClass(self.cfg.autoplayDisabledCSS).attr('title', 'Click to disable video autoplay');
      }
      
      // Set initial display state
      if (hasAutoplayEnabled()) {
        setToggleToDisable();
      } else {
        setToggleToEnable();
      }

      // Add autoplay toggle class to button
      toggle.addClass(self.cfg.autoplayToggleCSS);
      
      // Clicking the button toggles autoplay enabled/disabled status
      toggle.click(function (e) {
        e.preventDefault();

        // Autoplay is disabled, re-enable and remove flag
        if (toggle.hasClass(self.cfg.autoplayDisabledCSS)) {
          enableAutoplay();
          setToggleToDisable();

        // Autoplay is enabled, disable, add flag, and close
        } else {
          disableAutoplay();
          setToggleToEnable();
        }
      });

      return toggle;
    }
    
    /**
     * Trigger onBeforeSend event
     */
    function handleBeforeSend () {
      self.trigger(ON_BEFORE_SEND);
    }

    /**
     * Parse Live Look-In data and trigger onSuccess event
     */
    function handleLoadSuccess (data) {

      var medium = data.media[0],
          wasAvailable;

      if (medium) {
        
        // Filter media by configured playback scenario
        medium = self.cfg.playback.scenario in medium.playback_scenarios && medium;

        // Apply custom filter to remaining media
        if ($.isFunction(self.cfg.grep)) {
          medium = self.cfg.grep(medium) && medium;
        }
      }

      if (medium) {

        // Only fire onSuccess event if medium is new or different
        if (!nowAvailable || nowAvailable.calendar_event_id !== medium.calendar_event_id) {

          wasAvailable = nowAvailable;
          nowAvailable = medium;

          if (self.cfg.enableTracking) {
            trackImpression(nowAvailable);
          }

          // nowAvailable passed as array for backward-compatible support
          // @todo would be nice to update all consumers to use single object
          self.trigger(ON_SUCCESS, [[nowAvailable], wasAvailable]);
        }
      } else {
        // Only fire onEmpty event if media was previously available
        if (nowAvailable) {

          wasAvailable = nowAvailable;
          nowAvailable = null;

          // The goal here is to close the player if a club stream switches
          // over to a non-club stream
          if (self.zPlayer.isPlaying()) {
            self.zPlayer.onCollapse();
          }

          self.trigger(ON_EMPTY, [wasAvailable]);
        }
      }
    }
    

    /**
     * Trigger onError event
     */
    function handleLoadError () {
      self.trigger(ON_ERROR, ['Unable to load Live Look-In data.']);
    }

    /**
     * Trigger onComplete event
     */
    function handleLoadComplete (xhr, status) {
      self.trigger(ON_COMPLETE, [status]);
    }

    /**
     * Public API
     */
    self = {
      
      /**
       * Initialize
       */
      init: function (cfg) {

        /**
         * LiveLookIn configuration
         */
        self.cfg = $.extend(true, {
          
          // Data
          url:                    '/gen/multimedia/livelookin.json',
          
          // Polling
          autostart:              false,
          interval:               15000,
          
          // Filtering
          grep:                   null,
          
          // Tracking
          enableTracking:         true,
          
          // Autoplay
          autoplay:               true,
          autoplayCookiePrefix:   'LLI_',
          autoplayCookieDuration: 30,
          autoplayToggleCSS:      'autoplay',
          autoplayDisabledCSS:    'autoplay-disabled',
          
          // Playback
          playback: {
            scenario:             'FLASH_800K_640X360',
            bitRate:              '800K'
          },
          
          // Custom zPlayer configuration (see bam.zPlayer.js for more)
          zPlayer: {
            containerId:          'LiveLookinPlayer',
            playerObjName:        'bam.widget.LiveLookIn.zPlayer.flashPlayer',
            replayImgUrl:         '/images/000000.gif',
            bgImageUrl:           '/scripts/lookin/bg.png',
            cssUrl:               '/scripts/lookin/lookin.css',
            redirectBadBrowsers:  false
          }
        }, cfg);
        
        // Create a poller to load Live Look-In data at a specified interval
        poller = new bam.util.PeriodicalExecuter(self.load, self.cfg.interval, !self.cfg.autostart);
        
        // Initialize Live Look-In zPlayer 
        self.zPlayer = (new bam.ZPlayer(self.cfg.zPlayer))
          // Add autoplay toggle button to zPlayer
          .afterInit(function () {
            // renderAutoplayToggle().insertAfter('#' + self.cfg.zPlayer.containerId + ' .zPlayerClipInfo a');
            $('#' + self.cfg.zPlayer.containerId).find('a.zPlayerCloseBtn').after(renderAutoplayToggle());
          })
          // When the zPlayer collapses, force a refresh of Live Look-In data
          .onCollapsePlayer(self.restart);

        // Store separate cookies for MLB.com and club.com
        autoplayCookie = self.cfg.autoplayCookiePrefix + (club === 'mlb' ? club : 'club');
        
        // Bind autoplay callback to onSuccess event
        if (self.cfg.autoplay && hasAutoplayEnabled()) {
          self.bind(ON_SUCCESS, autoplay);
        }
        
        // Start polling data
        if (self.cfg.autostart) {
          self.start();
        }
      },
      
      start: function () {
        poller.start();
      },
      
      stop: function () {
        poller.stop();
      },
      
      restart: function () {
        poller.restart(true);
      },
      
      /**
       * Load
       */
      load: function () {
        $.ajax({
          url:        self.cfg.url,
          dataType:   'json',
          beforeSend: handleBeforeSend,
          success:    handleLoadSuccess,
          error:      handleLoadError,
          complete:   handleLoadComplete
        });
      },
      
      /**
       * Play
       */
      play: function (calendarEventId, isActionGen, linkOrigin) {

        // onEmpty clean-up is less than perfect, so we provide 
        if (isActionGen && (!nowAvailable || nowAvailable.calendar_event_id !== calendarEventId)) { 
          window.alert('We\'re sorry. The Live Look-In you requested has expired.');
          return;
        }

        var playback = self.cfg.playback,
            scenario = nowAvailable.playback_scenarios[playback.scenario];

        if (self.zPlayer.isPlaying()) {

          if (nowPlaying && nowPlaying.calendar_event_id !== nowAvailable.calendar_event_id) {

            self.zPlayer.updateClip(nowAvailable);

            if (self.cfg.enableTracking) {
              trackPlay(nowAvailable, isActionGen, linkOrigin);
            }
            
            nowPlaying = nowAvailable;
          }
          
        } else {
          
          self.zPlayer.actionGen = isActionGen;
                    
          self.zPlayer.play({
            content_id:        nowAvailable.content_id,
            headline:          nowAvailable.headline,
            playbackScenario:  playback.scenario,
            bitRate:           playback.bitRate,
            curVideoFlashUrl:  scenario.url,
            sequenceId:        scenario.sequence,
            cdn:               scenario.cdn
          });
         
          if (self.cfg.enableTracking) {
            trackPlay(nowAvailable, isActionGen, linkOrigin);
          }

          nowPlaying = nowAvailable;
          
        }
        
      }
    };
    
    /**
     * Implement bindable behavior with custom methods
     */
    $.bindable(self, [ON_BEFORE_SEND, ON_SUCCESS, ON_EMPTY, ON_ERROR, ON_COMPLETE].join(' '));
        
    /**
     * Handle DOM events
     */
    $('a.liveLookIn').live('click', function (e) {
      e.preventDefault();
      var elem = $(this);
      self.play(elem.attr('data-calendar-event-id'), true, elem.attr('data-link-origin'));
    });
    
    return self;

  })();

})(this, this.document, this.jQuery, this.bam);
