////
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.
////
= PubSub Serialization

WARNING: The PubSub Serialization Services are still work in progress. The current PubSubAdmins still work differently.

PubSub serialization is realized by providing multiple message serialization services
(`pubsub_message_serialization_service_t`), a message serialization service per msg type.

The message serialization services must provide a mandatory `serialization.type`.
This is used to identify the serialization "type" they belong to, for example "json" or "avrobin".

The serialization services are used by the PubSubAdmins to find a matching serializer.
This is done by checking if a publisher requester or subscriber provider has configured a serialization type and
if this is not the case select the highest ranking available serialization service.

The serialization services are also used by the PubSub Admins to send (serialize) and receive (deserialize) messages.
The PubSub Admins realizes the transport for messages and the serialization services serializes the serializations.

Currently the Celix PubSub facilities delivers two serialiation types: "json" and "avrobin".
<<PubSub Serialization Provider>> can be used to create additional serialization types.

pubsub_message_serialization_service_t
[source,c]
----
#define PUBSUB_MESSAGE_SERIALIZATION_SERVICE_NAME      "pubsub_message_serialization_service"
#define PUBSUB_MESSAGE_SERIALIZATION_SERVICE_VERSION   "1.0.0"
#define PUBSUB_MESSAGE_SERIALIZATION_SERVICE_RANGE     "[1,2)"

#define PUBSUB_MESSAGE_SERIALIZATION_SERVICE_SERIALIZATION_TYPE_PROPERTY     "serialization.type"
#define PUBSUB_MESSAGE_SERIALIZATION_SERVICE_MSG_FQN_PROPERTY                "msg.fqn"
#define PUBSUB_MESSAGE_SERIALIZATION_SERVICE_MSG_VERSION_PROPERTY            "msg.version"
#define PUBSUB_MESSAGE_SERIALIZATION_SERVICE_MSG_ID_PROPERTY                 "msg.id"

/**
 * A message serialization service for a serialization type (e.g. json) and
 * for a specific msg type (based on the fully qualified name) and version.
 *
 * The properties serialization.type, msg,fqn, msg.version and msg.id are mandatory
 */
typedef struct pubsub_message_serialization_service {
    void* handle;

    celix_status_t (*serialize)(void* handle, const void* input, struct iovec** output, size_t* outputIovLen);
    void (*freeSerializedMsg)(void* handle, struct iovec* input, size_t inputIovLen);
    celix_status_t (*deserialize)

(void* handle, const struct iovec* input, size_t inputIovLen, void** out); //note inputLen can be 0 if predefined size is not needed
    void (*freeDeserializedMsg)(void* handle, void* msg);

} pubsub_message_serialization_service_t;
----

== PubSub Serialization Matching

TODO: Explain matching and usage service ranking on serialization service.


=== Match Serializer for Subscriber
[plantuml]
----
@startuml
actor PubSubUser

PubSubUser -> BundleContext: Register Subscriber Service
BundleContext -> Framework: Register Subscriber Service
Framework -> PubSubTopologyManager : Service Added (service tracker)
PubSubTopologyManager --> Framework
Framework --> BundleContext
BundleContext --> PubSubUser

== PubSubAdmin Setup ==

loop psa handling thread
PubSubTopologyManager -> PubSubTopologyManager: pstm_setupTopicReceivers
activate PubSubTopologyManager
PubSubTopologyManager ->  PubSubAdmin: matchSubscriber
PubSubAdmin -> PubSubUtils: matchSerializer
PubSubUtils --> PubSubAdmin
PubSubAdmin --> PubSubTopologyManager
PubSubTopologyManager -> PubSubAdmin: setupTopicReceiver
PubSubAdmin -> PubSubAdmin: create topic receiver
PubSubAdmin --> PubSubTopologyManager
deactivate PubSubTopologyManager
end

@enduml
----

=== Match Serializer for Publisher
[plantuml]
----
@startuml
actor PubSubUser

PubSubUser -> BundleContext: Request Publisher Service
BundleContext -> Framework: Register Service Listener hook
Framework -> PubSubTopologyManager : Listener Added
PubSubTopologyManager --> Framework
Framework --> BundleContext
BundleContext --> PubSubUser

== PubSubAdmin Setup ==

loop psa handling thread
PubSubTopologyManager -> PubSubTopologyManager: pstm_setupTopicSenders
activate PubSubTopologyManager
PubSubTopologyManager ->  PubSubAdmin: matchPublisher
PubSubAdmin -> PubSubUtils: matchSerializer
PubSubUtils --> PubSubAdmin
PubSubAdmin --> PubSubTopologyManager
PubSubTopologyManager -> PubSubAdmin: setupTopicSender
PubSubAdmin -> PubSubAdmin: create topic sender
PubSubAdmin -> BundleContext: Register Publisher Service
BundleContext -> PubSubUser: Publisher Added
PubSubUser --> BundleContext
BundleContext --> PubSubAdmin
PubSubAdmin --> PubSubTopologyManager
deactivate PubSubTopologyManager
end

@enduml
----

== PubSub Serialization Provider
The PubSub Serialization Provider (`pubsub_serialization_provider_t`) is a helper object which can be used to more easily
create serialization services based on descriptors (libdfi). It arranges tracking bundles, reading descriptor files,
generating errors for invalid descriptors and provides a shell command to interactively query the available descriptors.

The PubSub Serialization Provider needs to be constructor with the serializations service functions
(`serialize`, `freeSerializedMsg`, `deserialize` and `freeDeserializedMsg`)

The PubSub Serialization Provider is part of the `Celix::pubsub_utils` static library.

[ditaa]
----
             +----------------+
             | PubSub User    |
             | (bundle)       |              +----------------+
             |                |  contains    |                |
             |                +------------->+  +-------------+--+
             |                |              |  |                |
             +-------+--------+              |  | +--------------+--+
                     ^                       |  | | examples.Type   |
                     |       +-------------->+  | | (descriptor)    |
             tracks  |       |    reads      +----+                 |
             bundles |       |                  | |                 |
                     |       |                  +-+                 |
                     |       |                    |                 |
                     |       |                    |             {d} |
                     |       |                    +-----------------+
                     |       |
                     |       |
                     |       |
           +---------+-------+-+
           |  JSON             |
           |  Serialization    |             +----------------+
           |  Provider         +------------>+                |
           |  (bundle)         |  provides   |  +----------------+
           |                   |             |  |             |  |
           |                   |             |  | +-----------+--+--+
           |                   |             |  | |                 |  Properties:
           |                   |             |  | |  PubSub         |  o serialization.type = json
           +--+--------------+-+             +----+  Message        |  o msg.fqn = examples.Type
              |              |                  | |  Serialization  |  o msg.version = ...
              |              |                  +-+  Service        |  o msg.id = ...
              |              |                    |  (service)      |
    specialize|              |                    |                 |
              |              |                    +-----------------+
              |              |
              |              |
              v              |               +------------------+
+-------------+-----+        |               |                  |  Properties:
|                   |        |               | PubSub           |  o serialization.type = json
|  PubSub           |        |               | Message          |
|  Serialization    |        +-------------->+ Serialization    |
|  Provider         |           provides     | Marker           |
|  (library)        |                        | (service)        |
|                   |                        |                  |
|                   |                        +------------------+
+-------------------+
----

== PubSub Serialization Handler
The PubSub Serialization Handler (`pubsub_serialization_handler_t`) is a helper object which can be used to more easily
serialize/deserialize message.
The handler will track message serialization services for the provided serialization type (constructor argument) and
provides a more easy to use set of function for serialization.

It will also check if the provided message serialization services do not clash on the msg id and msg fqn combination.
When multiple message serialization services are registered it will use the highest ranking services.

serialization services based on descriptors (libdfi). It arranges tracking bundles, reading descriptor files,
generating errors for invalid descriptors and provides a shell command to interactively query the available descriptors.

The PubSub Serialization Handler is part of the `Celix::pubsub_utils` static library.

=== Serialization function for PubSub Serialization Handler
[source, c]
----
/**
 * Serialize a message into iovec structs (set of structures with buffer pointer and length)
 *
 * The correct message serialization services will be selected based on the provided msgId.
 *
 * @param handler       The pubsub serialization handler.
 * @param msgId         The msg id for the message to be serialized.
 * @param input         A pointer to the message object
 * @param output        An output pointer to a array of iovec structs.
 * @param outputIovLen  The number of iovec struct created
 * @return              CELIX_SUCCESS on success, CELIX_ILLEGAL_ARGUMENT if the msg id is not known or serialization failed.
 */
celix_status_t pubsub_serializerHandler_serialize(pubsub_serializer_handler_t* handler, uint32_t msgId, const void* input, struct iovec** output, size_t* outputIovLen);

/**
 * Free the memory of for the serialized msg.
 */
celix_status_t pubsub_serializerHandler_freeSerializedMsg(pubsub_serializer_handler_t* handler, uint32_t msgId, struct iovec* input, size_t inputIovLen);

/**
 * Deserialize a message using the provided iovec buffers.
 *
 * The deserialize function will also check if the target major/minor version of the message is valid with the version
 * of the serialized data.
 *
 * For some serialization types (e.g. JSON) newer versions of the serialized data can be deserialized.
 * E.g. JSON serialized data with version 1.2.0 can be deserialized to a target message with version 1.0.0
 * But JSON serialized data with a version 2.0.0 will not be deserialized to a target message with version 1.0.0
 * This assume correct use of semantic versioning.
 *
 * @param handler                   The pubsub serialization handler.
 * @param msgId                     The msg id for the message to be deserialized.
 * @param serializedMajorVersion    The major version of the serialized data
 * @param serializedMinorVersion    The minor version of the serialized data.
 * @param input                     Pointer to the first element in a array of iovecs.
 * @param inputIovLen               Then number of iovecs.
 * @param out                       The newly allocated and deserialized message object
 * @return                          CELIX_SUCCESS on success. CELIX_ILLEGAL_ARGUMENT if the msg id is not known,
 *                                  or if the version do no match or deserialization failed.
 */
celix_status_t pubsub_serializerHandler_deserialize(pubsub_serializer_handler_t* handler, uint32_t msgId, int serializedMajorVersion, int serializedMinorVersion, const struct iovec* input, size_t inputIovLen, void** out);

/**
 * Free the memory for the  deserialized message.
 */
celix_status_t pubsub_serializerHandler_freeDeserializedMsg(pubsub_serializer_handler_t* handler, uint32_t msgId, void* msg);
----