module JamRuby

  class Notification < ActiveRecord::Base

    @@log = Logging.logger[Notification]

    self.primary_key = 'id'

    default_scope { order('created_at DESC') }

    belongs_to :target_user, :class_name => "JamRuby::User", :foreign_key => "target_user_id"
    belongs_to :source_user, :class_name => "JamRuby::User", :foreign_key => "source_user_id"
    belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id"
    belongs_to :music_session, :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id"
    belongs_to :lesson_session, :class_name => "JamRuby::LessonSession", :foreign_key => "lesson_session_id"
    belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id"
    belongs_to :jam_track_right, :class_name => "JamRuby::JamTrackRight", :foreign_key => "jam_track_right_id"
    belongs_to :jam_track_mixdown_package, :class_name => "JamRuby::JamTrackMixdownPackage", :foreign_key => "jam_track_mixdown_package_id"

    validates :target_user, :presence => true
    validates :message, length: {minimum: 1, maximum: 400}, no_profanity: true, if: :text_message?
    validate  :different_source_target, if: :text_message?
    validate :same_school_protection
    def same_school_protection
      if source_user && target_user
        if !source_user.is_platform_instructor && !target_user.is_platform_instructor
          if source_user.school_id != target_user.school_id
            errors.add(:target_user, ValidationMessages::CAN_ONLY_CHAT_SAME_SCHOOL)
          end
        end
      end
    end

    def different_source_target
      unless target_user_id.nil? || source_user_id.nil?
        errors.add(:target_user, ValidationMessages::DIFFERENT_SOURCE_TARGET) if target_user_id == source_user_id
      end
    end

    def index(user_id)
      Notification.where(:target_user_id => user_id).limit(50)
    end

    def created_date
      self.created_at.nil? ? Time.now.getutc.iso8601.to_s : self.created_at.getutc.iso8601.to_s
    end

    def photo_url
      unless self.source_user.nil?
        self.source_user.photo_url
      end
    end

    # used for persisted notifications
    def formatted_msg
      # target_user, band, session, recording, invitation, join_request = nil
      source_user, band, session = nil

      unless self.source_user_id.nil?
        source_user = User.find(self.source_user_id)
      end

      unless self.band_id.nil?
        band = Band.find(self.band_id)
      end

      unless self.session_id.nil?
        session = MusicSession.find_by_id(self.session_id)

        # remove all notifications related to this session if it's not found
        if session.nil?
          Notification.delete_all "(session_id = '#{self.session_id}')"
        end
      end

      self.class.format_msg(self.description, {:user => source_user, target: target_user, :band => band, :session => session, purpose: purpose, student_directed: student_directed, msg: message})
    end

    # TODO: MAKE ALL METHODS BELOW ASYNC SO THE CLIENT DOESN'T BLOCK ON NOTIFICATION LOGIC
    # TODO: ADD TESTS FOR THIS CLASS

    class << self

      @@mq_router = MQRouter.new
      @@message_factory = MessageFactory.new

      ################### HELPERS ###################
      def notified?(music_session, notification_type)
        Notification.where("session_id=? AND description=?", music_session, notification_type).count != 0
      end

      def retrieve_friends(connection, user_id)
        friend_ids = []
        connection.exec("SELECT f.friend_id as friend_id FROM friendships f WHERE f.user_id = $1", [user_id]) do |friend_results|
          friend_results.each do |friend_result|
            friend_ids.push(friend_result['friend_id'])
          end
        end
        return friend_ids
      end

      def retrieve_user_followers(connection, user_id)
        follower_ids = []
        connection.exec("SELECT u.user_id as follower_id FROM follows f WHERE f.followable_id = $1", [user_id]) do |follower_results|
          follower_results.each do |follower_result|
            follower_ids.push(follower_result['follower_id'])
          end
        end
        return follower_ids
      end

      def retrieve_friends_not_in_session(connection, user_id, session_id)
        ids = retrieve_friends(connection, user_id)
        connection.exec("SELECT c.user_id as musician_id FROM connections c WHERE c.music_session_id = $1", [session_id]) do |musicians|
          musicians.each do |musician_result|
            # remove users who are in the session
            ids.reject! {|item| item == musician_result['musician_id']}
          end
        end
        return ids
      end

      def retrieve_friends_and_followers(connection, user_id)
        ids = retrieve_friends(connection, user_id)
        ids.concat(retrieve_user_followers(connection, user_id))
        ids.uniq! {|id| id}
        return ids
      end

      def retrieve_friends_and_followers_not_in_session(connection, user_id, session_id)
        ids = retrieve_friends_and_followers(connection, user_id)
        connection.exec("SELECT c.user_id as musician_id FROM connections c WHERE c.music_session_id = $1", [session_id]) do |musicians|
          musicians.each do |musician_result|
            # remove users who are in the session
            ids.reject! {|item| item == musician_result['musician_id']}
          end
        end
        return ids
      end

      def format_msg(description, options)

        user = options[:user]
        band = options[:band]
        session = options[:session]
        purpose = options[:purpose]
        student_directed = options[:student_directed]
        msg = options[:msg]

        name, band_name = ""
        unless user.nil?
          name = user.name
        else
          name = "Someone"
        end

        if !band.nil?
          band_name = band.name
        end

        case description

          # friend notifications
          when NotificationTypes::FRIEND_UPDATE
            return "#{name} is now "

          when NotificationTypes::FRIEND_REQUEST
            return "#{name} has sent you a friend request."

          when NotificationTypes::FRIEND_REQUEST_ACCEPTED
            return "#{name} has accepted your friend request."

          when NotificationTypes::NEW_USER_FOLLOWER
            return "#{name} is now following you on JamKazam."

          when NotificationTypes::NEW_BAND_FOLLOWER
            return "#{name} is now following your band #{band.name} on JamKazam."

          # session notifications
          when NotificationTypes::SESSION_INVITATION
            return "You have been invited to join a session by #{name}."

          when NotificationTypes::JOIN_REQUEST
            return "#{name} has requested to join your session."

          when NotificationTypes::JOIN_REQUEST_APPROVED
            return "#{name} has approved your request to join the session."

          when NotificationTypes::JOIN_REQUEST_REJECTED
            return "We're sorry, but you cannot join the session at this time."

          when NotificationTypes::SESSION_JOIN
            return "#{name} has joined the session."

          when NotificationTypes::SESSION_DEPART
            return "#{name} has left the session."

          when NotificationTypes::MUSICIAN_SESSION_JOIN
            return "#{name} is now in a session."

          when NotificationTypes::BAND_SESSION_JOIN
            return "#{band_name} is now in a session."

          when NotificationTypes::SCHEDULED_SESSION_INVITATION
            return "You have been invited to a future session by #{name}."

          when NotificationTypes::SCHEDULED_SESSION_RSVP
            return "#{name} would like to play in a session you have scheduled."

          when NotificationTypes::SCHEDULED_SESSION_RSVP_APPROVED
            return "Your RSVP to a scheduled session has been approved!"

          when NotificationTypes::SCHEDULED_SESSION_RSVP_CANCELLED
            return "A musician has cancelled an RSVP to your session."

          when NotificationTypes::SCHEDULED_SESSION_RSVP_CANCELLED_ORG
            return "The session organizer has cancelled your RSVP to this session."

          when NotificationTypes::SCHEDULED_SESSION_CANCELLED
            return "The session organizer has cancelled this session."

          when NotificationTypes::SCHEDULED_SESSION_RESCHEDULED
            return "The following session has been rescheduled."

          when NotificationTypes::SCHEDULED_SESSION_REMINDER_DAY
            return "A session to which you have RSVPd will begin in one hour, so get ready to play!"

          when NotificationTypes::SCHEDULED_SESSION_REMINDER_UPCOMING
            return "A session to which you have RSVPd will begin in one hour, so get ready to play!"

          when NotificationTypes::SCHEDULED_SESSION_REMINDER_IMMINENT
            return "A session to which you have RSVPd is scheduled to start in 5 minutes!"

          when NotificationTypes::SCHEDULED_SESSION_COMMENT
            return "New message about session."

          when NotificationTypes::JAM_TRACK_SIGN_COMPLETE
            return "JamTrack is ready for download."

          # recording notifications
          when NotificationTypes::MUSICIAN_RECORDING_SAVED
            return "#{name} has made a new recording."

          when NotificationTypes::BAND_RECORDING_SAVED
            return "#{band.name} has made a new recording."

          when NotificationTypes::RECORDING_STARTED
            return "#{name} has started a recording."

          when NotificationTypes::RECORDING_ENDED
            return "#{name} has stopped recording."

          when NotificationTypes::RECORDING_MASTER_MIX_COMPLETE
            return "One of your recordings has been mastered and mixed and is ready to share."

          when NotificationTypes::RECORDING_STREAM_MIX_COMPLETE
            return "One of your recordings has a stream mix available and is ready to share."

          # band notifications
          when NotificationTypes::BAND_INVITATION
            return "You have been invited to join the band #{band_name}."

          when NotificationTypes::BAND_INVITATION_ACCEPTED
            return "#{name} has accepted your band invitation to join #{band_name}."

          when NotificationTypes::LESSON_MESSAGE
            notification_msg = 'Lesson Changed'

            if purpose == 'requested'
              notification_msg = 'You have received a lesson request'
            elsif purpose == 'accept'
              notification_msg = 'Your lesson request is confirmed!'
            elsif purpose == 'declined'
              notification_msg = "We're sorry your lesson request has been declined."
            elsif purpose == 'canceled'
              notification_msg = "Your lesson request has been canceled."
            elsif purpose == 'counter'
              if student_directed
                notification_msg = "Instructor has proposed a different time for your lesson."
              else
                notification_msg = "Student has proposed a different time for your lesson."
              end
            elsif purpose == 'reschedule'
              'A lesson reschedule has been requested'
            elsif purpose == 'chat'
              notification_msg = "Lesson Message: #{msg}"
            end
            return notification_msg

          when NotificationTypes::SCHEDULED_JAMCLASS_INVITATION
            if student_directed
              "You have been scheduled to take a JamClass with #{user.name}."
            else
              "You have been scheduled to teach a JamClass to #{user.name}"
            end

          else
            return description
        end
      end

      def send_friend_update(user_id, online, connection)

        friend_ids = retrieve_friends(connection, user_id)

        unless friend_ids.empty?
          user = User.find(user_id)

          online_msg = online ? "online." : "offline."
          notification_msg = format_msg(NotificationTypes::FRIEND_UPDATE, {:user => user}) + online_msg
          msg = @@message_factory.friend_update(
            user.id,
            user.photo_url,
            online,
            notification_msg
          )

          @@mq_router.publish_to_friends(friend_ids, msg, user_id)
        end
      end

      def send_friend_request(friend_request_id, user_id, friend_id)
        user = User.find(user_id)
        friend = User.find(friend_id)

        notification = Notification.new
        notification.description = NotificationTypes::FRIEND_REQUEST
        notification.source_user_id = user_id
        notification.target_user_id = friend_id
        notification.friend_request_id = friend_request_id
        notification.save

        notification_msg = format_msg(notification.description, {:user => user})

        if friend.online?
          msg = @@message_factory.friend_request(
            friend.id,
            friend_request_id,
            user.photo_url,
            notification_msg,
            notification.id,
            notification.created_date
          )

          @@mq_router.publish_to_user(friend_id, msg)
        else
          begin
            UserMailer.friend_request(friend, notification_msg, friend_request_id).deliver_now
          rescue => e
            @@log.error("Unable to send FRIEND_REQUEST email to offline user #{friend.email} #{e}")
          end
        end
        notification
      end

      def send_friend_request_accepted(user_id, friend_id)
        friend = User.find(friend_id)
        user = User.find(user_id)

        notification = Notification.new
        notification.description = NotificationTypes::FRIEND_REQUEST_ACCEPTED
        notification.source_user_id = friend_id
        notification.target_user_id = user_id
        notification.save

        notification_msg = format_msg(notification.description, {:user => friend})

        if user.online?
          msg = @@message_factory.friend_request_accepted(
            user.id,
            friend.photo_url,
            notification_msg,
            notification.id,
            notification.created_date
          )

          @@mq_router.publish_to_user(user.id, msg)

        else
          begin
            UserMailer.friend_request_accepted(user, notification_msg).deliver_now
          rescue => e
            @@log.error("Unable to send FRIEND_REQUEST_ACCEPTED email to offline user #{user.email} #{e}")
          end
        end
      end

      def send_new_user_follower(follower, user)

        notification = Notification.new
        notification.description = NotificationTypes::NEW_USER_FOLLOWER
        notification.source_user_id = follower.id
        notification.target_user_id = user.id
        notification.save

        notification_msg = format_msg(notification.description, {:user => follower})

        if follower.id != user.id
          if user.online?
            msg = @@message_factory.new_user_follower(
              user.id,
              follower.photo_url,
              notification_msg,
              notification.id,
              notification.created_date
            )

            @@mq_router.publish_to_user(user.id, msg)

          else
            begin
              UserMailer.new_user_follower(user, notification_msg).deliver_now
            rescue => e
              @@log.error("Unable to send NEW_USER_FOLLOWER email to offline user #{user.email} #{e}")
            end
          end
        end
      end

      def send_lesson_message(purpose, lesson_session, student_directed, msg = nil)

        notification = Notification.new
        notification.description = NotificationTypes::LESSON_MESSAGE
        notification.student_directed = student_directed

        if !student_directed
          notification.source_user_id = lesson_session.student.id
          notification.target_user_id = lesson_session.teacher.id
        else
          notification.source_user_id = lesson_session.teacher.id
          notification.target_user_id = lesson_session.student.id
        end

        notification.purpose = purpose
        notification.session_id = lesson_session.music_session.id
        notification.lesson_session_id = lesson_session.id

        notification_msg = format_msg(NotificationTypes::LESSON_MESSAGE, {purpose: purpose, msg: msg})

        if purpose == 'chat'
          notification.message = msg
        end

        notification.save

        # receiver_id, sender_photo_url, sender_name, sender_id, msg, clipped_msg, notification_id, created_at
        message = @@message_factory.lesson_message(
                notification.target_user.id,
                notification.source_user.resolved_photo_url,
                notification.source_user.name,
                notification.source_user.id,
                notification_msg,
                notification.id,
                notification.session_id,
                notification.created_date,
                notification.student_directed,
                notification.purpose,
                notification.lesson_session_id
        )

        @@mq_router.publish_to_user(notification.target_user.id, message)

      end

      def send_new_band_follower(follower, band)

        band.band_musicians.each.each do |bm|
          notification = Notification.new
          notification.description = NotificationTypes::NEW_BAND_FOLLOWER
          notification.source_user_id = follower.id
          notification.target_user_id = bm.user_id
          notification.band_id = band.id
          notification.save

          notification_msg = format_msg(notification.description, {:user => follower, :band => band})

          # this protects against sending the notification to a band member who decides to follow the band
          if follower.id != bm.user.id
            if bm.user.online?

              msg = @@message_factory.new_band_follower(
                bm.user_id,
                follower.photo_url,
                notification_msg,
                notification.id,
                notification.created_date
              )

              @@mq_router.publish_to_user(bm.user_id, msg)

            else
              begin
                UserMailer.new_band_follower(bm.user, notification_msg).deliver_now
              rescue => e
                @@log.error("Unable to send NEW_BAND_FOLLOWER email to offline user #{bm.user.email} #{e}")
              end
            end
          end
        end
      end

      def send_session_invitation(receiver, sender, session_id)

        notification = Notification.new
        notification.description = NotificationTypes::SESSION_INVITATION
        notification.source_user_id = sender.id
        notification.target_user_id = receiver.id
        notification.session_id = session_id
        notification.save

        notification_msg = format_msg(NotificationTypes::SESSION_INVITATION, {:user => sender})

        if receiver.online?
          msg = @@message_factory.session_invitation(
            receiver.id,
            session_id,
            notification_msg,
            notification.id,
            notification.created_date
          )

          @@mq_router.publish_to_user(receiver.id, msg)

        else
          begin
            UserMailer.session_invitation(receiver, notification_msg).deliver_now
          rescue => e
            @@log.error("Unable to send SESSION_INVITATION email to user #{receiver.email} #{e}")
          end
        end
      end

      def send_session_ended(session_id)

        return if session_id.nil?  # so we don't query every notification in the system with a nil session_id

        notifications = Notification.where(:session_id => session_id)

        # publish to all users who have a notification for this session
        # TODO: do this in BULK or in async block
        notifications.each do |n|
          msg = @@message_factory.session_ended(n.target_user_id, session_id)
          @@mq_router.publish_to_user(n.target_user_id, msg)
        end

        Notification.delete_all "(session_id = '#{session_id}')"
      end

      def send_join_request(music_session, join_request, text)

        notification = Notification.new
        notification.description = NotificationTypes::JOIN_REQUEST
        notification.source_user_id = join_request.user.id
        notification.target_user_id = music_session.creator.id
        notification.session_id = music_session.id
        notification.save

        notification_msg = format_msg(notification.description, {:user => join_request.user})

        msg = @@message_factory.join_request(
          join_request.id,
          music_session.id,
          join_request.user.photo_url,
          notification_msg,
          notification.id,
          notification.created_date
        )

        @@mq_router.publish_to_user(music_session.creator.id, msg)
      end

      def send_join_request_approved(music_session, join_request)

        notification = Notification.new
        notification.description = NotificationTypes::JOIN_REQUEST_APPROVED
        notification.source_user_id = music_session.creator.id
        notification.target_user_id = join_request.user.id
        notification.session_id = music_session.id
        notification.save

        notification_msg = format_msg(notification.description, {:user => music_session.creator})

        msg = @@message_factory.join_request_approved(
          join_request.id,
          music_session.id,
          music_session.creator.photo_url,
          notification_msg,
          notification.id,
          notification.created_date
        )

        @@mq_router.publish_to_user(join_request.user.id, msg)
      end

      def send_join_request_rejected(music_session, join_request)

        notification = Notification.new
        notification.description = NotificationTypes::JOIN_REQUEST_REJECTED
        notification.source_user_id = music_session.creator.id
        notification.target_user_id = join_request.user.id
        notification.session_id = music_session.id
        notification.save

        notification_msg = format_msg(notification.description, {:user => music_session.creator})

        msg = @@message_factory.join_request_rejected(
          join_request.id,
          music_session.id,
          music_session.creator.photo_url,
          notification_msg,
          notification.id,
          notification.created_date
        )

        @@mq_router.publish_to_user(join_request.user.id, msg)
      end

      def send_session_join(active_music_session, connection, user)

        notification_msg = format_msg(NotificationTypes::SESSION_JOIN, {:user => user})

        sessionMsg = @@message_factory.session_join(
          active_music_session.id,
          user.photo_url,
          user.id,
          notification_msg,
          active_music_session.track_changes_counter,
          connection.client_id,
          MessageFactory::CLIENT_TARGET
        )

        userMsg = @@message_factory.session_join(
            active_music_session.id,
            user.photo_url,
            user.id,
            notification_msg,
            active_music_session.track_changes_counter,
            connection.client_id,
            MessageFactory::USER_TARGET_PREFIX + user.id
        )

        @@mq_router.server_publish_to_session(active_music_session, sessionMsg, sender = {:client_id => connection.client_id})
        @@mq_router.publish_to_user(user.id, userMsg, sender = {:client_id => connection.client_id})
      end

      def send_session_depart(active_music_session, client_id, user, recordingId)

        notification_msg = format_msg(NotificationTypes::SESSION_DEPART, {:user => user})

        msg = @@message_factory.session_depart(
          active_music_session.id,
          user.photo_url,
          notification_msg,
          recordingId,
          active_music_session.track_changes_counter,
          client_id,
          user.id,
          MessageFactory::CLIENT_TARGET
        )

        userMsg = @@message_factory.session_depart(
            active_music_session.id,
            user.photo_url,
            notification_msg,
            recordingId,
            active_music_session.track_changes_counter,
            client_id,
            user.id,
            MessageFactory::USER_TARGET_PREFIX + user.id
        )

        @@mq_router.server_publish_to_session(active_music_session, msg, sender = {:client_id => client_id})
        @@mq_router.publish_to_user(user.id, userMsg, sender = {:client_id => client_id})

      end

      def send_tracks_changed(active_music_session)
        msg = @@message_factory.tracks_changed(
            active_music_session.id, active_music_session.track_changes_counter
        )

        @@mq_router.server_publish_to_session(active_music_session, msg)
      end

      # tell all your friends you joined a session
      def send_musician_session_join(music_session, user)

        # tell the creator's friends that they joined a session, because we changed Find session to show your friend's sessions
        if music_session.creator.id == user.id || music_session.musician_access || music_session.fan_access

          friends = Friendship.where(:friend_id => user.id)
          user_followers = user.followers

          # construct an array of User objects representing friends and followers
          friend_users = friends.map { |fu| fu.user }
          follower_users = user_followers.map { |uf| uf.user }
          friends_and_followers = friend_users.concat(follower_users).uniq

          # remove anyone in the session and invited musicians
          friends_and_followers = friends_and_followers - music_session.unique_users - music_session.invited_musicians
          notification_msg = format_msg(NotificationTypes::MUSICIAN_SESSION_JOIN, {:user => user})

          friends_and_followers.each do |ff|
            if ff.online?
              notification = Notification.new
              notification.description = NotificationTypes::MUSICIAN_SESSION_JOIN
              notification.source_user_id = user.id
              notification.target_user_id = ff.id
              notification.session_id = music_session.id
              notification.save

              msg = @@message_factory.musician_session_join(
                ff.id,
                music_session.id,
                user.photo_url,
                music_session.fan_access,
                music_session.musician_access,
                music_session.approval_required,
                notification_msg,
                notification.id,
                notification.created_date
              )

              @@mq_router.publish_to_user(ff.id, msg)
            end
          end
        end
      end

      def send_jamclass_invitation_teacher(music_session, user)
        return if music_session.nil? || user.nil?

        teacher = target_user = user
        student = source_user = music_session.creator

        notification_msg = format_msg(NotificationTypes::SCHEDULED_JAMCLASS_INVITATION, {user: student, student_directed: false})

        notification = Notification.new
        notification.description = NotificationTypes::SCHEDULED_JAMCLASS_INVITATION
        notification.source_user_id = source_user.id
        notification.target_user_id = target_user.id
        notification.session_id = music_session.id
        notification.lesson_session_id = music_session.lesson_session.id
        notification.student_directed = false
        #notification.message = notification_msg
        notification.save

        if target_user.online
          msg = @@message_factory.scheduled_jamclass_invitation(
              target_user.id,
              music_session.id,
              source_user.photo_url,
              notification_msg,
              music_session.name,
              music_session.pretty_scheduled_start(false),
              notification.id,
              notification.created_date,
              notification.lesson_session.id
          )

          @@mq_router.publish_to_user(target_user.id, msg)
        end

        begin
          #UserMailer.teacher_scheduled_jamclass_invitation(music_session.lesson_session.teacher, notification_msg, music_session).deliver_now
        rescue => e
          @@log.error("Unable to send SCHEDULED_JAMCLASS_INVITATION email to user #{music_session.lesson_session.teacher.email} #{e}")
        end
      end

      def send_student_jamclass_invitation(music_session, user)
        return if music_session.nil? || user.nil?

        student = target_user = user
        teacher = source_user = music_session.lesson_session.teacher
        notification_msg = format_msg(NotificationTypes::SCHEDULED_JAMCLASS_INVITATION, {user: teacher, student_directed: true})

        notification = Notification.new
        notification.description = NotificationTypes::SCHEDULED_JAMCLASS_INVITATION
        notification.source_user_id = source_user.id
        notification.target_user_id = target_user.id
        notification.session_id = music_session.id
        notification.lesson_session_id = music_session.lesson_session.id
        notification.student_directed = true
        #notification.message = notification_msg
        notification.save

        if target_user.online
          msg = @@message_factory.scheduled_jamclass_invitation(
              target_user.id,
              music_session.id,
              source_user.photo_url,
              notification_msg,
              music_session.name,
              music_session.pretty_scheduled_start(false),
              notification.id,
              notification.created_date,
              notification.lesson_session_id
          )

          @@mq_router.publish_to_user(target_user.id, msg)
        end

        begin
          #UserMailer.student_scheduled_jamclass_invitation(student, notification_msg, music_session).deliver_now
        rescue => e
          @@log.error("Unable to send SCHEDULED_JAMCLASS_INVITATION email to user #{student.email} #{e}")
        end

      end


      def send_scheduled_session_invitation(music_session, user)

          return if music_session.nil? || user.nil?

          target_user = user
          source_user = music_session.creator

          notification = Notification.new
          notification.description = NotificationTypes::SCHEDULED_SESSION_INVITATION
          notification.source_user_id = source_user.id
          notification.target_user_id = target_user.id
          notification.session_id = music_session.id
          notification.save

          notification_msg = format_msg(notification.description, {:user => source_user, :session => music_session})

          if target_user.online
            msg = @@message_factory.scheduled_session_invitation(
              target_user.id,
              music_session.id,
              source_user.photo_url,
              notification_msg,
              music_session.name,
              music_session.pretty_scheduled_start(false),
              notification.id,
              notification.created_date
            )

            @@mq_router.publish_to_user(target_user.id, msg)
          end

          begin
            UserMailer.scheduled_session_invitation(target_user, notification_msg, music_session).deliver_now
          rescue => e
            @@log.error("Unable to send SCHEDULED_SESSION_INVITATION email to user #{target_user.email} #{e}")
          end
      end

      def send_scheduled_session_rsvp(music_session, user, instruments)

        return if music_session.nil? || user.nil?

        target_user = music_session.creator
        source_user = user

        return if target_user == source_user

        instruments = [] if instruments.nil?

        notification = Notification.new
        notification.description = NotificationTypes::SCHEDULED_SESSION_RSVP
        notification.source_user_id = source_user.id
        notification.target_user_id = target_user.id
        notification.session_id = music_session.id
        notification.save

        notification_msg = format_msg(notification.description, {:user => source_user, :session => music_session})

        if target_user.online?
          puts "ONLINE"
          msg = @@message_factory.scheduled_session_rsvp(
            target_user.id,
            music_session.id,
            source_user.photo_url,
            notification_msg,
            source_user.id,
            instruments.join('|'),
            music_session.name,
            music_session.pretty_scheduled_start(false),
            notification.id,
            notification.created_date
          )

          @@mq_router.publish_to_user(target_user.id, msg)
        end

        begin
          UserMailer.scheduled_session_rsvp(target_user, notification_msg, music_session).deliver_now
        rescue => e
          @@log.error("Unable to send SCHEDULED_SESSION_RSVP email to user #{target_user.email} #{e}")
        end
      end

      def send_scheduled_session_rsvp_approved(music_session, user, instruments)

        return if music_session.nil? || user.nil?

        target_user = user
        source_user = music_session.creator

        return if target_user == source_user

        instruments = [] if instruments.nil?

        notification = Notification.new
        notification.description = NotificationTypes::SCHEDULED_SESSION_RSVP_APPROVED
        notification.source_user_id = source_user.id
        notification.target_user_id = target_user.id
        notification.session_id = music_session.id
        notification.save

        notification_msg = format_msg(notification.description, {:session => music_session})

        if target_user.online?
          msg = @@message_factory.scheduled_session_rsvp_approved(
            target_user.id,
            music_session.id,
            notification_msg,
            music_session.name,
            music_session.pretty_scheduled_start(false),
            notification.id,
            notification.created_date
          )

          @@mq_router.publish_to_user(target_user.id, msg)
        end

        begin
          UserMailer.scheduled_session_rsvp_approved(target_user, notification_msg, music_session).deliver_now
        rescue => e
          @@log.error("Unable to send SCHEDULED_SESSION_RSVP_APPROVED email to user #{target_user.email} #{e}")
        end
      end

      def send_scheduled_session_rsvp_cancelled(music_session, user)

        return if music_session.nil? || user.nil?

        target_user = music_session.creator
        source_user = user

        notification = Notification.new
        notification.description = NotificationTypes::SCHEDULED_SESSION_RSVP_CANCELLED
        notification.source_user_id = source_user.id
        notification.target_user_id = target_user.id
        notification.session_id = music_session.id
        notification.save

        notification_msg = format_msg(notification.description, {:session => music_session})

        if target_user.online?
          msg = @@message_factory.scheduled_session_rsvp_cancelled(
            target_user.id,
            music_session.id,
            notification_msg,
            music_session.name,
            music_session.pretty_scheduled_start(false),
            notification.id,
            notification.created_date
          )

          @@mq_router.publish_to_user(target_user.id, msg)
        end

        begin
          UserMailer.scheduled_session_rsvp_cancelled(target_user, notification_msg, music_session).deliver_now
        rescue => e
          @@log.error("Unable to send SCHEDULED_SESSION_RSVP_CANCELLED email to user #{target_user.email} #{e}")
        end
      end

      def send_scheduled_session_rsvp_cancelled_org(music_session, user)

        return if music_session.nil? || user.nil?

        target_user = user
        source_user = music_session.creator

        notification = Notification.new
        notification.description = NotificationTypes::SCHEDULED_SESSION_RSVP_CANCELLED_ORG
        notification.source_user_id = source_user.id
        notification.target_user_id = target_user.id
        notification.session_id = music_session.id
        notification.save

        notification_msg = format_msg(notification.description, {:session => music_session})

        if target_user.online?
          msg = @@message_factory.scheduled_session_rsvp_cancelled_org(
            target_user.id,
            music_session.id,
            notification_msg,
            music_session.name,
            music_session.pretty_scheduled_start(false),
            notification.id,
            notification.created_date
          )

          @@mq_router.publish_to_user(target_user.id, msg)
        end

        begin
          UserMailer.scheduled_session_rsvp_cancelled_org(target_user, notification_msg, music_session).deliver_now
        rescue => e
          @@log.error("Unable to send SCHEDULED_SESSION_RSVP_CANCELLED_ORG email to user #{target_user.email} #{e}")
        end
      end

      def send_scheduled_session_cancelled(music_session)

        return if music_session.nil?

        rsvp_requests = RsvpRequest.index(music_session)
        target_users = rsvp_requests.where(:canceled => false).map { |r| r.user }

        # remove the creator from the array
        target_users = target_users.uniq - [music_session.creator]

        target_users.each do |target_user|
          source_user = music_session.creator

          notification = Notification.new
          notification.description = NotificationTypes::SCHEDULED_SESSION_CANCELLED
          notification.source_user_id = source_user.id
          notification.target_user_id = target_user.id
          notification.session_id = music_session.id
          notification.save

          notification_msg = format_msg(notification.description, {:session => music_session})

          if target_user.online?
            msg = @@message_factory.scheduled_session_cancelled(
              target_user.id,
              music_session.id,
              notification_msg,
              music_session.name,
              music_session.pretty_scheduled_start(false),
              notification.id,
              notification.created_date
            )

            @@mq_router.publish_to_user(target_user.id, msg)
          end

          begin
            UserMailer.scheduled_session_cancelled(target_user, notification_msg, music_session).deliver_now
          rescue => e
            @@log.error("Unable to send SCHEDULED_SESSION_CANCELLED email to user #{target_user.email} #{e}")
          end
        end
      end

      def send_scheduled_session_rescheduled(music_session)

        return if music_session.nil?

        rsvp_requests = RsvpRequest.index(music_session)
        target_users = rsvp_requests.where(:canceled => false).map { |r| r.user }
        pending_invites = music_session.pending_invitations

        # remove the creator from the array
        target_users = target_users.concat(pending_invites).uniq - [music_session.creator]

        target_users.each do |target_user|
          source_user = music_session.creator

          notification = Notification.new
          notification.description = NotificationTypes::SCHEDULED_SESSION_RESCHEDULED
          notification.source_user_id = source_user.id
          notification.target_user_id = target_user.id
          notification.session_id = music_session.id
          notification.save

          notification_msg = format_msg(notification.description, {:session => music_session})

          if target_user.online?
            msg = @@message_factory.scheduled_session_rescheduled(
              target_user.id,
              music_session.id,
              notification_msg,
              music_session.name,
              music_session.pretty_scheduled_start(false),
              notification.id,
              notification.created_date
            )

            @@mq_router.publish_to_user(target_user.id, msg)
          end

          begin
            UserMailer.scheduled_session_rescheduled(target_user, notification_msg, music_session).deliver_now
          rescue => e
            @@log.error("Unable to send SCHEDULED_SESSION_RESCHEDULED email to offline user #{target_user.email} #{e}")
          end
        end
      end

      # Send session reminders to sessions that
      # start in less than 24 hours, and haven't been
      # notified for a particular interval yet:
      def send_session_reminders
        MusicSession.where("scheduled_start > NOW() AND scheduled_start <= (NOW()+INTERVAL '1 DAYS') AND lesson_session_id IS NULL").each do |candidate_session|
          tm = candidate_session.scheduled_start
          if (tm>(12.hours.from_now) && !notified?(candidate_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_DAY))
            # Send 24 hour reminders:
            send_session_reminder_day(candidate_session)
          elsif (tm<=(65.minutes.from_now) && tm>(15.minutes.from_now) && !notified?(candidate_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_UPCOMING))
            # Send 1 hour reminders:
            send_session_reminder_upcoming(candidate_session)
          elsif (tm<=(10.minutes.from_now) && !notified?(candidate_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_IMMINENT))
            # Send 5 minute reminders:
            send_session_reminder_imminent(candidate_session)
          end
        end
      end

      def send_session_reminder_day(music_session)
        send_session_reminder(music_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_DAY) do |music_session, target_user, notification|
          begin
            UserMailer.scheduled_session_reminder_day(target_user, music_session).deliver_now
          rescue => e
            @@log.error("Unable to send SCHEDULED_SESSION_REMINDER_DAY email to user #{target_user.email} #{e}")
          end
        end
      end

      def send_session_reminder_upcoming(music_session)
        send_session_reminder(music_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_UPCOMING) do |music_session, target_user, notification|
          begin
            UserMailer.scheduled_session_reminder_upcoming(target_user, music_session).deliver_now
          rescue => e
            @@log.error("Unable to send SCHEDULED_SESSION_REMINDER_UPCOMING email to user #{target_user.email} #{e}")
          end
        end
      end

      def send_session_reminder_imminent(music_session)
        send_session_reminder(music_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_IMMINENT) do |music_session, target_user, notification|
          if target_user.online?
            msg = @@message_factory.scheduled_session_reminder(
              target_user.id,
              music_session.id,
              format_msg(notification.description, {:session => music_session}),
              music_session.name,
              music_session.pretty_scheduled_start(false),
              notification.id,
              notification.created_date
            )

            @@mq_router.publish_to_user(target_user.id, msg)
          end
        end
      end

      # @param music_session - the session for which to send reminder
      # @param reminder_type - the type of reminder; one of:
      #         =>  SCHEDULED_SESSION_REMINDER_DAY       24 hours
      #         =>  SCHEDULED_SESSION_REMINDER_UPCOMING  15 minutes
      #         =>  SCHEDULED_SESSION_REMINDER_IMMINENT  5 minutes (in-app)
      def send_session_reminder(music_session, reminder_type)
        raise ArgumentError, "Block required" unless block_given?
        source_user = music_session.creator
        rsvp_requests = RsvpRequest.index(music_session)
        rsvp_requests.where(:canceled => false).each do |rsvp|
          target_user = rsvp.user
          notification = Notification.new
          notification.description = reminder_type
          notification.source_user_id = source_user.id
          notification.target_user_id = target_user.id
          notification.session_id = music_session.id
          notification.save
          yield(music_session, target_user, notification)
        end
      end

      def send_scheduled_session_comment(music_session, creator, comment, send_to_cancelled = false)

        return if music_session.nil? || creator.nil? || comment.blank?

        rsvp_requests = RsvpRequest.index(music_session)
        target_users = send_to_cancelled ? rsvp_requests.map { |r| r.user } : rsvp_requests.where(:canceled => false).map { |r| r.user }
        target_users = target_users.concat([music_session.creator])
        source_user = creator
        pending_invites = music_session.pending_invitations

        # remove the creator from the array
        target_users = target_users.concat(pending_invites).uniq - [creator]

        target_users.each do |target_user|
          notification = Notification.new
          notification.description = NotificationTypes::SCHEDULED_SESSION_COMMENT
          notification.source_user_id = source_user.id
          notification.target_user_id = target_user.id
          notification.session_id = music_session.id
          notification.save

          notification_msg = format_msg(notification.description, {:session => music_session})

          if target_user.online?
            msg = @@message_factory.scheduled_session_comment(
              target_user.id,
              music_session.id,
              target_user.photo_url,
              notification_msg,
              comment,
              music_session.name,
              music_session.pretty_scheduled_start(false),
              notification.id,
              notification.created_date
            )

            @@mq_router.publish_to_user(target_user.id, msg)
          end

          begin
            UserMailer.scheduled_session_comment(target_user, source_user, notification_msg, comment, music_session).deliver_now
          rescue => e
            @@log.error("Unable to send SCHEDULED_SESSION_COMMENT email to user #{target_user.email} #{e}")
          end
        end
      end

      def send_band_session_join(music_session, band)

        # if the session is private, don't send any notifications
        if music_session.musician_access || music_session.fan_access

          notification_msg = format_msg(NotificationTypes::BAND_SESSION_JOIN, {:band => band})

          followers = band.followers.map { |bf| bf.user }

          # do not send band session notifications to band members
          followers = followers - band.users

          followers.each do |f|
            follower = f
            notification = Notification.new
            notification.band_id = band.id
            notification.description = NotificationTypes::BAND_SESSION_JOIN
            notification.target_user_id = follower.id
            notification.session_id = music_session.id
            notification.save

            if follower.online?
              msg = @@message_factory.band_session_join(
                follower.id,
                music_session.id,
                band.photo_url,
                music_session.fan_access,
                music_session.musician_access,
                music_session.approval_required,
                notification_msg,
                notification.id,
                notification.created_date
              )

              @@mq_router.publish_to_user(follower.id, msg)
            else
              if music_session.fan_access && APP_CONFIG.send_join_session_email_notifications
                begin
                  UserMailer.band_session_join(follower, notification_msg, music_session.id).deliver_now
                rescue => e
                  @@log.error("Unable to send BAND_SESSION_JOIN email to user #{follower.email} #{e}")
                end
              end
            end
          end
        end
      end

      def send_musician_recording_saved(recording)

        user = recording.owner

        friends = Friendship.where(:friend_id => user.id)
        user_followers = user.followers

        # construct an array of User objects representing friends and followers
        friend_users = friends.map { |fu| fu.user }
        follower_users = user_followers.map { |uf| uf.user }
        friends_and_followers = friend_users.concat(follower_users).uniq

        notification_msg = format_msg(NotificationTypes::MUSICIAN_RECORDING_SAVED, {:user => user})

        friends_and_followers.each do |ff|
          notification = Notification.new
          notification.description = NotificationTypes::MUSICIAN_RECORDING_SAVED
          notification.source_user_id = user.id
          notification.target_user_id = ff.id
          notification.recording_id = recording.id
          notification.save

          if ff.online?
            msg = @@message_factory.musician_recording_saved(
              ff.id,
              recording.id,
              user.photo_url,
              notification_msg,
              notification.id,
              notification.created_date
            )

            @@mq_router.publish_to_user(ff.id, notification_msg)
          else
            begin
              UserMailer.musician_recording_saved(ff, notification_msg).deliver_now
            rescue => e
              @@log.error("Unable to send MUSICIAN_RECORDING_SAVED email to user #{ff.email} #{e}")
            end
          end
        end
      end

      def send_band_recording_saved(recording)

        band = recording.band
        notification_msg = format_msg(NotificationTypes::BAND_RECORDING_SAVED, {:band => band})

        band.followers.each do |bf|
          follower = bf.user
          notification = Notification.new
          notification.description = NotificationTypes::BAND_RECORDING_SAVED
          notification.band_id = band.id
          notification.target_user_id = follower.id
          notification.recording_id = recording.id
          notification.save

          if follower.online?
            msg = @@message_factory.band_recording_saved(
              follower.id,
              recording.id,
              band.photo_url,
              notification_msg,
              notification.id,
              notification.created_date
            )

            @@mq_router.publish_to_user(follower.id, notification_msg)
          else
            begin
              UserMailer.band_recording_saved(follower, notification_msg).deliver_now
            rescue => e
              @@log.error("Unable to send BAND_RECORDING_SAVED email to user #{follower.email} #{e}")
            end
          end
        end
      end

      def send_recording_started(music_session, user)

        notification_msg = format_msg(NotificationTypes::RECORDING_STARTED, {:user => user})

        music_session.users.each do |musician|
          if musician.id != user.id
            msg = @@message_factory.recording_started(
              musician.id,
              user.photo_url,
              notification_msg
            )

            @@mq_router.publish_to_user(musician.id, msg)
          end
        end
      end

      def send_recording_ended(music_session, user)

        notification_msg = format_msg(NotificationTypes::RECORDING_ENDED, {:user => user})

        music_session.users.each do |musician|
          if musician.id != user.id
            msg = @@message_factory.recording_ended(
              musician.id,
              user.photo_url,
              notification_msg
            )

            @@mq_router.publish_to_user(musician.id, msg)
          end
        end
      end

      def send_recording_master_mix_complete(recording)

        # only people who get told about mixes are folks who claimed it... not everyone in the session
        recording.claimed_recordings.each do |claimed_recording|

          notification = Notification.new
          notification.band_id = recording.band.id if recording.band
          notification.recording_id = recording.id
          notification.target_user_id = claimed_recording.user_id
          notification.description = NotificationTypes::RECORDING_MASTER_MIX_COMPLETE
          notification.save

          notification_msg = format_msg(notification.description, {:band => recording.band})

          msg = @@message_factory.recording_master_mix_complete(
            claimed_recording.user_id,
            recording.id,
            claimed_recording.id,
            notification.band_id,
            notification_msg,
            notification.id,
            notification.created_date)

          @@mq_router.publish_to_user(claimed_recording.user_id, msg)
        end
      end

      def send_recording_stream_mix_complete(recording)

        # only people who get told about mixes are folks who claimed it... not everyone in the session
        recording.claimed_recordings.each do |claimed_recording|

          notification = Notification.new
          notification.band_id = recording.band.id if recording.band
          notification.recording_id = recording.id
          notification.target_user_id = claimed_recording.user_id
          notification.description = NotificationTypes::RECORDING_STREAM_MIX_COMPLETE
          notification.save

          notification_msg = format_msg(notification.description, {:band => recording.band})

          msg = @@message_factory.recording_stream_mix_complete(
              claimed_recording.user_id,
              recording.id,
              claimed_recording.id,
              notification.band_id,
              notification_msg,
              notification.id,
              notification.created_date)

          @@mq_router.publish_to_user(claimed_recording.user_id, msg)
        end
      end

      def send_jam_track_sign_failed(jam_track_right)

        notification = Notification.new
        notification.jam_track_right_id = jam_track_right.id
        notification.description = NotificationTypes::JAM_TRACK_SIGN_FAILED
        notification.target_user_id = jam_track_right.user_id
        notification.save!

        msg = @@message_factory.jam_track_sign_failed(jam_track_right.user_id, jam_track_right.id)
        @@mq_router.publish_to_user(jam_track_right.user_id, msg)
        #@@mq_router.publish_to_all_clients(msg)
      end

      def send_jam_track_sign_complete(jam_track_right)

        notification = Notification.new
        notification.jam_track_mixdown_package = jam_track_right.id
        notification.description = NotificationTypes::JAM_TRACK_SIGN_COMPLETE
        notification.target_user_id = jam_track_right.user_id
        notification.save!

        msg = @@message_factory.jam_track_sign_complete(jam_track_right.user_id, jam_track_right.id)
        @@mq_router.publish_to_user(jam_track_right.user_id, msg)
        #@@mq_router.publish_to_all_clients(msg)
      end

      def send_mixdown_sign_failed(jam_track_mixdown_package)

        notification = Notification.new
        notification.jam_track_mixdown_package_id = jam_track_mixdown_package.id
        notification.description = NotificationTypes::MIXDOWN_SIGN_FAILED
        notification.target_user_id = jam_track_mixdown_package.jam_track_mixdown.user_id
        notification.save!

        msg = @@message_factory.mixdown_sign_failed(jam_track_mixdown_package.jam_track_mixdown.user_id, jam_track_mixdown_package.id)
        @@mq_router.publish_to_user(jam_track_mixdown_package.jam_track_mixdown.user_id, msg)
      end

      def send_mixdown_sign_complete(jam_track_mixdown_package)

        notification = Notification.new
        notification.jam_track_mixdown_package_id = jam_track_mixdown_package.id
        notification.description = NotificationTypes::MIXDOWN_SIGN_COMPLETE
        notification.target_user_id = jam_track_mixdown_package.jam_track_mixdown.user_id
        notification.save!

        msg = @@message_factory.mixdown_sign_complete(jam_track_mixdown_package.jam_track_mixdown.user_id, jam_track_mixdown_package.id)
        @@mq_router.publish_to_user(jam_track_mixdown_package.jam_track_mixdown.user_id, msg)
      end

      def send_client_update(product, version, uri, size)
        msg = @@message_factory.client_update( product, version, uri, size)

        @@mq_router.publish_to_all_clients(msg)
      end

      def send_reload(client_id)
        msg = @@message_factory.reload(client_id)

        if client_id == MessageFactory::ALL_NATIVE_CLIENTS
          @@mq_router.publish_to_all_clients(msg)
        else
          @@mq_router.publish_to_client(client_id, msg)
        end
      end

      def send_restart_application(client_id)
        msg = @@message_factory.restart_application(client_id)

        if client_id == MessageFactory::ALL_NATIVE_CLIENTS
          @@mq_router.publish_to_all_clients(msg)
        else
          @@mq_router.publish_to_client(client_id, msg)
        end
      end

      def send_stop_application(client_id)
        msg = @@message_factory.stop_application(client_id)

        if client_id == MessageFactory::ALL_NATIVE_CLIENTS
          @@mq_router.publish_to_all_clients(msg)
        else
          @@mq_router.publish_to_client(client_id, msg)
        end
      end

      def send_text_message(message, sender, receiver)

        notification = Notification.new
        notification.description = NotificationTypes::TEXT_MESSAGE
        notification.message = message
        notification.source_user_id = sender.id
        notification.target_user_id = receiver.id if receiver
        if notification.save
          if receiver.online?
            clip_at = 200
            msg_is_clipped = message.length > clip_at
            truncated_msg = message[0..clip_at - 1]
            msg = @@message_factory.text_message(
                receiver.id,
                sender.photo_url,
                sender.name,
                sender.id,
                truncated_msg,
                msg_is_clipped,
                notification.id,
                notification.created_date)

            @@mq_router.publish_to_user(receiver.id, msg)

          else
            begin
              UserMailer.text_message(receiver, sender.id, sender.name, sender.resolved_photo_url, message).deliver_now
            rescue => e
              @@log.error("Unable to send TEXT_MESSAGE email to user #{receiver.email} #{e}")
            end
          end
        end

        notification
      end

      def send_band_invitation(band, band_invitation, sender, receiver)

        notification = Notification.new
        notification.band_id = band.id
        notification.band_invitation_id = band_invitation.id
        notification.description = NotificationTypes::BAND_INVITATION
        notification.source_user_id = sender.id
        notification.target_user_id = receiver.id
        notification.save

        notification_msg = format_msg(notification.description, {:band => band})

        if receiver.online?
          msg = @@message_factory.band_invitation(
            receiver.id,
            band_invitation.id,
            band.id,
            sender.photo_url,
            notification_msg,
            notification.id,
            notification.created_date
          )

          @@mq_router.publish_to_user(receiver.id, msg)

        else
          begin
            UserMailer.band_invitation(receiver, notification_msg).deliver_now
          rescue => e
            @@log.error("Unable to send BAND_INVITATION email to offline user #{receiver.email} #{e}")
          end
        end
      end

      def send_band_invitation_accepted(band, band_invitation, sender, receiver)

        notification = Notification.new
        notification.band_id = band.id
        notification.description = NotificationTypes::BAND_INVITATION_ACCEPTED
        notification.source_user_id = sender.id
        notification.target_user_id = receiver.id
        notification.save

        notification_msg = format_msg(notification.description, {:user => sender, :band => band})

        if receiver.online?
          msg = @@message_factory.band_invitation_accepted(
            receiver.id,
            band_invitation.id,
            sender.photo_url,
            notification_msg,
            notification.id,
            notification.created_date
          )
          @@mq_router.publish_to_user(receiver.id, msg)

        else
          begin
            UserMailer.band_invitation_accepted(receiver, notification_msg).deliver_now
          rescue => e
            @@log.error("Unable to send BAND_INVITATION_ACCEPTED email to offline user #{receiver.email} #{e}")
          end
        end
      end

      def send_musician_session_fresh(music_session, client_id, user)

        msg = @@message_factory.musician_session_fresh(
          music_session.id,
          user.id,
          user.name,
          user.photo_url
        )

        @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
      end

      def send_musician_session_stale(music_session, client_id, user)

        msg = @@message_factory.musician_session_stale(
          music_session.id,
          user.id,
          user.name,
          user.photo_url
        )

        @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
      end

      def send_download_available(user_id)
        msg = @@message_factory.download_available

        @@mq_router.publish_to_user(user_id, msg)
      end

      def send_source_up_requested(music_session, host, port, mount, source_user, source_pass, bitrate)
        msg = @@message_factory.source_up_requested(music_session.id, host, port, mount, source_user, source_pass, bitrate)

        @@mq_router.server_publish_to_session(music_session, msg)
      end

      def send_source_down_requested(music_session, mount)
        msg = @@message_factory.source_down_requested(music_session.id, mount)

        @@mq_router.server_publish_to_session(music_session, msg)
      end

      def send_source_up(music_session)
        msg = @@message_factory.source_up(music_session.id)

        @@mq_router.server_publish_to_everyone_in_session(music_session, msg)
      end

      def send_source_down(music_session)
        msg = @@message_factory.source_down(music_session.id)

        @@mq_router.server_publish_to_everyone_in_session(music_session, msg)
      end

      def send_subscription_message(type, id, body)
        msg = @@message_factory.subscription_message(type, id, body)

        @@mq_router.publish_to_subscription(type, id, msg)
      end
    end

    private

    def text_message?
      description == 'TEXT_MESSAGE'
    end
  end
end