// Copyright 2015-2022 The NATS Authors
// 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.

#ifndef MSG_H_
#define MSG_H_

#include "status.h"
#include "gc.h"

#define HDR_LINE_PRE        "NATS/1.0"
#define HDR_LINE_PRE_LEN    (8)
#define HDR_LINE            HDR_LINE_PRE _CRLF_
#define HDR_LINE_LEN        (10)
#define STATUS_HDR          "Status"
#define DESCRIPTION_HDR     "Description"
#define NO_RESP_STATUS      "503"
#define NOT_FOUND_STATUS    "404"
#define REQ_TIMEOUT         "408"
#define CTRL_STATUS         "100"
#define HDR_STATUS_LEN      (3)

#define natsMsg_setNeedsLift(m)     ((m)->flags  |=  (1 << 0))
#define natsMsg_needsLift(m)        (((m)->flags &   (1 << 0)) != 0)
#define natsMsg_clearNeedsLift(m)   ((m)->flags  &= ~(1 << 0))

#define natsMsg_setAcked(m)         ((m)->flags  |=  (1 << 1))
#define natsMsg_isAcked(m)          (((m)->flags &   (1 << 1)) != 0)
#define natsMsg_clearAcked(m)       ((m)->flags  &= ~(1 << 1))

#define natsMsg_setNoDestroy(m)     ((m)->flags  |=  (1 << 2))
#define natsMsg_isNoDestroy(m)      (((m)->flags &   (1 << 2)) != 0)
#define natsMsg_clearNoDestroy(m)   ((m)->flags  &= ~(1 << 2))

#define natsMsg_setTimeout(m)       ((m)->flags  |=  (1 << 3))
#define natsMsg_isTimeout(m)        (((m)->flags &   (1 << 3)) != 0)
#define natsMsg_clearTimeout(m)     ((m)->flags  &= ~(1 << 3))

#define natsMsg_dataAndHdrLen(m)    ((m)->dataLen + (m)->hdrLen)

struct __natsMsg
{
    natsGCItem          gc;

    // The message is allocated as a single memory block that contains
    // this structure and enough space for the headers/payload.
    // Headers, if any, start after the 'next' pointer, followed by
    // the message payload.
    const char          *subject;
    const char          *reply;
    char                *hdr;
    natsStrHash         *headers;
    const char          *data;
    int                 dataLen;
    int                 hdrLen;
    int                 wsz;
    int                 flags;
    uint64_t            seq;
    int64_t             time;

    // subscription (needed when delivery done by connection,
    // or for JetStream).
    struct __natsSubscription *sub;

    // Must be last field!
    struct __natsMsg    *next;

    // Nothing after this: the message headers/payload goes there.

};

struct __natsHeaderValue;

typedef struct __natsHeaderValue
{
    char                        *value;
    bool                        needFree;
    struct __natsHeaderValue    *next;

} natsHeaderValue;

int
natsMsgHeader_encodedLen(natsMsg *msg);

natsStatus
natsMsgHeader_encode(natsBuffer *buf, natsMsg *msg);

void
natsMsg_init(natsMsg *msg,
             const char *subject,
             const char *data, int dataLen);

natsStatus
natsMsg_create(natsMsg **newMsg,
               const char *subject, int subjLen,
               const char *reply, int replyLen,
               const char *buf, int bufLen, int hdrLen);

natsStatus
natsMsg_createWithPadding(natsMsg **newMsg,
                          const char *subject, int subjLen,
                          const char *reply, int replyLen,
                          const char *buf, int bufLen, int bufPaddingSize,
                          int hdrLen);

void
natsMsg_freeHeaders(natsMsg *msg);

// This needs to follow the nats_FreeObjectCb prototype (see gc.h)
void
natsMsg_free(void *object);

#endif /* MSG_H_ */
