/* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */

#ifndef GCS_PLUGIN_MESSAGES_INCLUDED
#define	GCS_PLUGIN_MESSAGES_INCLUDED

#include <string>
#include <vector>

/*
  Since this file is used on unit tests, through member_info.h,
  includes must set here and not through plugin_server_include.h.
*/
#include <my_global.h>

/**
 This is the base GCS plugin message.
 It is composed by a fixed header and 1 or more payload items.

 The on-the-wire layout looks like this:

   +-----------------------------------+
   | fixed header |          payload   |
   +-----------------------------------+

 The on-the-wire representation of the message is:

  +-------------------+-----------+--------------------------------------+
  | field             | wire size | description                          |
  +===================+===========+======================================+
  | version           |   4 bytes | protocol version                     |
  | fixed_hdr_len     |   2 bytes | length of the fixed header           |
  | message_len       |   8 bytes | length of the message                |
  | cargo_type        |   2 bytes | the cargo type in the payload        |
  +-------------------+-----------+--------------------------------------+
  | payload_item_type |   2 bytes | the item type in the payload         |
  | payload_item_len  |   8 bytes | length of the payload item           |
  | payload_item      |   X bytes | payload item                         |
  +-------------------+-----------+--------------------------------------+

 The last tree lines can occur one or more times.
*/

class Plugin_gcs_message
{
public:
  /**
   The protocol version number.
   */
  static const int PLUGIN_GCS_MESSAGE_VERSION;

  /**
   The protocol version number.
   */
  static const unsigned int WIRE_VERSION_SIZE;

  /**
   The on-the-wire size of the header length field.
   */
  static const unsigned int WIRE_HD_LEN_SIZE;

  /**
   The on-the-wire size of the message size field.
   */
  static const unsigned int WIRE_MSG_LEN_SIZE;

  /**
   The on-the-wire size of the cargo type field.
   */
  static const unsigned int WIRE_CARGO_TYPE_SIZE;

  /**
   The on-the-wire size of the fixed header.
   */
  static const unsigned int WIRE_FIXED_HEADER_SIZE;

  /**
   The on-the-wire size of the each payload item type field.
   */
  static const unsigned int WIRE_PAYLOAD_ITEM_TYPE_SIZE;

  /**
   The on-the-wire size of the each payload item size field.
   */
  static const unsigned int WIRE_PAYLOAD_ITEM_LEN_SIZE;

  /**
   The on-the-wire size of the payload item header.
   */
  static const unsigned int WIRE_PAYLOAD_ITEM_HEADER_SIZE;

  /**
   The different cargo type codes.

   NOTE: all type values must fit into WIRE_CARGO_TYPE_SIZE bytes storage.
   */
  enum enum_cargo_type
  {
    // This type should not be used anywhere.
    CT_UNKNOWN= 0,

    // This cargo type is used for certification events, GTID_EXECUTED broadcast.
    CT_CERTIFICATION_MESSAGE= 1,

    // This cargo type is used for transaction data.
    CT_TRANSACTION_MESSAGE= 2,

    // This cargo type is used for recovery events, signal when a given member
    // becomes online.
    CT_RECOVERY_MESSAGE= 3,

    // This cargo type is used for messaging related to stage exchanges,
    // on which it represents one member.
    CT_MEMBER_INFO_MESSAGE= 4,

    // This cargo type is used for messaging related to stage exchanges,
    // on which it represents a set of members.
    CT_MEMBER_INFO_MANAGER_MESSAGE= 5,

    // This cargo type is used for messaging related to members pipeline
    // stats.
    CT_PIPELINE_STATS_MEMBER_MESSAGE= 6,

    // This cargo type is used for messaging related to single primary
    // mode.
    CT_SINGLE_PRIMARY_MESSAGE= 7,

    // No valid type codes can appear after this one.
    CT_MAX= 8
  };

private:
  /**
   This header instance protocol version.
   */
  int m_version;

  /**
   This header instance length.
   */
  unsigned short m_fixed_header_len;

  /**
   This is the message length field.
   */
  unsigned long long m_msg_len;

  /**
   The cargo type code.
   */
  enum_cargo_type m_cargo_type;

public:
  virtual ~Plugin_gcs_message() {}

  /**
   @return the value of the version field.
   */
  int get_version() { return m_version; }

  /**
   @return the value of the header length field value.
   */
  unsigned short get_header_length() { return m_fixed_header_len; }

  /**
   @return the cargo type.
   */
  enum_cargo_type get_cargo_type() { return m_cargo_type; }

  /**
   @return the message length field value.
   */
  unsigned long long get_msg_length() { return m_msg_len; }

  /**
    Encodes the contents of this instance into the buffer.

    @param[out] buffer the buffer to encode to.
  */
  void encode(std::vector<unsigned char>* buffer) const;

  /**
    Decodes the contents of the buffer and sets the field values
    according to the values decoded.

    @param[in] buffer the buffer to decode from.
    @param[in] length the length of the buffer.
  */
  void decode(const unsigned char* buffer, uint64 length);

  /**
    Return the cargo type of a given message buffer, without decode
    the complete message.

    @param[in] buffer the buffer to decode from.

    @return the cargo type of a given message buffer
   */
  static enum_cargo_type get_cargo_type(const unsigned char* buffer);

  /**
    Return the raw data of the first payload item of a given message buffer,
    without decode the complete message.

    @param[out] buffer              the buffer to decode from.
    @param[out] payload_item_data   the data.
    @param[out] payload_item_length the length of the data.

    @return the raw data of the first payload item
  */
  static void get_first_payload_item_raw_data(const unsigned char* buffer,
                                              const unsigned char** payload_item_data,
                                              uint64* payload_item_length);

protected:
  /**
    Plugin_gcs_message constructor. Only to be called by derivative classes

    @param[in] cargo_type Message type to be sent
   */
  explicit Plugin_gcs_message(enum_cargo_type cargo_type);

  /**
    Encodes the contents of this instance payload into the buffer.

    @param[out] buffer the buffer to encode to.
  */
  virtual void encode_payload(std::vector<unsigned char>* buffer) const = 0;

  /**
    Decodes the contents of the buffer and sets the payload field
    values according to the values decoded.

    @param[in] buffer the buffer to decode from.
    @param[in] end    the end of the buffer.
  */
  virtual void decode_payload(const unsigned char* buffer,
                              const unsigned char* end)= 0;

  /**
    Encodes the given payload item type and length into the buffer.

    @param[out] buffer              the buffer to encode to
    @param[in]  payload_item_type   the type of the payload item
    @param[in]  payload_item_length the length of the payload item
  */
  void encode_payload_item_type_and_length(std::vector<unsigned char>* buffer,
                                           uint16 payload_item_type,
                                           unsigned long long payload_item_length)
                                           const;

  /**
    Decodes the given payload item type and length from the buffer.

    @param[in]  buffer              the buffer to encode from
    @param[out] payload_item_type   the type of the payload item
    @param[out] payload_item_length the length of the payload item
  */
  void decode_payload_item_type_and_length(const unsigned char** buffer,
                                           uint16* payload_item_type,
                                           unsigned long long* payload_item_length);

  /**
    Encodes the given payload item (type, length and value) into the buffer as
    a char (1 byte).

    @param[out] buffer the buffer to encode to
    @param[in]  type   the type of the payload item
    @param[in]  value  the value of the payload item
  */
  void encode_payload_item_char(std::vector<unsigned char>* buffer,
                                uint16 type,
                                unsigned char value)
                                const;

  /**
    Decodes the given payload item (type, length and value) from the buffer as
    a char (1 byte).

    @param[in]  buffer the buffer to encode from
    @param[out] type   the type of the payload item
    @param[out] value  the value of the payload item
  */
  void decode_payload_item_char(const unsigned char** buffer,
                                uint16* type,
                                unsigned char* value);

  /**
    Encodes the given payload item (type, length and value) into the buffer as
    a 2 bytes integer.

    @param[out] buffer the buffer to encode to
    @param[in]  type   the type of the payload item
    @param[in]  value  the value of the payload item
  */
  void encode_payload_item_int2(std::vector<unsigned char>* buffer,
                                uint16 type,
                                uint16 value)
                                const;

  /**
    Decodes the given payload item (type, length and value) from the buffer as
    a 2 bytes integer.

    @param[in]  buffer the buffer to encode from
    @param[out] type   the type of the payload item
    @param[out] value  the value of the payload item
  */
  void decode_payload_item_int2(const unsigned char** buffer,
                                uint16* type,
                                uint16* value);

  /**
    Encodes the given payload item (type, length and value) into the buffer as
    a 4 bytes integer.

    @param[out] buffer the buffer to encode to
    @param[in]  type   the type of the payload item
    @param[in]  value  the value of the payload item
  */
  void encode_payload_item_int4(std::vector<unsigned char>* buffer,
                                uint16 type,
                                uint32 value)
                                const;

  /**
    Decodes the given payload item (type, length and value) from the buffer as
    a 4 bytes integer.

    @param[in]  buffer the buffer to encode from
    @param[out] type   the type of the payload item
    @param[out] value  the value of the payload item
  */
  void decode_payload_item_int4(const unsigned char** buffer,
                                uint16* type,
                                uint32* value);

  /**
    Encodes the given payload item (type, length and value) into the buffer as
    a 8 bytes integer.

    @param[out] buffer the buffer to encode to
    @param[in]  type   the type of the payload item
    @param[in]  value  the value of the payload item
  */
  void encode_payload_item_int8(std::vector<unsigned char>* buffer,
                                uint16 type,
                                ulonglong value)
                                const;

  /**
    Decodes the given payload item (type, length and value) from the buffer as
    a 8 bytes integer.

    @param[in]  buffer the buffer to encode from
    @param[out] type   the type of the payload item
    @param[out] value  the value of the payload item
  */
  void decode_payload_item_int8(const unsigned char** buffer,
                                uint16* type,
                                ulonglong* value);

  /**
    Encodes the given payload item (type, length and value) into the buffer as
    a char array (variable size).

    @param[out] buffer the buffer to encode to
    @param[in]  type   the type of the payload item
    @param[in]  value  the value of the payload item
    @param[in]  length the length of the payload item
  */
  void encode_payload_item_string(std::vector<unsigned char>* buffer,
                                  uint16 type,
                                  const char* value,
                                  unsigned long long length)
                                  const;

  /**
    Decodes the given payload item (type, length and value) from the buffer as
    a char array (variable size).

    @param[in]  buffer the buffer to encode from
    @param[out] type   the type of the payload item
    @param[out] value  the value of the payload item
    @param[out] length the length of the payload item
  */
  void decode_payload_item_string(const unsigned char** buffer,
                                  uint16* type,
                                  std::string* value,
                                  unsigned long long* length);
};

#endif	/* GCS_PLUGIN_MESSAGES_INCLUDED */
