// Copyright 2018 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.

#include "msg.h"

uint64_t
stanMsg_GetSequence(const stanMsg *msg)
{
    if (msg == NULL)
        return 0;

    return msg->seq;
}

const char*
stanMsg_GetData(const stanMsg *msg)
{
    if (msg == NULL)
        return NULL;

    return (const char*) msg->data;
}

int
stanMsg_GetDataLength(const stanMsg *msg)
{
    if (msg == NULL)
        return 0;

    return msg->dataLen;
}

int64_t
stanMsg_GetTimestamp(const stanMsg *msg)
{
    if (msg == NULL)
        return 0;

    return msg->timestamp;
}

bool
stanMsg_IsRedelivered(const stanMsg *msg)
{
    if (msg == NULL)
        return false;

    return msg->redelivered;
}

static void
stanMsg_free(void *object)
{
    stanMsg *msg;

    if (object == NULL)
        return;

    msg = (stanMsg*) object;

    NATS_FREE(msg);
}

void
stanMsg_Destroy(stanMsg *msg)
{
    if (msg == NULL)
        return;

    if (natsGC_collect((natsGCItem *) msg))
        return;

    stanMsg_free((void*) msg);
}

natsStatus
stanMsg_create(stanMsg **newMsg, stanSubscription *sub, Pb__MsgProto *pb)
{
    stanMsg *msg        = NULL;
    char    *ptr        = NULL;
    int     payloadSize = 0;

    payloadSize = (int) pb->data.len;
    msg = NATS_MALLOC(sizeof(stanMsg) + payloadSize + 1);
    if (msg == NULL)
        return nats_setDefaultError(NATS_NO_MEMORY);

    // To be safe, we could 'memset' the message up to sizeof(stanMsg),
    // but since we are explicitly initializing most of the fields, we save
    // on that call, but we need to make sure what we initialize all fields!!!

    // That being said, we memset the 'gc' structure to protect us in case
    // some fields are later added to this 'external' structure and we forget
    // about updating this initialization code.
    memset(&(msg->gc), 0, sizeof(natsGCItem));

    msg->seq         = pb->sequence;
    msg->timestamp   = pb->timestamp;
    msg->redelivered = pb->redelivered;
    msg->sub         = sub;

    ptr = (char*) (((char*) &(msg->sub)) + sizeof(msg->sub));
    msg->data    = (const char*) ptr;
    msg->dataLen = payloadSize;
    memcpy(ptr, (char*) pb->data.data, payloadSize);
    ptr += payloadSize;
    *(ptr) = '\0';

    // Setting the callback will trigger garbage collection when
    // stanMsg_Destroy() is invoked.
    msg->gc.freeCb = stanMsg_free;

    *newMsg = msg;

    return NATS_OK;
}
