module JamRuby
  # creates messages (implementation: protocol buffer) objects cleanly
  class MessageFactory

    CLIENT_TARGET         = "client"
    SERVER_TARGET         = "server"
    SESSION_TARGET_PREFIX = "session:"
    USER_TARGET_PREFIX    = "user:"
    CLIENT_TARGET_PREFIX  = "client:"
    ALL_NATIVE_CLIENTS    = '__ALL_NATIVE_CLIENTS__' # special value used in place of a specific client ID
    ALL_ACTIVE_CLIENTS    = '__ALL_ACTIVE_CLIENTS__' # special value used in place of a specific client ID

    def initialize()
      @type_values = {}

      Jampb::ClientMessage::Type.constants.each do |constant|
        @type_values[Jampb::ClientMessage::Type.const_get(constant)] = constant
      end

    end

    # given a string (bytes) payload, return a client message
    def parse_client_msg(payload)
      Jampb::ClientMessage.parse(payload)
    end

    # create a login message using client_id (used by latency_tester)
    def login_with_client_id(client_id)
      login = Jampb::Login.new(
        :client_id => client_id,
        :client_type => Connection::TYPE_LATENCY_TESTER
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::LOGIN,
        :route_to => SERVER_TARGET,
        :login => login
      )
    end

    # create a login message using user/pass
    def login_with_user_pass(username, password, options = {})
      login = Jampb::Login.new(
          :username => username,
          :password => password,
          :client_id => options[:client_id],
          :client_type => options[:client_type]
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::LOGIN,
          :route_to => SERVER_TARGET,
          :login => login
      )
    end

    # create a login message using token (a cookie or similar)
    def login_with_token(token, options = {})
      login = Jampb::Login.new(
        :token => token,
        :client_id => options[:client_id]
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::LOGIN,
        :route_to => SERVER_TARGET,
        :login => login
      )
    end

    def ars_body(ars)
      Jampb::Ars.new(
          :id => ars.id_int,
          :ip => ars.ip,
          :username => ars.username,
          :password => ars.password,
          :port => ars.port,
          :country => ars.country,
          :continent => ars.continent,
          :city => ars.city,
          :subdivision => ars.subdivision,
          :latitude => ars.latitude,
          :longitude => ars.longitude
      )
    end
    # create a login ack (login was successful)
    def login_ack(public_ip, client_id, token, heartbeat_interval, music_session_id, reconnected, user_id, connection_expire_time, username, client_id_int, client_update_data = nil, arses = [], subscription=nil, connection_policy = nil)
      client_update = Jampb::ClientUpdate.new(
          product: client_update_data[:product],
          version: client_update_data[:version],
          uri: client_update_data[:uri],
          size: client_update_data[:size]
      ) if client_update_data


      login_ack = Jampb::LoginAck.new(
        :public_ip => public_ip,
        :client_id => client_id,
        :token => token,
        :heartbeat_interval => heartbeat_interval,
        :music_session_id => music_session_id,
        :reconnected => reconnected,
        :user_id => user_id,
        :connection_expire_time =>  connection_expire_time,
        :client_update => client_update,
        :username => username,
        :client_id_int => client_id_int,
        :arses => arses,
        :subscription => Jampb::SiteSubscription.new(
            play_time_per_month: subscription[:play_time_per_month],
            play_time_per_session: subscription[:play_time_per_session],
            can_record_audio: subscription[:can_record_audio],
            can_use_video: subscription[:can_use_video],
            can_record_video: subscription[:can_record_video],
            can_record_wave: subscription[:can_record_wave],
            audio_max_bitrate: subscription[:audio_max_bitrate],
            video_resolution: subscription[:video_resolution],
            can_broadcast: subscription[:can_broadcast],
            broadcasting_type: subscription[:broadcasting_type],
            max_players: subscription[:max_players],
            pro_audio: subscription[:pro_audio],
            name: subscription[:name]
        ),
        :connection_policy => connection_policy
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::LOGIN_ACK,
        :route_to => CLIENT_TARGET,
        :login_ack => login_ack
      )
    end

    # create a login ack (login was successful)
    def logout_ack()

      logout_ack = Jampb::LogoutAck.new()

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::LOGOUT_ACK,
          :route_to => CLIENT_TARGET,
          :logout_ack => logout_ack
      )
    end

    # create a login ack (login was successful)
    def diagnostic(message)

      diagnostic = Jampb::Diagnostic.new(message: message)

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::DIAGNOSTIC,
          :route_to => CLIENT_TARGET,
          :diagnostic => diagnostic
      )
    end


    # create a login ack (login was successful)
    def connect_ack(public_ip, client_id,heartbeat_interval, connection_expire_time, client_update_data = nil)
      client_update = Jampb::ClientUpdate.new(
          product: client_update_data[:product],
          version: client_update_data[:version],
          uri: client_update_data[:uri],
          size: client_update_data[:size]
      ) if client_update_data

      connect_ack = Jampb::ConnectAck.new(
          :public_ip => public_ip,
          :client_id => client_id,
          :heartbeat_interval => heartbeat_interval,
          :connection_expire_time =>  connection_expire_time,
          :client_update => client_update
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::CONNECT_ACK,
          :route_to => CLIENT_TARGET,
          :connect_ack => connect_ack
      )
    end

    def download_available
      download_available = Jampb::DownloadAvailable.new

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::DOWNLOAD_AVAILABLE,
          :route_to => CLIENT_TARGET,
          :download_available => download_available
      )
    end

    # create a music session login message
    def login_music_session(music_session)
      login_music_session = Jampb::LoginMusicSession.new(:music_session => music_session)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::LOGIN_MUSIC_SESSION,
        :route_to => SERVER_TARGET,
        :login_music_session => login_music_session
      )
    end

    # create a music session login message ack (success or on failure)
    def login_music_session_ack(error, error_reason)
      login_music_session_ack = Jampb::LoginMusicSessionAck.new(:error => error, :error_reason => error_reason)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::LOGIN_MUSIC_SESSION_ACK,
        :route_to => CLIENT_TARGET,
        :login_music_session_ack => login_music_session_ack
      )
    end

    # create a music session 'leave session' message
    def leave_music_session(music_session)
      leave_music_session = Jampb::LeaveMusicSession.new(:music_session => music_session)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::LEAVE_MUSIC_SESSION,
        :route_to => SERVER_TARGET,
        :leave_music_session => leave_music_session
      )
    end

    # create a music session leave message ack (success or on failure)
    def leave_music_session_ack(error, error_reason)
      leave_music_session_ack = Jampb::LeaveMusicSessionAck.new(:error => error, :error_reason => error_reason)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::LEAVE_MUSIC_SESSION_ACK,
        :route_to => CLIENT_TARGET,
        :leave_music_session_ack => leave_music_session_ack
      )
    end

    # create a heartbeat
    def heartbeat()
      heartbeat = Jampb::Heartbeat.new

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::HEARTBEAT,
        :route_to => SERVER_TARGET,
        :heartbeat => heartbeat
      )
    end

    # create a heartbeat ack
    def heartbeat_ack(track_changes_counter)
      heartbeat_ack = Jampb::HeartbeatAck.new(
          :track_changes_counter => track_changes_counter,
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::HEARTBEAT_ACK,
        :route_to => CLIENT_TARGET,
        :heartbeat_ack => heartbeat_ack
      )
    end

    # create a server bad state recovered msg
    def server_bad_state_recovered(original_message_id)
      recovered = Jampb::ServerBadStateRecovered.new()

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SERVER_BAD_STATE_RECOVERED,
        :route_to => CLIENT_TARGET,
        :server_bad_state_recovered => recovered,
        :in_reply_to => original_message_id
      )
    end

    # create a server error
    def server_generic_error(error_msg)
      error = Jampb::ServerGenericError.new(:error_msg => error_msg)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SERVER_GENERIC_ERROR,
        :route_to => CLIENT_TARGET,
        :server_generic_error => error
      )
    end

    # create a server rejection error
    def server_rejection_error(error_msg, error_code)
      error = Jampb::ServerRejectionError.new(:error_msg => error_msg, :error_code => error_code)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SERVER_REJECTION_ERROR,
        :route_to => CLIENT_TARGET,
        :server_rejection_error => error
      )
    end

    # create a server rejection error
    def server_permission_error(original_message_id, error_msg)
      error = Jampb::ServerPermissionError.new(:error_msg => error_msg)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SERVER_PERMISSION_ERROR,
        :route_to => CLIENT_TARGET,
        :server_permission_error => error,
        :in_reply_to => original_message_id
      )
    end

    # create a server bad state error
    def server_bad_state_error(original_message_id, error_msg)
      error = Jampb::ServerBadStateError.new(:error_msg => error_msg)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SERVER_BAD_STATE_ERROR,
        :route_to => CLIENT_TARGET,
        :server_bad_state_error => error,
        :in_reply_to => original_message_id
      )
    end

    # create a server bad state error
    def server_duplicate_client_error
      error = Jampb::ServerDuplicateClientError.new()

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::SERVER_DUPLICATE_CLIENT_ERROR,
          :route_to => CLIENT_TARGET,
          :server_duplicate_client_error => error
      )
    end

    ###################################### NOTIFICATIONS ######################################

    # create a friend update message
    def friend_update(user_id, photo_url, online, msg)
      friend = Jampb::FriendUpdate.new(
        :user_id => user_id,
        :photo_url => photo_url,
        :online => online,
        :msg => msg
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::FRIEND_UPDATE,
        :route_to => USER_TARGET_PREFIX + user_id,
        :friend_update => friend
      )
    end

    # create a friend request message
    def friend_request(receiver_id, friend_request_id, photo_url, msg, notification_id, created_at)
      friend_request = Jampb::FriendRequest.new(
        :friend_request_id => friend_request_id, 
        :photo_url => photo_url,
        :msg => msg, 
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::FRIEND_REQUEST,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :friend_request => friend_request
      )
    end

    # create a friend request acceptance message
    def friend_request_accepted(receiver_id, photo_url, msg, notification_id, created_at)
      friend_request_accepted = Jampb::FriendRequestAccepted.new(
        :photo_url => photo_url,
        :msg => msg, 
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::FRIEND_REQUEST_ACCEPTED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :friend_request_accepted => friend_request_accepted
      )
    end

    def new_user_follower(receiver_id, photo_url, msg, notification_id, created_at)
      new_user_follower = Jampb::NewUserFollower.new( 
        :photo_url => photo_url,
        :msg => msg, 
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::NEW_USER_FOLLOWER,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :new_user_follower => new_user_follower
      )
    end

    def new_band_follower(receiver_id, photo_url, msg, notification_id, created_at)
      new_band_follower = Jampb::NewBandFollower.new(
        :photo_url => photo_url,
        :msg => msg, 
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::NEW_BAND_FOLLOWER,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :new_band_follower => new_band_follower
      )
    end

    def session_invitation(receiver_id, session_id, msg, notification_id, created_at)
      session_invitation = Jampb::SessionInvitation.new(
        :session_id => session_id,
        :msg => msg,        
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SESSION_INVITATION,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :session_invitation => session_invitation
      )
    end

    def session_ended(receiver_id, session_id)
      session_ended = Jampb::SessionEnded.new(
        :session_id => session_id
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SESSION_ENDED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :session_ended => session_ended
      )
    end

    # create a join request session message
    def join_request(join_request_id, session_id, photo_url, msg, notification_id, created_at)
      req = Jampb::JoinRequest.new(
        :join_request_id => join_request_id,
        :session_id => session_id,
        :photo_url => photo_url,
        :msg => msg,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::JOIN_REQUEST,
        :route_to => SESSION_TARGET_PREFIX + session_id,
        :join_request => req
      )
    end

    # create a join request approved session message
    def join_request_approved(join_request_id, session_id, photo_url, msg, notification_id, created_at)
      req_approved = Jampb::JoinRequestApproved.new(
        :join_request_id => join_request_id,
        :session_id => session_id,
        :photo_url => photo_url,
        :msg => msg,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::JOIN_REQUEST_APPROVED,
        :route_to => SESSION_TARGET_PREFIX + session_id,
        :join_request_approved => req_approved
      )
    end

    # create a join request rejected session message
    def join_request_rejected(join_request_id, session_id, photo_url, msg, notification_id, created_at)
      req_rejected = Jampb::JoinRequestRejected.new(
        :join_request_id => join_request_id,
        :session_id => session_id,
        :photo_url => photo_url,
        :msg => msg,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::JOIN_REQUEST_REJECTED,
        :route_to => SESSION_TARGET_PREFIX + session_id,
        :join_request_rejected => req_rejected
      )
    end

    # we send session_join to both every one in the session, and also every client belonging to user X
    def session_join(session_id, photo_url, source_user_id, msg, track_changes_counter, source_client_id, route_to = CLIENT_TARGET)
      join = Jampb::SessionJoin.new(
        :session_id => session_id,
        :photo_url => photo_url,
        :source_user_id => source_user_id,
        :msg => msg,
        :track_changes_counter => track_changes_counter,
        :client_id => source_client_id
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SESSION_JOIN,
        :route_to => route_to,
        :session_join => join
      )
    end

    def session_depart(session_id, photo_url, msg, recording_id, track_changes_counter, source_client_id, source_user_id, route_to = CLIENT_TARGET)
      left = Jampb::SessionDepart.new(
        :session_id => session_id,
        :photo_url => photo_url,
        :msg => msg,
        :recording_id => recording_id,
        :track_changes_counter => track_changes_counter,
        :client_id => source_client_id,
        :source_user_id => source_user_id
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SESSION_DEPART,
        :route_to => route_to,
        :session_depart => left
      )
    end


    def tracks_changed(session_id, track_changes_counter)
      tracks_changed = Jampb::TracksChanged.new(
          :session_id => session_id,
          :track_changes_counter => track_changes_counter
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::TRACKS_CHANGED,
          :route_to => CLIENT_TARGET,
          :tracks_changed => tracks_changed
      )
    end

    def musician_session_join(receiver_id, session_id, photo_url, fan_access, musician_access, approval_required, msg, notification_id, created_at)
      musician_session_join = Jampb::MusicianSessionJoin.new(
        :session_id => session_id,
        :photo_url => photo_url,
        :fan_access => fan_access,
        :musician_access => musician_access,
        :approval_required => approval_required,
        :msg => msg,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::MUSICIAN_SESSION_JOIN,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :musician_session_join => musician_session_join
      )
    end

    def scheduled_jamclass_invitation(receiver_id, session_id, photo_url, msg, session_name, session_date, notification_id, created_at, lesson_session_id)
      scheduled_jamclas_invitation = Jampb::ScheduledJamclassInvitation.new(
        :session_id => session_id,
        :photo_url => photo_url,
        :msg => msg,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at,
        lesson_session_id: lesson_session_id
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_JAMCLASS_INVITATION,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_jamclass_invitation => scheduled_jamclas_invitation
      )
    end


    def scheduled_session_invitation(receiver_id, session_id, photo_url, msg, session_name, session_date, notification_id, created_at)
      scheduled_session_invitation = Jampb::ScheduledSessionInvitation.new(
          :session_id => session_id,
          :photo_url => photo_url,
          :msg => msg,
          :session_name => session_name,
          :session_date => session_date,
          :notification_id => notification_id,
          :created_at => created_at
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::SCHEDULED_SESSION_INVITATION,
          :route_to => USER_TARGET_PREFIX + receiver_id,
          :scheduled_session_invitation => scheduled_session_invitation
      )
    end

    def scheduled_session_rsvp(receiver_id, session_id, photo_url, msg, user_id, instruments, session_name, session_date, notification_id, created_at)
      scheduled_session_rsvp = Jampb::ScheduledSessionRsvp.new(
        :session_id => session_id,
        :photo_url => photo_url,
        :msg => msg,
        :user_id => user_id,
        :instruments => instruments,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_SESSION_RSVP,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_session_rsvp => scheduled_session_rsvp
      )
    end

    def scheduled_session_rsvp_approved(receiver_id, session_id, msg, session_name, session_date, notification_id, created_at)

      scheduled_session_rsvp_approved = Jampb::ScheduledSessionRsvpApproved.new(
        :session_id => session_id,
        :msg => msg,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_SESSION_RSVP_APPROVED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_session_rsvp_approved => scheduled_session_rsvp_approved
      )
    end

    def scheduled_session_rsvp_cancelled(receiver_id, session_id, msg, session_name, session_date, notification_id, created_at)

      scheduled_session_rsvp_cancelled = Jampb::ScheduledSessionRsvpCancelled.new(
        :session_id => session_id,
        :msg => msg,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_SESSION_RSVP_CANCELLED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_session_rsvp_cancelled => scheduled_session_rsvp_cancelled
      )
    end

    def scheduled_session_rsvp_cancelled_org(receiver_id, session_id, msg, session_name, session_date, notification_id, created_at)

      scheduled_session_rsvp_cancelled_org = Jampb::ScheduledSessionRsvpCancelledOrg.new(
        :session_id => session_id,
        :msg => msg,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_SESSION_RSVP_CANCELLED_ORG,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_session_rsvp_cancelled_org => scheduled_session_rsvp_cancelled_org
      )
    end

    def scheduled_session_cancelled(receiver_id, session_id, msg, session_name, session_date, notification_id, created_at)

      scheduled_session_cancelled = Jampb::ScheduledSessionCancelled.new(
        :session_id => session_id,
        :msg => msg,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_SESSION_CANCELLED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_session_cancelled => scheduled_session_cancelled
      )
    end

    def scheduled_session_rescheduled(receiver_id, session_id, msg, session_name, session_date, notification_id, created_at)

      scheduled_session_rescheduled = Jampb::ScheduledSessionRescheduled.new(
        :session_id => session_id,
        :msg => msg,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_SESSION_RESCHEDULED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_session_rescheduled => scheduled_session_rescheduled
      )
    end

    def scheduled_session_reminder(receiver_id, session_id, msg, session_name, session_date, notification_id, created_at)

      scheduled_session_reminder = Jampb::ScheduledSessionReminder.new(
        :session_id => session_id,
        :msg => msg,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_SESSION_REMINDER,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_session_reminder => scheduled_session_reminder
      )
    end

    def scheduled_session_comment(receiver_id, session_id, photo_url, msg, comment, session_name, session_date, notification_id, created_at)

      scheduled_session_comment = Jampb::ScheduledSessionComment.new(
        :session_id => session_id,
        :photo_url => photo_url,
        :msg => msg,
        :comment => comment,
        :session_name => session_name,
        :session_date => session_date,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::SCHEDULED_SESSION_COMMENT,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :scheduled_session_comment => scheduled_session_comment
      )
    end

    def band_session_join(receiver_id, session_id, photo_url, fan_access, musician_access, approval_required, msg, notification_id, created_at)
      band_session_join = Jampb::BandSessionJoin.new(
          :session_id => session_id,
          :photo_url => photo_url,
          :fan_access => fan_access,
          :musician_access => musician_access,
          :approval_required => approval_required,
          :msg => msg,
          :notification_id => notification_id,
          :created_at => created_at
        )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::BAND_SESSION_JOIN,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :band_session_join => band_session_join
      )
    end

    def musician_recording_saved(receiver_id, recording_id, photo_url, msg, notification_id, created_at)
      musician_recording_saved = Jampb::MusicianRecordingSaved.new(
        :recording_id => recording_id,
        :photo_url => photo_url,
        :msg => msg,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::MUSICIAN_RECORDING_SAVED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :musician_recording_saved => musician_recording_saved
      )
    end

    def band_recording_saved(receiver_id, recording_id, photo_url, msg, notification_id, created_at)
      band_recording_saved = Jampb::BandRecordingSaved.new(
        :recording_id => recording_id,
        :photo_url => photo_url,
        :msg => msg,
        :notification_id => notification_id,
        :created_at => created_at
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::BAND_RECORDING_SAVED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :band_recording_saved => band_recording_saved
      )
    end

    def recording_started(receiver_id, photo_url, msg)
      recording_started = Jampb::RecordingStarted.new(
        :photo_url => photo_url,
        :msg => msg
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::RECORDING_STARTED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :recording_started => recording_started
      )
    end

    def recording_ended(receiver_id, photo_url, msg)
      recording_ended = Jampb::RecordingEnded.new(
        :photo_url => photo_url,
        :msg => msg
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::RECORDING_ENDED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :recording_ended => recording_ended
      )
    end

    def jam_track_sign_complete(receiver_id, jam_track_right_id)
      signed = Jampb::JamTrackSignComplete.new(
        :jam_track_right_id => jam_track_right_id
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::JAM_TRACK_SIGN_COMPLETE,
          :route_to => USER_TARGET_PREFIX + receiver_id, #:route_to => CLIENT_TARGET,
          :jam_track_sign_complete => signed          
      )
    end

    def jam_track_sign_failed(receiver_id, jam_track_right_id)
      signed = Jampb::JamTrackSignFailed.new(
          :jam_track_right_id => jam_track_right_id
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::JAM_TRACK_SIGN_FAILED,
          :route_to => USER_TARGET_PREFIX + receiver_id, #:route_to => CLIENT_TARGET,
          :jam_track_sign_failed=> signed
      )
    end

    def mixdown_sign_complete(receiver_id, mixdown_package_id)
      signed = Jampb::MixdownSignComplete.new(
          :mixdown_package_id => mixdown_package_id
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::MIXDOWN_SIGN_COMPLETE,
          :route_to => USER_TARGET_PREFIX + receiver_id, #:route_to => CLIENT_TARGET,
          :mixdown_sign_complete => signed
      )
    end

    def mixdown_sign_failed(receiver_id, mixdown_package_id)
      signed = Jampb::MixdownSignFailed.new(
          :mixdown_package_id => mixdown_package_id
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::MIXDOWN_SIGN_FAILED,
          :route_to => USER_TARGET_PREFIX + receiver_id, #:route_to => CLIENT_TARGET,
          :mixdown_sign_failed=> signed
      )
    end


    def recording_master_mix_complete(receiver_id, recording_id, claimed_recording_id, band_id, msg, notification_id, created_at)
      recording_master_mix_complete = Jampb::RecordingMasterMixComplete.new(
          :recording_id => recording_id,
          :claimed_recording_id => claimed_recording_id,
          :band_id => band_id,
          :msg => msg,
          :notification_id => notification_id,
          :created_at => created_at
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::RECORDING_MASTER_MIX_COMPLETE,
          :route_to => USER_TARGET_PREFIX + receiver_id,
          :recording_master_mix_complete => recording_master_mix_complete
      )
    end

    def recording_stream_mix_complete(receiver_id, recording_id, claimed_recording_id, band_id, msg, notification_id, created_at)
      recording_stream_mix_complete = Jampb::RecordingStreamMixComplete.new(
          :recording_id => recording_id,
          :claimed_recording_id => claimed_recording_id,
          :band_id => band_id,
          :msg => msg,
          :notification_id => notification_id,
          :created_at => created_at
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::RECORDING_STREAM_MIX_COMPLETE,
          :route_to => USER_TARGET_PREFIX + receiver_id,
          :recording_stream_mix_complete => recording_stream_mix_complete
      )
    end

    def client_update(product, version, uri, size)
      client_update = Jampb::ClientUpdate.new(
          product: product,
          version: version,
          uri: uri,
          size: size
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::CLIENT_UPDATE,
          :route_to => CLIENT_TARGET_PREFIX + ALL_NATIVE_CLIENTS,
          :client_update => client_update
      )
    end

    def reload(client_id)
      reload = Jampb::Reload.new()

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::RELOAD,
          :route_to => CLIENT_TARGET_PREFIX + client_id,
          :reload => reload
      )
    end

    def restart_application(client_id)
      restart_application = Jampb::RestartApplication.new()

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::RESTART_APPLICATION,
          :route_to => CLIENT_TARGET_PREFIX + client_id,
          :restart_application => restart_application
      )
    end

    def stop_application(client_id)
      stop_application = Jampb::StopApplication.new()

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::STOP_APPLICATION,
          :route_to => CLIENT_TARGET_PREFIX + client_id,
          :stop_application => stop_application
      )
    end

    # create a band invitation message
    def band_invitation(receiver_id, invitation_id, band_id, photo_url, msg, notification_id, created_at)
      band_invitation = Jampb::BandInvitation.new(
          :band_invitation_id => invitation_id,
          :band_id => band_id,
          :photo_url => photo_url,
          :msg => msg,
          :notification_id => notification_id,
          :created_at => created_at
        )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::BAND_INVITATION,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :band_invitation => band_invitation
      )
    end

    # create a band invitation acceptance message
    def band_invitation_accepted(receiver_id, invitation_id, photo_url, msg, notification_id, created_at)
      band_invitation_accepted = Jampb::BandInvitationAccepted.new(
          :band_invitation_id => invitation_id,
          :photo_url => photo_url,
          :msg => msg,
          :notification_id => notification_id,
          :created_at => created_at
        )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::BAND_INVITATION_ACCEPTED,
        :route_to => USER_TARGET_PREFIX + receiver_id,
        :band_invitation_accepted => band_invitation_accepted
      )
    end

    # creates the general purpose text message
    def text_message(receiver_id, sender_photo_url, sender_name, sender_id, msg, clipped_msg, notification_id, created_at)
      text_message = Jampb::TextMessage.new(
          :photo_url => sender_photo_url,
          :sender_name => sender_name,
          :sender_id => sender_id,
          :msg => msg,
          :clipped_msg => clipped_msg,
          :notification_id => notification_id,
          :created_at => created_at
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::TEXT_MESSAGE,
          :route_to => USER_TARGET_PREFIX + receiver_id,
          :text_message => text_message
      )
    end

    # creates the general purpose text message
    def lesson_message(receiver_id, sender_photo_url, sender_name, sender_id, msg, notification_id, music_session_id, created_at, student_directed, purpose, lesson_session_id)
      lesson_message = Jampb::LessonMessage.new(
          :photo_url => sender_photo_url,
          :sender_name => sender_name,
          :sender_id => sender_id,
          :receiver_id => receiver_id,
          :msg => msg,
          :notification_id => notification_id,
          :music_session_id => music_session_id,
          :created_at => created_at,
          :student_directed => student_directed,
          :purpose => purpose,
          :lesson_session_id => lesson_session_id
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::LESSON_MESSAGE,
          :route_to => USER_TARGET_PREFIX + receiver_id,
          :lesson_message => lesson_message
      )
    end

    # creates the chat message
    def chat_message(session_id, sender_name, sender_id, msg, msg_id, created_at, channel, lesson_session_id, purpose,
    attachment_id, attachment_type, attachment_name)
      chat_message = Jampb::ChatMessage.new(
          :sender_id    => sender_id,
          :sender_name  => sender_name,
          :msg          => msg,
          :msg_id       => msg_id,
          :created_at   => created_at,
          :channel      => channel,
          :lesson_session_id => lesson_session_id,
          :purpose => purpose,
          :attachment_id => attachment_id,
          :attachment_type => attachment_type,
          :attachment_name => attachment_name
      )

      if session_id
        route_to = SESSION_TARGET_PREFIX + session_id
      else
        route_to = ALL_ACTIVE_CLIENTS
      end

      Jampb::ClientMessage.new(
          :type         => ClientMessage::Type::CHAT_MESSAGE,
          :route_to     => route_to,
          :chat_message => chat_message
      )
    end

    def pair_attempt(jbid, scid, vtoken)
      pair_attempt = Jampb::PairAttempt.new(
        :scid => scid,
        :vtoken => vtoken
      )

      Jampb::ClientMessage.new(
          :type         => ClientMessage::Type::PAIR_ATTEMPT,
          :route_to     => CLIENT_TARGET_PREFIX + jbid,
          :pair_attempt => pair_attempt
      )
    end
    # create a musician fresh session message
    def musician_session_fresh(session_id, user_id, username, photo_url)
      fresh = Jampb::MusicianSessionFresh.new(
        :session_id => session_id,
        :user_id => user_id,
        :username => username,
        :photo_url => photo_url
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::MUSICIAN_SESSION_FRESH,
        :route_to => CLIENT_TARGET,
        :musician_session_fresh => fresh
      )
    end

    # create a musician stale session message
    def musician_session_stale(session_id, user_id, username, photo_url)
      stale = Jampb::MusicianSessionStale.new(
        :session_id => session_id,
        :user_id => user_id,
        :username => username,
        :photo_url => photo_url
      )

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::MUSICIAN_SESSION_STALE,
        :route_to => CLIENT_TARGET,
        :musician_session_stale => stale
      )
    end

    # create a source up requested message to send to clients in a session,
    # so that one of the clients will start sending source audio to icecast
      def source_up_requested (session_id, host, port, mount, source_user, source_pass, bitrate)
      source_up_requested = Jampb::SourceUpRequested.new(
          music_session: session_id,
          host: host,
          port: port,
          mount: mount,
          source_user: source_user,
          source_pass: source_pass,
          bitrate: bitrate)

      Jampb::ClientMessage.new(
          type: ClientMessage::Type::SOURCE_UP_REQUESTED,
          route_to:  SESSION_TARGET_PREFIX + session_id,
          source_up_requested: source_up_requested)
    end

    # create a source up requested message to send to clients in a session,
    # so that one of the clients will start sending source audio to icecast
    def source_down_requested (session_id, mount)
      source_down_requested = Jampb::SourceDownRequested.new(music_session: session_id, mount: mount)

      Jampb::ClientMessage.new(
          type: ClientMessage::Type::SOURCE_DOWN_REQUESTED,
          route_to:  SESSION_TARGET_PREFIX + session_id,
          source_down_requested: source_down_requested)
    end

    # let's someone know that the source came online. the stream activate shortly
    # it might be necessary to refresh the client
    def source_up (session_id)
      source_up  = Jampb::SourceUp.new(music_session: session_id)

      Jampb::ClientMessage.new(
          type: ClientMessage::Type::SOURCE_UP,
          route_to:  SESSION_TARGET_PREFIX + session_id,
          source_up: source_up)
    end

    # let's someone know that the source went down. the stream will go offline
    def source_down (session_id)
      source_down = Jampb::SourceDown.new(music_session: session_id)

      Jampb::ClientMessage.new(
          type: ClientMessage::Type::SOURCE_DOWN,
          route_to:  SESSION_TARGET_PREFIX + session_id,
          source_down: source_down)
    end

    # create a test message to send in session
    def test_session_message(session_id, msg)
      test = Jampb::TestSessionMessage.new(:msg => msg)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::TEST_SESSION_MESSAGE,
        :route_to => SESSION_TARGET_PREFIX + session_id,
        :test_session_message => test
      )
    end

    ############## P2P CLIENT MESSAGES #################

    # send a request to do a ping
    def ping_request(client_id, from)
      ping_request = Jampb::PingRequest.new()

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::PING_REQUEST,
        :route_to => CLIENT_TARGET_PREFIX + client_id,
        :from => from,
        :ping_request => ping_request
      )
    end

    # respond to a ping_request with an ack
    def ping_ack(client_id, from)
      ping_ack = Jampb::PingAck.new

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::PING_ACK,
        :route_to => CLIENT_TARGET_PREFIX + client_id,
        :from => from,
        :ping_ack => ping_ack
      )
    end

    # create a test message to send in session
    def test_client_message(client_id, from, msg)
      test = Jampb::TestClientMessage.new(:msg => msg)

      Jampb::ClientMessage.new(
        :type => ClientMessage::Type::TEST_CLIENT_MESSAGE,
        :route_to => CLIENT_TARGET_PREFIX + client_id,
        :from => from,
        :test_client_message => test
      )
    end

    def subscription_message(type, id, body)
      subscription_message = Jampb::SubscriptionMessage.new(
        id: id,
        type: type,
        body: body
      )

      Jampb::ClientMessage.new(
          :type => ClientMessage::Type::SUBSCRIPTION_MESSAGE,
          :route_to => CLIENT_TARGET,
          :subscription_message => subscription_message,
      )
    end

    ####################################################

    # is this message directed to the server?
    def server_directed? msg
       return msg.route_to == MessageFactory::SERVER_TARGET
    end

    # is this message directed to the client?
    def client_directed? msg
       return msg.route_to.start_with? MessageFactory::CLIENT_TARGET_PREFIX
    end

    # is this message directed to a (music) session?
    def session_directed? msg
       return msg.route_to.start_with? MessageFactory::SESSION_TARGET_PREFIX
    end

    # is this message directed to a user?
    def user_directed? msg
       return msg.route_to.start_with? MessageFactory::USER_TARGET_PREFIX
    end

    def extract_session(msg)
      return msg.route_to[MessageFactory::SESSION_TARGET_PREFIX..-1]
    end

    def get_message_type msg
      return @type_values[msg.type]
    end
  end
end
