(function(context,$) {

  "use strict";

  context.JK = context.JK || {};
  context.JK.SessionList = function(app) {
    var EVENTS = context.JK.EVENTS;
    var gearUtils = context.JK.GearUtils;
    var sessionUtils = context.JK.SessionUtils;
    var helpBubble = context.JK.HelpBubbleHelper;
    var logger = context.JK.logger;
    var rest = context.JK.Rest();
    var ui = new context.JK.UIHelper(app);
    var $activeSessionTemplate = $('#template-active-session-row');
    var $inactiveSessionTemplate = $('#template-inactive-session-row');
    var $notationFileTemplate = $('#template-notation-files');
    var $openSlotsTemplate = $('#template-open-slots');
    var $latencyTemplate = $('#template-latency');
    var $musicianTemplate = $('#template-musician-info');
    var showJoinLink = true;
    var showListenLink = true;
    var MAX_MINUTES_SHOW_START = 15;

    // related to listen
    function stateChange(e, data) {
      var $listenLink = data.element;
      var $parent = $listenLink.closest('.action-links')
      //console.log(e,$(this))
      var $listenText = $('.listen-link-text', $parent);
      var $listenDetails = $('.listen-link-details', $parent);

      if(data.displayText)
      {
        if(data.state == 'playing') {
          $listenText.text('Stop Listening')
        }
        else {
          $listenText.text(context.JK.toTitleCase(data.displayText))
          $listenDetails.addClass('statusing')
        }
      }

      if(data.isEnd) {
        //$listenDetails.removeClass('statusing')
        //$listenText.text('Listen')
        stopPlay($listenLink);
      }

      if(data.isSessionOver) {
        $listenLink.removeClass('inprogress').addClass('ended')
      }
    }

    function startPlay($listenLink) {
      $listenLink.find('img').attr('src', '/assets/content/pause-icon.jpg');
      $listenLink.trigger('play.listenBroadcast');
    }

    function stopPlay($listenLink) {
      $listenLink.find('img').attr('src', '/assets/content/listen-icon.jpg');
      $listenLink.trigger('pause.listenBroadcast');
    }

    function togglePlay() {
      var $listenLink = $(this)
      var $parent = $listenLink.closest('.action-links');
      var $listenText = $('.listen-link-text', $parent);
      var $listenDetails = $('.listen-link-details', $parent);

      if (true) {
        context.JK.Banner.showAlert('Disabled Due to High Demand', 'JamKazam has been under very high demand recently.<br/><br/>This feature is disabled while we scale up the backend servers and technology.')
      }
      else {
        if ($listenLink.data('listenbroadcast-playstate') == 'playing') {
          $listenText.text('Listen')
          $listenDetails.removeClass('statusing')
          stopPlay($listenLink);
        }
        else {
          startPlay($listenLink);
        }
      }
      return false;
    }


    function renderActiveSession(session, tbGroup, myAudioLatency) {

      var i = 0;
      var inSessionUsersHtml = '', rsvpFirst3UsersHtml = '', rsvpRemainingUsersHtml = '', openSlotsFirst3Html = '', openSlotsRemainingHtml = '', latencyInSessionHtml = '', latencyFirst3Html = '', latencyRemainingHtml = '', notationFileHtml = '';

      // this is used to track which users are already in the session so we can exclude them from the
      // "RSVPs" section
      var inSessionUsers = [];

      showJoinLink = session.musician_access;
      showListenLink = session.fan_access && session.active_music_session && session.active_music_session.mount;

      // render musicians who are already in the session
      if (session.active_music_session && "participants" in session.active_music_session && session.active_music_session.participants.length > 0) {
        for (i=0; i < session.active_music_session.participants.length; i++) {
          inSessionUsers.push(session.active_music_session.participants[i].user.id);
          var inSessionUserInfo = createInSessionUser(session.active_music_session.participants[i]);
          inSessionUsersHtml += inSessionUserInfo[0];
          latencyInSessionHtml += inSessionUserInfo[1];
        }
      }
      // this provides a buffer at the top to shift the first latency tag down in the event there are NO in-session musicians
      else {
        latencyInSessionHtml += "<div style='height:15px;'>&nbsp;</div>";
      }

      // render users who have approved RSVPs
      if (session.approved_rsvps) {
        var approvedRsvpCount = session.approved_rsvps.length;
        for (i=0; i < approvedRsvpCount; i++) {
          // do not show the user in this section if he is already in the session
          if ($.inArray(session.approved_rsvps[i].id, inSessionUsers) === -1) {
            if (session.approved_rsvps[i].id === context.JK.currentUserId) {
              showJoinLink = true;
            }
            var rsvpUserInfo = createRsvpUser(session.approved_rsvps[i], session, approvedRsvpCount, i);
            if (i < 3) {
              rsvpFirst3UsersHtml += rsvpUserInfo[0];
              latencyFirst3Html += rsvpUserInfo[1];
            }
            else {
              rsvpRemainingUsersHtml += rsvpUserInfo[0];
              latencyRemainingHtml += rsvpUserInfo[1];
            }
          }
          else {
            showJoinLink = true;
          }
        }
      }

      // render if anyone interested
      if(session['is_unstructured_rsvp?']) {
        openSlotsFirst3Html += sessionUtils.createOpenSlot($openSlotsTemplate, {description: 'Any Instrument'});
      }

      // render open slots
      if (session.open_slots) {
        var openSlotCount = session.open_slots.length;
        for (i=0; i < openSlotCount; i++) {
          if (i < 3) {
            openSlotsFirst3Html += sessionUtils.createOpenSlot($openSlotsTemplate, session.open_slots[i], openSlotCount, i);
          }
          else {
            openSlotsRemainingHtml += sessionUtils.createOpenSlot($openSlotsTemplate, session.open_slots[i], openSlotCount, i);
          }
        }
      }

      // notation files
      if (session.music_notations) {
        for (i=0; i < session.music_notations.length; i++) {
          notationFileHtml += createNotationFile(session.music_notations[i]);
        }
      }

      var sessionVals = buildSessionObject(session, notationFileHtml, rsvpFirst3UsersHtml, rsvpRemainingUsersHtml, openSlotsFirst3Html, openSlotsRemainingHtml, latencyFirst3Html, latencyRemainingHtml, latencyInSessionHtml);
      sessionVals.in_session_musicians = inSessionUsersHtml.length > 0 ? inSessionUsersHtml : 'N/A';
      sessionVals.join_link_display_style = showJoinLink ? "block" : "none";
      sessionVals.listen_link_display_style = showListenLink ? "inline-block" : "none";
      if (!session.fan_access) {
        sessionVals.listen_link_text = '';
      }
      else if (session.active_music_session && session.active_music_session.mount) {
        sessionVals.listen_link_text = 'Listen';
      }
      else {
        sessionVals.listen_link_text = '';
      }



      var $row = $(context.JK.fillTemplate($activeSessionTemplate.html(), sessionVals));
      var $offsetParent = $(tbGroup).closest('.content');
      var $latencyBadges = $row.find('.latency-value');
      context._.each($latencyBadges, function(latencyBadge) {
        var $latencyBadge = $(latencyBadge);
        var full_score = $latencyBadge.attr('data-full-score') || null;
        var internet_score = $latencyBadge.attr('data-internet-score') || null;
        var audio_latency = $latencyBadge.attr('data-audio-latency') || null;
        var latencyBadgeUserId = $latencyBadge.attr('data-user-id');
        var scoreOptions = {offsetParent: $offsetParent};
        helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, myAudioLatency, audio_latency, internet_score, scoreOptions);
      });

      $(tbGroup).append($row);

      var $parentRow = $('tr[data-session-id=' + session.id + ']', tbGroup);

      // wire up "more" links
      $('a.more.slots', $parentRow).click(toggleSlots);
      $('a.more.rsvps', $parentRow).click(toggleRsvps);

        $('.notation-link').click(function(evt) {
            rest.getMusicNotation($(this).attr('data-notation-id'))
                .done(function(result) {
                    window.open(result, '_blank');
                })
                .fail(function(xhr, textStatus, errorMessage) {
                    if (xhr.status === 403) {
                        app.ajaxError(xhr, textStatus, errorMessage);
                    }
                });
        });

      // enable listen button

      if(showListenLink)  {
        var $listenLink = $('.listen-link', $parentRow)
        var $listenText = $('.listen-link-text', $parentRow)
        var $listenDetailHover = $('.listen-detail-hover', $parentRow)
        $listenLink.parent().hoverIntent({
          over: function() {
            $listenDetailHover.show();
          },
          out: function() {
            $listenDetailHover.hide();
          }
        })
        $listenLink.attr('data-music-session', session.id).attr('fan-access', session.fan_access).attr('data-audio-src', session.active_music_session.mount.url).attr('data-audio-type', session.active_music_session.mount.mime_type)
        $listenLink.listenBroadcast({lazyAudioInit:true, hoverOptions: {offsetParent: $offsetParent}, detailHelper: $listenDetailHover});
        $listenLink.bind('statechange.listenBroadcast', stateChange);
        $listenLink.click(togglePlay);
      }

      if (showJoinLink) {
        // wire up the Join Link to the T&Cs dialog
        $('.join-link', $parentRow).click(function(evt) {
          sessionUtils.ensureValidClient(app, gearUtils, function() {
            sessionUtils.joinSession(session.id);
          });
        });
      }
    }

    function renderInactiveSession(session, tbGroup, $rowToUpdate, myAudioLatency) {

      var openSlots = false;
      var hasInvitation = false;
      var approvedRsvpId = null; // if set, the user has an accepted RSVP
      var pendingRsvpId = null; // if set, the user has a pending RSVP
      var hasPendingOrDeclinedRsvp = false;
      var openRsvps = session.open_rsvps;

      var i = 0;
      var rsvpFirst3UsersHtml = '', rsvpRemainingUsersHtml = '', openSlotsFirst3Html = '', openSlotsRemainingHtml = '', latencyFirst3Html = '', latencyRemainingHtml = '', notationFileHtml = '';

      context._.each(session.pending_rsvp_requests, function(pending_rsvp_request) {
          if(pending_rsvp_request.user_id === context.JK.currentUserId) {
            pendingRsvpId = pending_rsvp_request.id;
          }
      });

      // render users who have approved RSVPs
      if (session.approved_rsvps && session.approved_rsvps.length > 0) {
        var approvedRsvpCount = session.approved_rsvps.length;
        context._.each(session.approved_rsvps, function(approved_rsvp) {
          if (approved_rsvp.id === context.JK.currentUserId) {
            approvedRsvpId = approved_rsvp.rsvp_request_id;
          }

          var rsvpUserInfo = createRsvpUser(approved_rsvp, session, approvedRsvpCount, i);

          if (i < 3) {
            rsvpFirst3UsersHtml += rsvpUserInfo[0];
            latencyFirst3Html += rsvpUserInfo[1];
          }
          else {
            rsvpRemainingUsersHtml += rsvpUserInfo[0];
            latencyRemainingHtml += rsvpUserInfo[1];
          }

          i++;
        });
      }
      // this provides a buffer at the top to shift the first latency tag down in the event there are NO RSVP musicians
      else {
        latencyFirst3Html += "<div style='height:15px;'>&nbsp;</div>";
      }

      if(session['is_unstructured_rsvp?']) {
        openSlots = true; // unstructured RSVP means there are always open slots
        openSlotsFirst3Html += sessionUtils.createOpenSlot($openSlotsTemplate, {description: 'Any Instrument'});
      }

      // render open slots
      if (session.open_slots) {
        var openSlotCount = session.open_slots.length;
        for (i=0; i < openSlotCount; i++) {
          openSlots = true;

          if (i < 3) {
            openSlotsFirst3Html += sessionUtils.createOpenSlot($openSlotsTemplate, session.open_slots[i], openSlotCount, i);
          }
          else {
            openSlotsRemainingHtml += sessionUtils.createOpenSlot($openSlotsTemplate, session.open_slots[i], openSlotCount, i);
          }
        }
      }

      // render pending invitations
      if (session.pending_invitations) {
        for (i=0; i < session.pending_invitations.length; i++) {
          if (session.pending_invitations[i].id === context.JK.currentUserId) {
            hasInvitation = true;
          }
        }
      }

      // notation files
      if (session.music_notations) {
        for (i=0; i < session.music_notations.length; i++) {
          notationFileHtml += createNotationFile(session.music_notations[i]);
        }
      }

      var sessionVals = buildSessionObject(session, notationFileHtml, rsvpFirst3UsersHtml, rsvpRemainingUsersHtml, 
        openSlotsFirst3Html, openSlotsRemainingHtml, latencyFirst3Html, latencyRemainingHtml, '');

      sessionVals.scheduled_start = session.pretty_scheduled_start_with_timezone;

      var $row = $(context.JK.fillTemplate($inactiveSessionTemplate.html(), sessionVals));

      var $offsetParent = $(tbGroup).closest('.content');
      var $latencyBadges = $row.find('.latency-value');
      context._.each($latencyBadges, function(latencyBadge) {
        var $latencyBadge = $(latencyBadge);
        var full_score = $latencyBadge.attr('data-full-score') || null;
        var internet_score = $latencyBadge.attr('data-internet-score') || null;
        var audio_latency = $latencyBadge.attr('data-audio-latency') || null;
        var latencyBadgeUserId = $latencyBadge.attr('data-user-id');
        var scoreOptions = {offsetParent: $offsetParent};
        helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, myAudioLatency, audio_latency, internet_score, scoreOptions);
      })


      // initial page load
      if (!$rowToUpdate) {
        $(tbGroup).append($row);
      }
      // inline update after an RSVP submission / cancellation
      else {
        $rowToUpdate.replaceWith($row);
      }
      
      var $parentRow = $('tr[data-session-id=' + session.id + ']', tbGroup);

      // wire up "more" links
      $('a.more.slots', $parentRow).click(toggleSlots);
      $('a.more.rsvps', $parentRow).click(toggleRsvps);

      var showRsvpLink = true;
      var sessionLinkText = '';
      $('.rsvp-link-text', $parentRow).hide();

      function showStartSessionButton(scheduledStart) {
        var now = new Date();
        var scheduledDate = new Date(scheduledStart);
        var minutesFromStart = (scheduledDate.getTime() - now.getTime()) / (1000 * 60);
        return minutesFromStart <= MAX_MINUTES_SHOW_START;
      };

      if (session.creator.id === context.JK.currentUserId) {
        showRsvpLink = false;
        sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
        sessionLinkText.find('a').click(function() {
          ui.launchSessionStartDialog(session);
          return false;
        });
      }
      else if (approvedRsvpId) {
        showRsvpLink = false;

        if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
          sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a>&nbsp;|&nbsp;<a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
          sessionLinkText.find('a.start').click(function() {
            ui.launchSessionStartDialog(session);
            return false;
          });
        }
        else {
          sessionLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
        }

        // wire cancel link
        sessionLinkText.find('a.cancel').click(function() {
          ui.launchRsvpCancelDialog(session.id, approvedRsvpId)
            .one(EVENTS.RSVP_CANCELED, function() {
              rest.getSessionHistory(session.id)
                .done(function(response) {
                  renderInactiveSession(response, tbGroup, $parentRow, myAudioLatency);
                });
            })
            .one(EVENTS.DIALOG_CLOSED, function() {
              $(this).unbind(EVENTS.RSVP_CANCELED);
            });
            return false;
        });
      }
      else if (hasInvitation) {
        showRsvpLink = false;

        if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
          sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
          sessionLinkText.find('a').click(function() {
            ui.launchSessionStartDialog(session);
            return false;
          });
        }
      }
      else if (pendingRsvpId) {
        showRsvpLink = false;
        sessionLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
        sessionLinkText.find('a').click(function() {
          ui.launchRsvpCancelDialog(session.id, pendingRsvpId)
            .one(EVENTS.RSVP_CANCELED, function() {
              rest.getSessionHistory(session.id)
                .done(function(response) {
                  renderInactiveSession(response, tbGroup, $parentRow, myAudioLatency);
                });
            })
            .one(EVENTS.DIALOG_CLOSED, function() {
              $(this).unbind(EVENTS.RSVP_CANCELED);
            });
            return false;
        });
      }
      else if (!openSlots) {
        showRsvpLink = false;
        sessionLinkText = '<span class="text">No more openings in this session.</span>';
      }
      else if (!openRsvps && !hasInvitation) {
        showRsvpLink = false;
        sessionLinkText = '<span class="text">You need an invitation to RSVP to this session.</span>';
      }
      
      if (showRsvpLink) {
        $('.rsvp-msg', $parentRow).hide();
        $('.rsvp-link', $parentRow).show();
        $('.rsvp-link-text', $parentRow).show();

        $('.rsvp-link', $parentRow).click(function(evt) {
          ui.launchRsvpSubmitDialog(session.id)
            .one(EVENTS.RSVP_SUBMITTED, function() {
              rest.getSessionHistory(session.id)
                .done(function(response) {
                  renderInactiveSession(response, tbGroup, $parentRow, myAudioLatency);
                });
            })
            .one(EVENTS.DIALOG_CLOSED, function() {
              $(this).unbind(EVENTS.RSVP_SUBMITTED);
            });
          return false;
        });
      }
      else {
        $('.rsvp-msg', $parentRow).html(sessionLinkText).show();
        $('.rsvp-link', $parentRow).hide();
      }
    }

    function buildSessionObject(session, notationFileHtml, rsvpFirst3UsersHtml, rsvpRemainingUsersHtml, openSlotsFirst3Html, openSlotsRemainingHtml, latencyFirst3Html, latencyRemainingHtml, latencyInSessionHtml) {
      return {
        id: session.id,
        name: session.name,
        description: session.description || "(No description)",
        notation_files: notationFileHtml.length > 0 ? notationFileHtml : 'N/A',
        genres: session.genres.join (', '),
        rsvp_musicians_first_3: rsvpFirst3UsersHtml.length > 0 ? rsvpFirst3UsersHtml : 'N/A',
        rsvp_musicians_remaining: rsvpRemainingUsersHtml.length > 0 ? rsvpRemainingUsersHtml : 'N/A',
        open_slots_first_3: openSlotsFirst3Html.length > 0 ? openSlotsFirst3Html : 'No slots available',
        open_slots_remaining: openSlotsRemainingHtml.length > 0 ? openSlotsRemainingHtml : 'No slots available',
        latency_first_3: latencyFirst3Html,
        latency_remaining: latencyRemainingHtml,
        latency_in_session: latencyInSessionHtml,
        language: session.language_description,
        musician_access: session.musician_access_description,
        fan_access: session.fan_access_description,
        legal_policy: session.legal_policy
      };
    }

    function createInSessionUser(participant) {
      var instrumentLogoHtml = '';
      var j;

      var existingTracks = [];

      // loop through the tracks to get the instruments
      for (j=0; j < participant.tracks.length; j++) {
        var track = participant.tracks[j];
        if (existingTracks.indexOf(track.instrument_id) < 0) {
          existingTracks.push(track.instrument_id);
          logger.debug("Find:Finding instruments. Participant tracks:", participant.tracks);
          var inst = context.JK.getInstrumentIcon24(track.instrument_id);
          instrumentLogoHtml += '<img title="' + context.JK.getInstrumentId(track.instrument_id) + '" hoveraction="instrument" data-instrument-id="' + track.instrument_id + '" src="' + inst + '" width="24" height="24" />&nbsp;';
        }
      }

      var id = participant.user.id;
      var name = participant.user.name;
      var musicianVals = {
        userId: id,
        avatar_url: context.JK.resolveAvatarUrl(participant.user.photo_url),
        profile_url: "/client#/profile/" + id,
        musician_name: name,
        instruments: instrumentLogoHtml,
        more_link: ''
      };

      var musicianHtml = context.JK.fillTemplate($musicianTemplate.html(), musicianVals);
      var latencyHtml = context._.template($latencyTemplate.html(), $.extend(sessionUtils.createLatency(participant.user), participant.user), { variable: 'data' });

      return [musicianHtml, latencyHtml];
    }

    function createRsvpUser(user, session, rsvpCount, currentIndex) {

      var instrumentLogoHtml = '';

      var j;

      // loop through the tracks to get the instruments
      if ("instrument_list" in user) {
        for (j=0; j < user.instrument_list.length; j++) {
          var instrument = user.instrument_list[j];
          var inst = context.JK.getInstrumentIcon24(instrument.id);
          instrumentLogoHtml += '<img title="' + context.JK.getInstrumentId(instrument.id) + '" hoveraction="instrument" data-instrument-id="' + instrument.id + '" src="' + inst + '" width="24" height="24" />&nbsp;';
        }
      }

      var moreLinkHtml = '';
      if (rsvpCount > 3 && currentIndex === 2) {
        moreLinkHtml = '<a class="rsvps more">more</a><a class="details-arrow arrow-down-orange"></a>';
      }

      var id = user.id;
      var name = user.name;
      var musicianVals = {
        userId: id,
        avatar_url: context.JK.resolveAvatarUrl(user.photo_url),
        profile_url: "/client#/profile/" + id,
        musician_name: name,
        instruments: instrumentLogoHtml,
        more_link: moreLinkHtml
      };

      var musicianHtml = context.JK.fillTemplate($musicianTemplate.html(), musicianVals);
      var latencyHtml = context._.template($latencyTemplate.html(), $.extend(sessionUtils.createLatency(user), user), { variable: 'data' });

      return [musicianHtml, latencyHtml];
    }

    function createNotationFile(notation) {
      var notationVals = {
        notation_id: notation.id,
        file_url: notation.viewable ? notation.file_url + '?target=_blank' : '#',
        file_name: notation.file_name,
        link_class: notation.viewable ? '' : 'notation-link'
      };

      return context.JK.fillTemplate($notationFileTemplate.html(), notationVals);
    }

    function toggleSlots() {
      var $div = $(this).closest('table').next();
      var $arrow = $(this).next();

      if ($div.is(":visible")) {
        $(this).text('more');
        $arrow.removeClass('arrow-up-orange');
        $arrow.addClass('arrow-down-orange');
        $div.hide();
      }
      else {
        $(this).text('less');
        $arrow.addClass('arrow-up-orange');
        $arrow.removeClass('arrow-down-orange');
        $div.show();
      }
    }

    function toggleRsvps() {
      var sessionId = $(this).closest('tr[data-session-id]').attr('data-session-id');

      // musicians
      var $musicians = $(this).closest('table').next();

      // latency indicators that need expanding / collapsing
      var $extraLatencies = $("#latency-extra-" + sessionId);

      var $arrow = $(this).next();

      if ($musicians.is(":visible")) {
        $(this).text('more');
        $arrow.removeClass('arrow-up-orange');
        $arrow.addClass('arrow-down-orange');
        $musicians.hide();
        $extraLatencies.hide();
      }
      else {
        $(this).text('less');
        $arrow.addClass('arrow-up-orange');
        $arrow.removeClass('arrow-down-orange');
        $musicians.show();
        $extraLatencies.show();
      }
    }

    this.renderActiveSession = renderActiveSession;
    this.renderInactiveSession = renderInactiveSession;

    return this;
}})(window,jQuery);