/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "util/dict.h"
#include "util/list.h"
//! @addtogroup Foundation
//! @{
//! @addtogroup AppMessage
//!
//!
//!
//! \brief Bi-directional communication between phone apps and Pebble watchapps
//!
//! AppMessage is a bi-directional messaging subsystem that enables communication between phone apps
//! and Pebble watchapps. This is accomplished by allowing phone and watchapps to exchange arbitrary
//! sets of key/value pairs. The key/value pairs are stored in the form of a Dictionary, the layout
//! of which is left for the application developer to define.
//!
//! AppMessage implements a push-oriented messaging protocol, enabling your app to call functions and
//! methods to push messages from Pebble to phone and vice versa. The protocol is symmetric: both Pebble
//! and the phone can send messages. All messages are acknowledged. In this context, there is no
//! client-server model, as such.
//!
//! During the sending phase, one side initiates the communication by transferring a dictionary over the air.
//! The other side then receives this message and is given an opportunity to perform actions on that data.
//! As soon as possible, the other side is expected to reply to the message with a simple acknowledgment
//! that the message was received successfully.
//!
//! PebbleKit JavaScript provides you with a set of standard JavaScript APIs that let your app receive messages
//! from the watch, make HTTP requests, and send new messages to the watch. AppMessage APIs are used to send and
//! receive data. A Pebble watchapp can use the resources of the connected phone to fetch information from web services,
//! send information to web APIs, or store login credentials. On the JavaScript side, you communicate
//! with Pebble via a Pebble object exposed in the namespace.
//!
//! Messages always need to get either ACKnowledged or "NACK'ed," that is, not acknowledged.
//! If not, messages will result in a time-out failure. The AppMessage subsystem takes care of this implicitly.
//! In the phone libraries, this step is a bit more explicit.
//!
//! The Pebble watch interfaces make a distinction between the Inbox and the Outbox calls. The Inbox
//! receives messages from the phone on the watch; the Outbox sends messages from the watch to the phone.
//! These two buffers can be managed separately.
//!
//!
Warning
//! A critical constraint of AppMessage is that messages are limited in size. An ingoing (outgoing) message
//! larger than the inbox (outbox) will not be transmitted and will generate an error. You can choose your
//! inbox and outbox size when you call app_message_open().
//!
//! Pebble SDK provides a static minimum guaranteed size (APP_MESSAGE_INBOX_SIZE_MINIMUM and APP_MESSAGE_OUTBOX_SIZE_MINIMUM).
//! Requesting a buffer of the minimum guaranteed size (or smaller) is always guaranteed to succeed on all
//! Pebbles in this SDK version or higher, and with every phone.
//!
//! In some context, Pebble might be able to provide your application with larger inbox/outbox.
//! You can call app_message_inbox_size_maximum() and app_message_outbox_size_maximum() in your code to get
//! the largest possible value you can use.
//!
//! To always get the largest buffer available, follow this best practice:
//!
//! app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum())
//!
//! AppMessage uses your application heap space. That means that the sizes you pick for the AppMessage
//! inbox and outbox buffers are important in optimizing your app’s performance. The more you use for
//! AppMessage, the less space you’ll have for the rest of your app.
//!
//! To register callbacks, you should call app_message_register_inbox_received(), app_message_register_inbox_dropped(),
//! app_message_register_outbox_sent(), app_message_register_outbox_failed().
//!
//! Pebble recommends that you call them before app_message_open() to ensure you do not miss a message
//! arriving between starting AppMessage and registering the callback. You can set a context that will be passed
//! to all the callbacks with app_message_set_context().
//!
//! In circumstances that may not be ideal, when using AppMessage several types of errors may occur.
//! For example:
//!
//! * The send can’t start because the system state won't allow for a success. Several reasons
//! you're unable to perform a send: A send() is already occurring (only one is possible at a time) or Bluetooth
//! is not enabled or connected.
//! * The send and receive occur, but the receiver can’t accept the message. For instance, there is no app
//! that receives such a message.
//! * The send occurs, but the receiver either does not actually receive the message or can’t handle it
//! in a timely fashion.
//! * In the case of a dropped message, the phone sends a message to the watchapp, while there is still
//! an unprocessed message in the Inbox.
//!
//! Other errors are possible and described by AppMessageResult. A client of the AppMessage interface
//! should use the result codes to be more robust in the face of communication problems either in the field or while debugging.
//!
//! Refer to the \htmlinclude app-phone-communication.html for a conceptual overview and code usage.
//!
//! For code examples, refer to the SDK Examples that directly use App Message. These include:
//! *
//! pebblekit-js-weather
//! *
//! pebblekit-js-quotes
//! @{
//!
//! AppMessage is a messaging subsystem that allows phone and watch applications
//! to exchange arbitrary sets of key-value pairs. The key value pairs are
//! stored in the form of a Dictionary, the layout of which is left for the
//! application developer to define.
//!
//! Communication Model
//!
//! AppMessage is a simple send-receive-reply protocol. The protocol is symmetric: both the watch and phone can start
//! sending a message and expect a reply from the other side.
//!
//! In the sending phase, one side initiates the communication by transferring a dictionary over the air. The other
//! side then receives this message and is given an opportunity to perform actions on that data. As soon as possible,
//! the other side is expected to reply to the message with a simple acknowledgement that the message was received
//! successfully.
//!
//! In non-ideal circumstances, several errors may occur. For example:
//! * The send can't start as the system state won't allow for a success.
//! * The send and receive occur, but the receiver cannot accept the message (for example, there is no app that receives such
//! a message).
//! * The send occurs, but the receiver either does not actually receive the message or can't handle it in a timely
//! fashion.
//!
//! Other errors are possible, described by \ref AppMessageResult. A client of the AppMessage interface
//! can use the result codes to be more robust in the face of communication problems either in the field or while
//! debugging.
//!
//! The watch interfaces make a distinction between the Inbox and the Outbox. The Inbox receives messages from the
//! phone on the watch; the Outbox sends messages from the watch to the phone. These two objects can be managed
//! separately.
//!
//! \note Messages are actually addressed by the UUID of the watch and phone apps. This is done automatically by the
//! system for the convenience of the client. However, this does require that both the watch and phone apps
//! share their UUID. AppMessage is not capable of 1:N or M:N communication at this time, and is merely 1:1.
//!
//! \sa AppMessageResult
// -------- Defines, Enumerations, and Structures ------------------------------------------------------------------ //
//! As long as the firmware maintains its current major version, inboxes of this size or smaller will be allowed.
//!
//! \sa app_message_inbox_size_maximum()
//! \sa APP_MESSAGE_OUTBOX_SIZE_MINIMUM
//!
#define APP_MESSAGE_INBOX_SIZE_MINIMUM 124 /* bytes */
//! As long as the firmware maintains its current major version, outboxes of this size or smaller will be allowed.
//!
//! \sa app_message_outbox_size_maximum()
//! \sa APP_MESSAGE_INBOX_SIZE_MINIMUM
//!
#define APP_MESSAGE_OUTBOX_SIZE_MINIMUM 636 /* bytes */
//! AppMessage result codes.
typedef enum {
//! (0) All good, operation was successful.
APP_MSG_OK = 0,
//! (2) The other end did not confirm receiving the sent data with an (n)ack in time.
APP_MSG_SEND_TIMEOUT = 1 << 1,
//! (4) The other end rejected the sent data, with a "nack" reply.
APP_MSG_SEND_REJECTED = 1 << 2,
//! (8) The other end was not connected.
APP_MSG_NOT_CONNECTED = 1 << 3,
//! (16) The local application was not running.
APP_MSG_APP_NOT_RUNNING = 1 << 4,
//! (32) The function was called with invalid arguments.
APP_MSG_INVALID_ARGS = 1 << 5,
//! (64) There are pending (in or outbound) messages that need to be processed first before
//! new ones can be received or sent.
APP_MSG_BUSY = 1 << 6,
//! (128) The buffer was too small to contain the incoming message.
//! @internal
//! @see \ref app_message_open()
APP_MSG_BUFFER_OVERFLOW = 1 << 7,
//! (512) The resource had already been released.
APP_MSG_ALREADY_RELEASED = 1 << 9,
//! (1024) The callback was already registered.
APP_MSG_CALLBACK_ALREADY_REGISTERED = 1 << 10,
//! (2048) The callback could not be deregistered, because it had not been registered before.
APP_MSG_CALLBACK_NOT_REGISTERED = 1 << 11,
//! (4096) The system did not have sufficient application memory to
//! perform the requested operation.
APP_MSG_OUT_OF_MEMORY = 1 << 12,
//! (8192) App message was closed.
APP_MSG_CLOSED = 1 << 13,
//! (16384) An internal OS error prevented AppMessage from completing an operation.
APP_MSG_INTERNAL_ERROR = 1 << 14,
//! (32768) The function was called while App Message was not in the appropriate state.
APP_MSG_INVALID_STATE = 1 << 15,
} AppMessageResult;
//! Called after an incoming message is received.
//!
//! \param[in] iterator
//! The dictionary iterator to the received message. Never NULL. Note that the iterator cannot be modified or
//! saved off. The library may need to re-use the buffered space where this message is supplied. Returning from
//! the callback indicates to the library that the received message contents are no longer needed or have already
//! been externalized outside its buffering space and iterator.
//!
//! \param[in] context
//! Pointer to application data as specified when registering the callback.
//!
typedef void (*AppMessageInboxReceived)(DictionaryIterator *iterator, void *context);
//! Called after an incoming message is dropped.
//!
//! \param[in] result
//! The reason why the message was dropped. Some possibilities include \ref APP_MSG_BUSY and
//! \ref APP_MSG_BUFFER_OVERFLOW.
//!
//! \param[in] context
//! Pointer to application data as specified when registering the callback.
//!
//! Note that you can call app_message_outbox_begin() from this handler to prepare a new message.
//! This will invalidate the previous dictionary iterator; do not use it after calling app_message_outbox_begin().
//!
typedef void (*AppMessageInboxDropped)(AppMessageResult reason, void *context);
//! Called after an outbound message has been sent and the reply has been received.
//!
//! \param[in] iterator
//! The dictionary iterator to the sent message. The iterator will be in the final state that was sent. Note that
//! the iterator cannot be modified or saved off as the library will re-open the dictionary with dict_begin() after
//! this callback returns.
//!
//! \param[in] context
//! Pointer to application data as specified when registering the callback.
//!
typedef void (*AppMessageOutboxSent)(DictionaryIterator *iterator, void *context);
//! Called after an outbound message has not been sent successfully.
//!
//! \param[in] iterator
//! The dictionary iterator to the sent message. The iterator will be in the final state that was sent. Note that
//! the iterator cannot be modified or saved off as the library will re-open the dictionary with dict_begin() after
//! this callback returns.
//!
//! \param[in] result
//! The result of the operation. Some possibilities for the value include \ref APP_MSG_SEND_TIMEOUT,
//! \ref APP_MSG_SEND_REJECTED, \ref APP_MSG_NOT_CONNECTED, \ref APP_MSG_APP_NOT_RUNNING, and the combination
//! `(APP_MSG_NOT_CONNECTED | APP_MSG_APP_NOT_RUNNING)`.
//!
//! \param context
//! Pointer to application data as specified when registering the callback.
//!
//! Note that you can call app_message_outbox_begin() from this handler to prepare a new message.
//! This will invalidate the previous dictionary iterator; do not use it after calling app_message_outbox_begin().
//!
typedef void (*AppMessageOutboxFailed)(DictionaryIterator *iterator, AppMessageResult reason, void *context);
// -------- AppMessage Callbacks ----------------------------------------------------------------------------------- //
//! Gets the context that will be passed to all AppMessage callbacks.
//!
//! \return The current context on record.
//!
void *app_message_get_context(void);
//! Sets the context that will be passed to all AppMessage callbacks.
//!
//! \param[in] context The context that will be passed to all AppMessage callbacks.
//!
//! \return The previous context that was on record.
//!
void *app_message_set_context(void *context);
//! Registers a function that will be called after any Inbox message is received successfully.
//!
//! Only one callback may be registered at a time. Each subsequent call to this function will replace the previous
//! callback. The callback is optional; setting it to NULL will deregister the current callback and no function will
//! be called anymore.
//!
//! \param[in] received_callback The callback that will be called going forward; NULL to not have a callback.
//!
//! \return The previous callback (or NULL) that was on record.
//!
AppMessageInboxReceived app_message_register_inbox_received(AppMessageInboxReceived received_callback);
//! Registers a function that will be called after any Inbox message is received but dropped by the system.
//!
//! Only one callback may be registered at a time. Each subsequent call to this function will replace the previous
//! callback. The callback is optional; setting it to NULL will deregister the current callback and no function will
//! be called anymore.
//!
//! \param[in] dropped_callback The callback that will be called going forward; NULL to not have a callback.
//!
//! \return The previous callback (or NULL) that was on record.
//!
AppMessageInboxDropped app_message_register_inbox_dropped(AppMessageInboxDropped dropped_callback);
//! Registers a function that will be called after any Outbox message is sent and an ACK reply occurs in a timely
//! fashion.
//!
//! Only one callback may be registered at a time. Each subsequent call to this function will replace the previous
//! callback. The callback is optional; setting it to NULL will deregister the current callback and no function will
//! be called anymore.
//!
//! \param[in] sent_callback The callback that will be called going forward; NULL to not have a callback.
//!
//! \return The previous callback (or NULL) that was on record.
//!
AppMessageOutboxSent app_message_register_outbox_sent(AppMessageOutboxSent sent_callback);
//! Registers a function that will be called after any Outbox message is not sent with a timely ACK reply.
//! The call to \ref app_message_outbox_send() must have succeeded.
//!
//! Only one callback may be registered at a time. Each subsequent call to this function will replace the previous
//! callback. The callback is optional; setting it to NULL will deregister the current callback and no function will
//! be called anymore.
//!
//! \param[in] failed_callback The callback that will be called going forward; NULL to not have a callback.
//!
//! \return The previous callback (or NULL) that was on record.
//!
AppMessageOutboxFailed app_message_register_outbox_failed(AppMessageOutboxFailed failed_callback);
//! Deregisters all callbacks and their context.
//!
void app_message_deregister_callbacks(void);
// -------- AppMessage Lifecycle ----------------------------------------------------------------------------------- //
//! Programmatically determine the inbox size maximum in the current configuration.
//!
//! \return The inbox size maximum on this firmware.
//!
//! \sa APP_MESSAGE_INBOX_SIZE_MINIMUM
//! \sa app_message_outbox_size_maximum()
//!
uint32_t app_message_inbox_size_maximum(void);
//! Programmatically determine the outbox size maximum in the current configuration.
//!
//! \return The outbox size maximum on this firmware.
//!
//! \sa APP_MESSAGE_OUTBOX_SIZE_MINIMUM
//! \sa app_message_inbox_size_maximum()
//!
uint32_t app_message_outbox_size_maximum(void);
//! Open AppMessage to transfers.
//!
//! Use \ref dict_calc_buffer_size_from_tuplets() or \ref dict_calc_buffer_size() to estimate the size you need.
//!
//! \param[in] size_inbound The required size for the Inbox buffer
//! \param[in] size_outbound The required size for the Outbox buffer
//!
//! \return A result code such as \ref APP_MSG_OK or \ref APP_MSG_OUT_OF_MEMORY.
//!
//! \note It is recommended that if the Inbox will be used, that at least the Inbox callbacks should be registered
//! before this call. Otherwise it is possible for an Inbox message to be NACK'ed without being seen by the
//! application.
//!
AppMessageResult app_message_open(const uint32_t size_inbound, const uint32_t size_outbound);
//! Close AppMessage to further transfers.
//!
void app_message_close(void);
// -------- AppMessage Inbox --------------------------------------------------------------------------------------- //
// Note: the Inbox has no direct functions, only callbacks.
// -------- AppMessage Outbox -------------------------------------------------------------------------------------- //
//! Begin writing to the Outbox's Dictionary buffer.
//!
//! \param[out] iterator Location to write the DictionaryIterator pointer. This will be NULL on failure.
//!
//! \return A result code, including but not limited to \ref APP_MSG_OK, \ref APP_MSG_INVALID_ARGS or
//! \ref APP_MSG_BUSY.
//!
//! \note After a successful call, one can add values to the dictionary using functions like \ref dict_write_data()
//! and friends.
//!
//! \sa Dictionary
//!
AppMessageResult app_message_outbox_begin(DictionaryIterator **iterator);
//! Sends the outbound dictionary.
//!
//! \return A result code, including but not limited to \ref APP_MSG_OK or \ref APP_MSG_BUSY. The APP_MSG_OK code does
//! not mean that the message was sent successfully, but only that the start of processing was successful.
//! Since this call is asynchronous, callbacks provide the final result instead.
//!
//! \sa AppMessageOutboxSent
//! \sa AppMessageOutboxFailed
//!
AppMessageResult app_message_outbox_send(void);
//! @} // end addtogroup AppMessage
//! @} // end addtogroup Foundation