
// adding a new message:
// within ClientMessage block, 
//     create a new field in the enum Type section (100s are meant to be normal messages; 1000s are meant to be errors).  It will have new field number X
//     create a optional message of the same name (but lower-case!  the json code requires this), and use the same field number X.
// then at bottom of the file (or wherever; just outside any existing blocks), add your message definition.

package jampb;

message ClientMessage {
    enum Type { 
        LOGIN = 100; 
        LOGIN_ACK = 101; 

        LOGIN_MUSIC_SESSION = 102; 
        LOGIN_MUSIC_SESSION_ACK = 103;
        FRIEND_SESSION_JOIN = 104;
        LEAVE_MUSIC_SESSION = 105;
        LEAVE_MUSIC_SESSION_ACK = 106;
        HEARTBEAT = 107;
        FRIEND_UPDATE = 108;
        SESSION_INVITATION = 109;
        MUSICIAN_SESSION_DEPART = 110;
        JOIN_REQUEST = 111;
        FRIEND_REQUEST = 112;
        FRIEND_REQUEST_ACCEPTED = 113;
        MUSICIAN_SESSION_JOIN = 114;
        MUSICIAN_SESSION_FRESH = 115;
        MUSICIAN_SESSION_STALE = 116;
        HEARTBEAT_ACK = 117;

        TEST_SESSION_MESSAGE = 200;

        PING_REQUEST = 300;
        PING_ACK     = 301;
        PEER_MESSAGE  = 302;
        TEST_CLIENT_MESSAGE = 303;

        SERVER_BAD_STATE_RECOVERED = 900;

        SERVER_GENERIC_ERROR = 1000;
        SERVER_REJECTION_ERROR = 1001;
        SERVER_PERMISSION_ERROR = 1002;
        SERVER_BAD_STATE_ERROR = 1003;
    }

    // Identifies which inner message is filled in
    required Type type = 1;
    required string route_to = 2;
    optional string from = 3;
    optional string message_id = 4;
    optional string in_reply_to = 5;
    
    // One of the following messages can be populated:

    // Client-Server messages (to/from)
    optional Login                      login = 100; // to server
    optional LoginAck                   login_ack = 101; // from server
    optional LoginMusicSession          login_music_session = 102; // to server
    optional LoginMusicSessionAck       login_music_session_ack = 103; // from server 
    optional FriendSessionJoin          friend_session_join = 104; // from server to all members
    optional LeaveMusicSession          leave_music_session = 105;
    optional LeaveMusicSessionAck       leave_music_session_ack = 106;
    optional Heartbeat                  heartbeat = 107;
    optional FriendUpdate               friend_update = 108; // from server to all friends of user
    optional SessionInvitation          session_invitation = 109; // from server to user
    optional MusicianSessionDepart      musician_session_depart = 110;
    optional JoinRequest                join_request = 111;
    optional FriendRequest              friend_request = 112;
    optional FriendRequestAccepted      friend_request_accepted = 113;
    optional MusicianSessionJoin        musician_session_join = 114;
    optional MusicianSessionFresh       musician_session_fresh = 115;
    optional MusicianSessionStale       musician_session_stale = 116;
    optional HeartbeatAck               heartbeat_ack = 117;

    // Client-Session messages (to/from)  
    optional TestSessionMessage     test_session_message = 200;

    // Client-Client messages (to/from)
    optional PingRequest        ping_request = 300;
    optional PingAck            ping_ack = 301;
    optional PeerMessage                peer_message = 302;
    optional TestClientMessage      test_client_message = 303;

    // Server-to-Client special messages
    optional ServerBadStateRecovered server_bad_state_recovered = 900;

    // Server-to-Client errors 
    optional ServerGenericError server_generic_error = 1000;
    optional ServerRejectionError server_rejection_error = 1001;
    optional ServerPermissionError server_permission_error = 1002;
    optional ServerBadStateError server_bad_state_error = 1003;
}

// route_to: server
// sent from client to server to associate the connection with a user
// either supply a username/password, or just supply token
// if successful, a LoginAck is sent.
// if errored, a GenericServerError is sent and the connection is closed.
message Login {
    optional string username = 1; // username... could be email.  need to wait for prod requirements
    optional string password = 2; // a password
    optional string token = 3; // a token/cookie from previous successful login attempt or from 'token' field in .music file
    optional string client_id = 4; // if supplied, the server will accept this client_id as the unique Id of this client instance
    optional string reconnect_music_session_id = 5; // if supplied, the server will attempt to log the client into this session (designed for reconnect scenarios while in-session)

}

// route_to: client
// sent from server to client to let the client know the login was successful,
// and to also show the IP address of the client as seen by the server.
message LoginAck {
    optional string public_ip = 1;
    optional string client_id = 2; // a new client_id if none is supplied in Login, or just the original client_id echoed back
    optional string token = 3; // the remember me token.  This is useful if you logged in with username/password (otherwise it's just 'token' echoed back)
    optional int32  heartbeat_interval = 4; // set your heartbeat interval to this value
    optional string music_session_id = 5; // the music session that the user was in very recently (likely due to dropped connection)
    optional bool   reconnected = 6; // if reconnect_music_session_id is specified, and the server could log the user into that session, then true is returned. 
}

// route_to: server
// send from client to server to log in to a music session and be 'present'.
// if successful, a LoginMusicSessionAck is sent with error = false, and Client-Session messages will be passed 
// directly from client to all other client. Also, the server will generate a 'UserJoinedMusicSession' message
// and send it to all other users to let the others know the user is here.
// if errored, a LoginMusicSessionAck is sent with error = true.
message LoginMusicSession {
   optional string music_session = 1; 
}

// route_to: client
// error = true if the LoginMusicSession command failed.
message LoginMusicSessionAck {
    optional bool error = 1;
    optional string error_reason = 2;
}

// route_to: server
// send from client to server to leave a previously joined music session
message LeaveMusicSession {
    optional string music_session = 1;
}

// route_to: client
// error = true if the session didn't exist, but otherwise this is intended to always work barring server unrecoverable errors
message LeaveMusicSessionAck {
    optional bool error = 1;
    optional string error_reason = 2;
}

// route_to: client:
// sent by server to let the rest of the participants know a user has joined. 
message FriendSessionJoin {
    optional string session_id = 1; // the session ID
    optional string user_id = 2; // this is the user_id and can be used for user unicast messages
    optional string username = 3; // meant to be a display name
    optional string photo_url = 4;
}

// route_to: client:
// sent by server to let the rest of the participants know a user has joined. 
message MusicianSessionJoin {
    optional string session_id = 1; // the session ID
    optional string user_id = 2; // this is the user_id and can be used for user unicast messages
    optional string username = 3; // meant to be a display name
    optional string photo_url = 4;
}

// route_to: client:
// sent by server to let the rest of the participants know a user has left. 
message MusicianSessionDepart {
    optional string session_id = 1; // the session ID
    optional string user_id = 2; // this is the user_id and can be used for user unicast messages
    optional string username = 3; // meant to be a display name
    optional string photo_url = 4;
}

// route_to: client:
// sent by server to let the rest of the participants know a client has become active again after going stale 
message MusicianSessionFresh {
    optional string session_id = 1; // the session ID
    optional string user_id = 2; // this is the user_id and can be used for user unicast messages
    optional string username = 3; // meant to be a display name
    optional string photo_url = 4;
}

// route_to: client:
// sent by server to let the rest of the participants know a user has gone stale (websocket connection dropped) 
message MusicianSessionStale {
    optional string session_id = 1; // the session ID
    optional string user_id = 2; // this is the user_id and can be used for user unicast messages
    optional string username = 3; // meant to be a display name
    optional string photo_url = 4;
}

message JoinRequest {
    optional string join_request_id = 1;
    optional string username = 2;
    optional string text = 3;
}

// route_to: session
// a test message used by ruby-client currently.  just gives way to send out to rest of session
message TestSessionMessage {
    optional string msg = 1;
}

// route_to: client:[CLIENT_ID]
// asks a client to begin a ping session
message PingRequest {
    
}

// route_to: client:[CLIENT_ID]
// tells a client to begin pinging a session
message PingAck {

}

// route_to: client:[CLIENT_ID]
// generic message between clients
message PeerMessage {
    optional string message = 1;
}

// route_to: client:[CLIENT_ID]
// a test message used by ruby-client currently.  just gives a way to send directly to another client
message TestClientMessage {
    optional string msg = 1;
}

// route_to: server
// sent from client to server periodically to let server track if the client is truly alive and avoid TCP timeout scenarios
// the server will send a HeartbeatAck in response to this
message Heartbeat {
    
}

// target: client
// sent from server to client in response to a Heartbeat
message HeartbeatAck {
}

// target: client
// send from server to client when user sends a friend request
message FriendRequest {
    optional string friend_request_id = 1;
    optional string user_id = 2;
    optional string name = 3;
    optional string photo_url = 4;
    optional string friend_id = 5;
    optional string msg = 6;
    optional string notification_id = 7;
    optional string created_at = 8;
}

// target: client
message FriendRequestAccepted {
    optional string friend_id = 1;     // accepter
    optional string name = 2;
    optional string photo_url = 3;
    optional string user_id = 4;  // original requester
    optional string msg = 5;
    optional string notification_id = 6;
    optional string created_at = 7;
}

// target: client
// send from server to client when a user logs in
message FriendUpdate {
    optional string user_id = 1;
    optional string name = 2;
    optional string photo_url = 3;
    optional bool online = 4;
    optional string msg = 5;
}

// route_to: user:[USER_ID]
// let a user know they've been invited to a session
message SessionInvitation {
    optional string invitation = 1;
}

// route_to: client
// this should follow a ServerBadStateError in the case that the 
// websocket gateway recovers from whatever ailed it
message ServerBadStateRecovered {
}



// route_to: client
// this indicates unhandled error on server
// if you receive this, your connection will close after.
// but gives the client a chance to know why.
message ServerGenericError {
    optional string error_msg = 1;
}

// route_to: client
// this indicates the client did something wrong, and the server is mad enough to close connection.
// if you receive this, your connection will close after.
// but gives the client a chance to know why.
message ServerRejectionError {
    optional string error_msg = 1;
}

// route_to: client
// this indicates that the client doesn't have permission to ask the command
// your connection is not closed if this message occurs
message ServerPermissionError {
    optional string error_msg = 1;
}

// route_to: client
// this indicates that the server is in a bad state, but could recover later, and is unable to process the request
// your connection is not closed if this message occurs
message ServerBadStateError {
    optional string error_msg = 1;
}


