/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/*
 * libqmi-glib -- GLib/GIO based library to control QMI devices
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 * Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
 */

#ifndef _LIBQMI_GLIB_QMI_MESSAGE_H_
#define _LIBQMI_GLIB_QMI_MESSAGE_H_

#if !defined (__LIBQMI_GLIB_H_INSIDE__) && !defined (LIBQMI_GLIB_COMPILATION)
#error "Only <libqmi-glib.h> can be included directly."
#endif

#include <glib.h>

#include "qmi-utils.h"
#include "qmi-enums.h"
#include "qmi-errors.h"
#include "qmi-message-context.h"

G_BEGIN_DECLS

/**
 * SECTION:qmi-message
 * @short_description: Generic QMI message handling routines
 *
 * #QmiMessage is a generic type representing a QMI message of any kind
 * (request, response, indication) or service (including #QMI_SERVICE_CTL).
 *
 * This set of generic routines help in handling these message types, and
 * allow creating any kind of message with any kind of TLV.
 */

/**
 * QMI_MESSAGE_QMUX_MARKER:
 *
 * First byte of every QMI QMUX message.
 *
 * Since: 1.0
 */
#define QMI_MESSAGE_QMUX_MARKER (guint8) 0x01

/**
 * QMI_MESSAGE_QRTR_MARKER:
 *
 * Fake header added by libqmi to re-use existing QMUX message parsers for QRTR messages.
 * QRTR QMI services with a service ID > 0xFF use this fake header where the service ID 
 * is set to 16 bits instead of 8 bits. This header has no purpose outside of libqmi
 * and is never send to the actual device implementing these QMI services.
 *
 * Since: 1.34
 */
#define QMI_MESSAGE_QRTR_MARKER (guint8) 0x02

/**
 * QmiMessage:
 *
 * An opaque type representing a QMI message.
 *
 * Since: 1.0
 */
typedef GByteArray QmiMessage;

/*****************************************************************************/
/* QMI Message life cycle */

/**
 * qmi_message_new:
 * @service: a #QmiService
 * @client_id: client ID of the originating control point.
 * @transaction_id: transaction ID.
 * @message_id: message ID.
 *
 * Create a new #QmiMessage with the specified parameters.
 *
 * Note that @transaction_id must be less than #G_MAXUINT8 if @service is
 * #QMI_SERVICE_CTL.
 *
 * Returns: (transfer full): a newly created #QmiMessage. The returned value should be freed with qmi_message_unref().
 *
 * Since: 1.0
 */
QmiMessage *qmi_message_new (QmiService service,
                             guint8     client_id,
                             guint16    transaction_id,
                             guint16    message_id);

/**
 * qmi_message_new_from_raw:
 * @raw: (inout): raw data buffer.
 * @error: return location for error or %NULL.
 *
 * Create a new #QmiMessage from the given raw data buffer.
 *
 * Whenever a complete QMI message is read, its raw data gets removed from the @raw buffer.
 *
 * Returns: (transfer full): a newly created #QmiMessage, which should be freed with qmi_message_unref(). If @raw doesn't contain a complete QMI message %NULL is returned. If there is a complete QMI message but it appears not to be valid, %NULL is returned and @error is set.
 *
 * Since: 1.0
 */
QmiMessage *qmi_message_new_from_raw (GByteArray  *raw,
                                      GError     **error);

/**
 * qmi_message_new_from_data:
 * @service: a #QmiService
 * @client_id: client ID of the originating control point.
 * @qmi_data: (inout): data buffer containing only the QMI part of the message.
 * @error: return location for error or %NULL.
 *
 * Create a new #QmiMessage for the given @service and @client_id and including the given QMI data buffer.
 *
 * Whenever a complete QMI message is read, its data gets removed from the @qmi_data buffer.
 *
 * This method should be used instead of qmi_message_new_from_raw() if the input data doesn't have QMUX headers.
 *
 * Returns: (transfer full): a newly created #QmiMessage, which should be freed with qmi_message_unref(). If @qmi_data doesn't contain a complete QMI data payload %NULL is returned. If there is a complete QMI data payload but it appears not to be valid, %NULL is returned and @error is set.
 *
 * Since: 1.24
 */
QmiMessage *qmi_message_new_from_data (QmiService   service,
                                       guint8       client_id,
                                       GByteArray  *qmi_data,
                                       GError     **error);

/**
 * qmi_message_response_new:
 * @request: a request #QmiMessage.
 * @error: a #QmiProtocolError to set in the result TLV.
 *
 * Create a new response #QmiMessage for the specified @request.
 *
 * Returns: (transfer full): a newly created #QmiMessage. The returned value should be freed with qmi_message_unref().
 *
 * Since: 1.8
 */
QmiMessage *qmi_message_response_new (QmiMessage       *request,
                                      QmiProtocolError  error);

/**
 * qmi_message_ref:
 * @self: a #QmiMessage.
 *
 * Atomically increments the reference count of @self by one.
 *
 * Returns: (transfer full): the new reference to @self.
 *
 * Since: 1.0
 */
QmiMessage *qmi_message_ref (QmiMessage *self);

/**
 * qmi_message_unref:
 * @self: a #QmiMessage.
 *
 * Atomically decrements the reference count of @self by one.
 * If the reference count drops to 0, @self is completely disposed.
 *
 * Since: 1.0
 */
void qmi_message_unref (QmiMessage *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (QmiMessage, qmi_message_unref)

/*****************************************************************************/
/* QMI Message content getters */

/**
 * qmi_message_is_request:
 * @self: a #QmiMessage.
 *
 * Checks whether the given #QmiMessage is a request.
 *
 * Returns: %TRUE if @self is a request message, %FALSE otherwise.
 *
 * Since: 1.8
 */
gboolean qmi_message_is_request (QmiMessage *self);

/**
 * qmi_message_is_response:
 * @self: a #QmiMessage.
 *
 * Checks whether the given #QmiMessage is a response.
 *
 * Returns: %TRUE if @self is a response message, %FALSE otherwise.
 *
 * Since: 1.0
 */
gboolean qmi_message_is_response (QmiMessage *self);

/**
 * qmi_message_is_indication:
 * @self: a #QmiMessage.
 *
 * Checks whether the given #QmiMessage is an indication.
 *
 * Returns: %TRUE if @self is an indication message, %FALSE otherwise.
 *
 * Since: 1.0
 */
gboolean qmi_message_is_indication (QmiMessage *self);

/**
 * qmi_message_get_service:
 * @self: a #QmiMessage.
 *
 * Gets the service corresponding to the given #QmiMessage.
 *
 * Returns: a #QmiService.
 *
 * Since: 1.0
 */
QmiService qmi_message_get_service (QmiMessage *self);

/**
 * qmi_message_get_client_id:
 * @self: a #QmiMessage.
 *
 * Gets the client ID of the message.
 *
 * Returns: the client ID.
 *
 * Since: 1.0
 */
guint8 qmi_message_get_client_id (QmiMessage *self);

/**
 * qmi_message_get_transaction_id:
 * @self: a #QmiMessage.
 *
 * Gets the transaction ID of the message.
 *
 * Returns: the transaction ID.
 *
 * Since: 1.0
 */
guint16 qmi_message_get_transaction_id (QmiMessage *self);

/**
 * qmi_message_get_message_id:
 * @self: a #QmiMessage.
 *
 * Gets the ID of the message.
 *
 * Returns: the ID.
 *
 * Since: 1.0
 */
guint16 qmi_message_get_message_id (QmiMessage *self);

/**
 * qmi_message_get_length:
 * @self: a #QmiMessage.
 *
 * Gets the length of the raw data corresponding to the given #QmiMessage.
 *
 * Returns: the length of the raw data.
 *
 * Since: 1.0
 */
gsize qmi_message_get_length (QmiMessage *self);

/**
 * qmi_message_get_raw:
 * @self: a #QmiMessage.
 * @length: (out): return location for the size of the output buffer.
 * @error: return location for error or %NULL.
 *
 * Gets the raw data buffer of the #QmiMessage.
 *
 * Returns: (transfer none): The raw data buffer, or %NULL if @error is set.
 *
 * Since: 1.0
 */
const guint8 *qmi_message_get_raw (QmiMessage  *self,
                                   gsize       *length,
                                   GError     **error);


/**
 * qmi_message_get_data:
 * @self: a #QmiMessage.
 * @length: (out): return location for the size of the output buffer.
 * @error: return location for error or %NULL.
 *
 * Gets the data buffer of the #QmiMessage without the QMUX header.
 *
 * Returns: (transfer none): The raw QMI buffer, or %NULL if @error is set.
 *
 * Since: 1.24
 */
const guint8 *qmi_message_get_data (QmiMessage  *self,
                                    gsize       *length,
                                    GError     **error);

/**
 * qmi_message_get_marker:
 * @self: a #QmiMessage.
 *
 * Gets the marker of the #QmiMessage.
 *
 * Returns: The message marker.
 *
 * Since: 1.34
 */
guint8 qmi_message_get_marker (QmiMessage *self);

/*****************************************************************************/
/* TLV builder & writer */

/**
 * qmi_message_tlv_write_init:
 * @self: a #QmiMessage.
 * @type: specific ID of the TLV to add.
 * @error: return location for error or %NULL.
 *
 * Starts building a new TLV in the #QmiMessage.
 *
 * In order to finish adding the TLV, qmi_message_tlv_write_complete() needs to be
 * called.
 *
 * If any error happens adding fields on the TLV, the previous state can be
 * recovered using qmi_message_tlv_write_reset().
 *
 * Returns: the offset where the TLV was started to be added, or 0 if an error happens.
 *
 * Since: 1.12
 */
gsize qmi_message_tlv_write_init (QmiMessage  *self,
                                  guint8       type,
                                  GError     **error);

/**
 * qmi_message_tlv_write_reset:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_write_init().
 *
 * Removes the TLV being currently added.
 *
 * Since: 1.12
 */
void qmi_message_tlv_write_reset (QmiMessage *self,
                                  gsize       tlv_offset);

/**
 * qmi_message_tlv_write_complete:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_write_init().
 * @error: return location for error or %NULL.
 *
 * Completes building a TLV in the #QmiMessage.
 *
 * In case of error the TLV will be reseted; i.e. there is no need to explicitly
 * call qmi_message_tlv_write_reset().
 *
 * Returns: %TRUE if the TLV is successfully completed, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_complete (QmiMessage  *self,
                                         gsize        tlv_offset,
                                         GError     **error);

/**
 * qmi_message_tlv_write_guint8:
 * @self: a #QmiMessage.
 * @in: a #guint8.
 * @error: return location for error or %NULL.
 *
 * Appends an unsigned byte to the TLV being built.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_guint8 (QmiMessage  *self,
                                       guint8       in,
                                       GError     **error);

/**
 * qmi_message_tlv_write_gint8:
 * @self: a #QmiMessage.
 * @in: a #gint8.
 * @error: return location for error or %NULL.
 *
 * Appends a signed byte variable to the TLV being built.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_gint8 (QmiMessage  *self,
                                      gint8        in,
                                      GError     **error);

/**
 * qmi_message_tlv_write_guint16:
 * @self: a #QmiMessage.
 * @endian: target endianness, swapped from host byte order if necessary.
 * @in: a #guint16 in host byte order.
 * @error: return location for error or %NULL.
 *
 * Appends an unsigned 16-bit integer to the TLV being built. The number to be
 * written is expected to be given in host endianness, and this method takes
 * care of converting the value written to the byte order specified by @endian.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_guint16 (QmiMessage  *self,
                                        QmiEndian    endian,
                                        guint16      in,
                                        GError     **error);

/**
 * qmi_message_tlv_write_gint16:
 * @self: a #QmiMessage.
 * @endian: target endianness, swapped from host byte order if necessary.
 * @in: a #gint16 in host byte order.
 * @error: return location for error or %NULL.
 *
 * Appends a signed 16-bit integer to the TLV being built. The number to be
 * written is expected to be given in host endianness, and this method takes
 * care of converting the value written to the byte order specified by @endian.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_gint16 (QmiMessage  *self,
                                       QmiEndian    endian,
                                       gint16       in,
                                       GError     **error);

/**
 * qmi_message_tlv_write_guint32:
 * @self: a #QmiMessage.
 * @endian: target endianness, swapped from host byte order if necessary.
 * @in: a #guint32 in host byte order.
 * @error: return location for error or %NULL.
 *
 * Appends an unsigned 32-bit integer to the TLV being built. The number to be
 * written is expected to be given in host endianness, and this method takes
 * care of converting the value written to the byte order specified by @endian.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_guint32 (QmiMessage  *self,
                                        QmiEndian    endian,
                                        guint32      in,
                                        GError     **error);

/**
 * qmi_message_tlv_write_gint32:
 * @self: a #QmiMessage.
 * @endian: target endianness, swapped from host byte order if necessary.
 * @in: a #gint32 in host byte order.
 * @error: return location for error or %NULL.
 *
 * Appends a signed 32-bit integer to the TLV being built. The number to be
 * written is expected to be given in host endianness, and this method takes
 * care of converting the value written to the byte order specified by @endian.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_gint32 (QmiMessage  *self,
                                       QmiEndian    endian,
                                       gint32       in,
                                       GError     **error);

/**
 * qmi_message_tlv_write_guint64:
 * @self: a #QmiMessage.
 * @endian: target endianness, swapped from host byte order if necessary.
 * @in: a #guint64 in host byte order.
 * @error: return location for error or %NULL.
 *
 * Appends an unsigned 64-bit integer to the TLV being built. The number to be
 * written is expected to be given in host endianness, and this method takes
 * care of converting the value written to the byte order specified by @endian.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_guint64 (QmiMessage  *self,
                                        QmiEndian    endian,
                                        guint64      in,
                                        GError     **error);

/**
 * qmi_message_tlv_write_gint64:
 * @self: a #QmiMessage.
 * @endian: target endianness, swapped from host byte order if necessary.
 * @in: a #gint64 in host byte order.
 * @error: return location for error or %NULL.
 *
 * Appends a signed 32-bit integer to the TLV being built. The number to be
 * written is expected to be given in host endianness, and this method takes
 * care of converting the value written to the byte order specified by @endian.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_gint64 (QmiMessage  *self,
                                       QmiEndian    endian,
                                       gint64       in,
                                       GError     **error);

/**
 * qmi_message_tlv_write_sized_guint:
 * @self: a #QmiMessage.
 * @n_bytes: number of bytes to write.
 * @endian: target endianness, swapped from host byte order if necessary.
 * @in: a #guint64 in host byte order.
 * @error: return location for error or %NULL.
 *
 * Appends a @n_bytes-sized unsigned integer to the TLV being built. The number
 * to be written is expected to be given in host endianness, and this method
 * takes care of converting the value written to the byte order specified by
 * @endian.
 *
 * The value of @n_bytes can be any between 1 and 8.
 *
 * Returns: %TRUE if the variable is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_sized_guint (QmiMessage  *self,
                                            guint        n_bytes,
                                            QmiEndian    endian,
                                            guint64      in,
                                            GError     **error);


/**
 * qmi_message_tlv_write_string:
 * @self: a #QmiMessage.
 * @n_size_prefix_bytes: number of bytes to use in the size prefix.
 * @in: string to write.
 * @in_length: length of @in, or -1 if @in is NUL-terminated.
 * @error: return location for error or %NULL.
 *
 * Appends a string to the TLV being built.
 *
 * Fixed-sized strings should give @n_size_prefix_bytes equal to 0.
 *
 * Returns: %TRUE if the string is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_write_string (QmiMessage   *self,
                                       guint8        n_size_prefix_bytes,
                                       const gchar  *in,
                                       gssize        in_length,
                                       GError      **error);

/*****************************************************************************/
/* TLV reader */

/**
 * qmi_message_tlv_read_init:
 * @self: a #QmiMessage.
 * @type: specific ID of the TLV to read.
 * @out_tlv_length: (out): optional return location for the TLV size.
 * @error: return location for error or %NULL.
 *
 * Starts reading a given TLV from the #QmiMessage.
 *
 * Returns: the offset where the TLV starts, or 0 if an error happens.
 *
 * Since: 1.12
 */
gsize qmi_message_tlv_read_init (QmiMessage  *self,
                                 guint8       type,
                                 guint16     *out_tlv_length,
                                 GError     **error);

/**
 * qmi_message_tlv_read_guint8:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of the offset within the TLV value.
 * @out: (out): return location for the read #guint8.
 * @error: return location for error or %NULL.
 *
 * Reads an unsigned byte from the TLV.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_guint8 (QmiMessage  *self,
                                      gsize        tlv_offset,
                                      gsize       *offset,
                                      guint8      *out,
                                      GError     **error);

/**
 * qmi_message_tlv_read_gint8:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @out: (out): return location for the read #gint8.
 * @error: return location for error or %NULL.
 *
 * Reads a signed byte from the TLV.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_gint8 (QmiMessage  *self,
                                     gsize        tlv_offset,
                                     gsize       *offset,
                                     gint8       *out,
                                     GError     **error);

/**
 * qmi_message_tlv_read_guint16:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #guint16.
 * @error: return location for error or %NULL.
 *
 * Reads an unsigned 16-bit integer from the TLV, in host byte order.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_guint16 (QmiMessage  *self,
                                       gsize        tlv_offset,
                                       gsize       *offset,
                                       QmiEndian    endian,
                                       guint16     *out,
                                       GError     **error);

/**
 * qmi_message_tlv_read_gint16:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #gint16.
 * @error: return location for error or %NULL.
 *
 * Reads a signed 16-bit integer from the TLV, in host byte order.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_gint16 (QmiMessage  *self,
                                      gsize        tlv_offset,
                                      gsize       *offset,
                                      QmiEndian    endian,
                                      gint16      *out,
                                      GError     **error);

/**
 * qmi_message_tlv_read_guint32:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #guint32.
 * @error: return location for error or %NULL.
 *
 * Reads an unsigned 32-bit integer from the TLV, in host byte order.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_guint32 (QmiMessage  *self,
                                       gsize        tlv_offset,
                                       gsize       *offset,
                                       QmiEndian    endian,
                                       guint32     *out,
                                       GError     **error);

/**
 * qmi_message_tlv_read_gint32:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #gint32.
 * @error: return location for error or %NULL.
 *
 * Reads a signed 32-bit integer from the TLV, in host byte order.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_gint32 (QmiMessage  *self,
                                      gsize        tlv_offset,
                                      gsize       *offset,
                                      QmiEndian    endian,
                                      gint32      *out,
                                      GError     **error);

/**
 * qmi_message_tlv_read_guint64:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #guint64.
 * @error: return location for error or %NULL.
 *
 * Reads an unsigned 64-bit integer from the TLV, in host byte order.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_guint64 (QmiMessage  *self,
                                       gsize        tlv_offset,
                                       gsize       *offset,
                                       QmiEndian    endian,
                                       guint64     *out,
                                       GError     **error);

/**
 * qmi_message_tlv_read_gint64:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #gint64.
 * @error: return location for error or %NULL.
 *
 * Reads a signed 64-bit integer from the TLV, in host byte order.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_gint64 (QmiMessage  *self,
                                      gsize        tlv_offset,
                                      gsize       *offset,
                                      QmiEndian    endian,
                                      gint64      *out,
                                      GError     **error);

/**
 * qmi_message_tlv_read_sized_guint:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @n_bytes: number of bytes to read.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #guint64.
 * @error: return location for error or %NULL.
 *
 * Reads a @b_bytes-sized integer from the TLV, in host byte order.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_sized_guint (QmiMessage  *self,
                                           gsize        tlv_offset,
                                           gsize       *offset,
                                           guint        n_bytes,
                                           QmiEndian    endian,
                                           guint64     *out,
                                           GError     **error);

/**
 * qmi_message_tlv_read_gfloat_endian:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #gfloat.
 * @error: return location for error or %NULL.
 *
 * Reads a 32-bit floating-point number from the TLV.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_gfloat_endian (QmiMessage  *self,
                                             gsize        tlv_offset,
                                             gsize       *offset,
                                             QmiEndian    endian,
                                             gfloat      *out,
                                             GError     **error);

/**
 * qmi_message_tlv_read_gdouble:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @endian: source endianness, which will be swapped to host byte order if necessary.
 * @out: (out): return location for the read #gdouble.
 * @error: return location for error or %NULL.
 *
 * Reads a 64-bit floating-point number from the TLV.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.22
 */
gboolean qmi_message_tlv_read_gdouble (QmiMessage  *self,
                                       gsize        tlv_offset,
                                       gsize       *offset,
                                       QmiEndian    endian,
                                       gdouble     *out,
                                       GError     **error);

/**
 * qmi_message_tlv_read_string:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @n_size_prefix_bytes: number of bytes used in the size prefix.
 * @max_size: maximum number of bytes to read, or 0 to read all available bytes.
 * @out: (out): return location for the read string. The returned value should be freed with g_free().
 * @error: return location for error or %NULL.
 *
 * Reads a string from the TLV.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Since 1.24.6 the read string is guaranteed to be valid UTF-8. Also, in order to
 * overcome known firmware errors on string fields, this method will also
 * attempt to parse the string as GSM-7 or UCS-2 if the initial UTF-8 validation
 * fails.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_string (QmiMessage  *self,
                                      gsize        tlv_offset,
                                      gsize       *offset,
                                      guint8       n_size_prefix_bytes,
                                      guint16      max_size,
                                      gchar      **out,
                                      GError     **error);

/**
 * qmi_message_tlv_read_fixed_size_string:
 * @self: a #QmiMessage.
 * @tlv_offset: offset that was returned by qmi_message_tlv_read_init().
 * @offset: (inout): address of a the offset within the TLV value.
 * @string_length: amount of bytes to read.
 * @out: (out): buffer preallocated by the client, with at least @string_length bytes.
 * @error: return location for error or %NULL.
 *
 * Reads a string from the TLV.
 *
 * The string written in @out will need to be NUL-terminated by the caller.
 *
 * @offset needs to point to a valid @gsize specifying the index to start
 * reading from within the TLV value (0 for the first item). If the variable
 * is successfully read, @offset will be updated to point past the read item.
 *
 * Since 1.24.6 the read string is guaranteed to be valid UTF-8.
 *
 * The fixed sized field may be suffixed with e.g. 0xFF if the contents are
 * shorter than @string_length. Since 1.26, this method will return the valid
 * UTF-8 characters in the start of the string, instead of returning an error
 * when doing the full UTF-8 string validation.
 *
 * Returns: %TRUE if the variable is successfully read, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.12
 */
gboolean qmi_message_tlv_read_fixed_size_string (QmiMessage  *self,
                                                 gsize        tlv_offset,
                                                 gsize       *offset,
                                                 guint16      string_length,
                                                 gchar       *out,
                                                 GError     **error);

#if defined (LIBQMI_GLIB_COMPILATION)
G_GNUC_INTERNAL
guint16 qmi_message_tlv_read_remaining_size (QmiMessage  *self,
                                             gsize        tlv_offset,
                                             gsize        offset);
#endif

/*****************************************************************************/
/* Raw TLV handling */

/**
 * QmiMessageForeachRawTlvFn:
 * @type: specific ID of the TLV.
 * @value: value of the TLV.
 * @length: length of the TLV, in bytes.
 * @user_data: user data.
 *
 * Callback type to use when iterating raw TLVs with
 * qmi_message_foreach_raw_tlv().
 *
 * Since: 1.0
 */
typedef void (* QmiMessageForeachRawTlvFn) (guint8        type,
                                            const guint8 *value,
                                            gsize         length,
                                            gpointer      user_data);

/**
 * qmi_message_foreach_raw_tlv:
 * @self: a #QmiMessage.
 * @func: (scope call): the function to call for each TLV.
 * @user_data: (closure func): user data to pass to the function.
 *
 * Calls the given function for each TLV found within the #QmiMessage.
 *
 * Since: 1.0
 */
void qmi_message_foreach_raw_tlv (QmiMessage                *self,
                                  QmiMessageForeachRawTlvFn  func,
                                  gpointer                   user_data);

/**
 * qmi_message_get_raw_tlv:
 * @self: a #QmiMessage.
 * @type: specific ID of the TLV to get.
 * @length: (out): return location for the length of the TLV.
 *
 * Get the raw data buffer of a specific TLV within the #QmiMessage.
 *
 * Returns: (transfer none): The raw data buffer of the TLV, or %NULL if not found.
 *
 * Since: 1.0
 */
const guint8 *qmi_message_get_raw_tlv (QmiMessage *self,
                                       guint8      type,
                                       guint16    *length);

/**
 * qmi_message_add_raw_tlv:
 * @self: a #QmiMessage.
 * @type: specific ID of the TLV to add.
 * @raw: raw data buffer with the value of the TLV.
 * @length: length of the raw data buffer.
 * @error: return location for error or %NULL.
 *
 * Creates a new @type TLV with the value given in @raw, and adds it to the #QmiMessage.
 *
 * Returns: %TRUE if the TLV is successfully added, otherwise %FALSE is returned and @error is set.
 *
 * Since: 1.0
 */
gboolean qmi_message_add_raw_tlv (QmiMessage   *self,
                                  guint8        type,
                                  const guint8 *raw,
                                  gsize         length,
                                  GError      **error);

/*****************************************************************************/
/* Other helpers */

/**
 * qmi_message_set_transaction_id:
 * @self: a #QmiMessage.
 * @transaction_id: transaction id.
 *
 * Overwrites the transaction ID of the message.
 *
 * Since: 1.8
 */
void qmi_message_set_transaction_id (QmiMessage *self,
                                     guint16 transaction_id);

#if defined (LIBQMI_GLIB_COMPILATION)
G_GNUC_INTERNAL
gboolean __qmi_message_is_abortable (QmiMessage        *self,
                                     QmiMessageContext *context);
#endif

/*****************************************************************************/
/* Printable helpers */

/**
 * qmi_message_get_printable_full:
 * @self: a #QmiMessage.
 * @context: a #QmiMessageContext.
 * @line_prefix: prefix string to use in each new generated line.
 *
 * Gets a printable string with the contents of the whole QMI message.
 *
 * If known, the printable string will contain translated TLV values as well as
 * the raw data buffer contents.
 *
 * The translation of the contents may be specific to the @context provided,
 * e.g. for vendor-specific messages.
 *
 * If no @context given, the behavior is the same as qmi_message_get_printable().
 *
 * Returns: (transfer full): a newly allocated string, which should be freed with g_free().
 *
 * Since: 1.18
 */
gchar *qmi_message_get_printable_full (QmiMessage        *self,
                                       QmiMessageContext *context,
                                       const gchar       *line_prefix);

/**
 * qmi_message_get_tlv_printable:
 * @self: a #QmiMessage.
 * @line_prefix: prefix string to use in each new generated line.
 * @type: type of the TLV.
 * @raw: raw data buffer with the value of the TLV.
 * @raw_length: length of the raw data buffer.
 *
 * Gets a printable string with the contents of the TLV.
 *
 * This method is the most generic one and doesn't try to translate the TLV contents.
 *
 * Returns: (transfer full): a newly allocated string, which should be freed with g_free().
 *
 * Since: 1.0
 */
gchar *qmi_message_get_tlv_printable (QmiMessage   *self,
                                      const gchar  *line_prefix,
                                      guint8        type,
                                      const guint8 *raw,
                                      gsize         raw_length);

G_END_DECLS

#endif /* _LIBQMI_GLIB_QMI_MESSAGE_H_ */
