/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2014 Couchbase, Inc.
 *
 *   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 LCB_PACKETUTILS_H
#define LCB_PACKETUTILS_H

#ifdef __cplusplus
extern "C" {
#endif

#include <libcouchbase/couchbase.h>
#include "ringbuffer.h"
#include "rdb/rope.h"
#include "memcached/protocol_binary.h"

/**
 * Response packet informational structure.
 *
 * This contains information regarding the response packet which is used by
 * the response processors.
 */
typedef struct packet_info_st {
    /** The response header */
    protocol_binary_response_header res;
    /** The payload of the response. This should only be used if there is a body */
    void *payload;
    /** Segment for payload */
    void *bufh;
} packet_info;

/**
 * Gets the size of the _total_ non-header part of the packet. This data is
 * also featured inside the payload field itself.
 */
#define PACKET_NBODY(pkt) (ntohl((pkt)->res.response.bodylen))

#define PACKET_BODY(pkt) (pkt)->payload

/**
 * Gets the key size, if included in the packet.
 */
#define PACKET_NKEY(pkt) (ntohs((pkt)->res.response.keylen))

/**
 * Gets the status of the packet
 */
#define PACKET_STATUS(pkt) (ntohs((pkt)->res.response.status))

/**
 * Gets the length of the 'extras' in the body
 */
#define PACKET_EXTLEN(pkt) ((pkt)->res.response.extlen)

/**
 * Gets the raw unconverted 'opaque' 32 bit field
 */
#define PACKET_OPAQUE(pkt) ((pkt)->res.response.opaque)

/**
 * Gets the command for the packet
 */
#define PACKET_OPCODE(pkt) ((pkt)->res.response.opcode)

/**
 * Gets the CAS for the packet
 */
#define PACKET_CAS(pkt) ((pkt)->res.response.cas)

/**
 * Gets the 'datatype' field for the packet.
 */
#define PACKET_DATATYPE(pkt) ((pkt)->res.response.datatype)

/**
 * Gets a pointer starting at the packet's key field. Only use if NKEY is 0
 */
#define PACKET_KEY(pkt) \
    ( ((const char *)(pkt)->payload) + PACKET_EXTLEN(pkt))

#define PACKET_REQUEST(pkt) \
    ( (protocol_binary_request_header *) &(pkt)->res)

#define PACKET_REQ_VBID(pkt) \
    (ntohs(PACKET_REQUEST(pkt)->request.vbucket))

/**
 * Gets a pointer starting at the packet's value field. Only use if NVALUE is 0
 */
#define PACKET_VALUE(pkt) \
    ( ((const char *) (pkt)->payload) + PACKET_NKEY(pkt) + PACKET_EXTLEN(pkt))

/**
 * Gets the size of the packet value. The value is the part of the payload
 * which is after the key (if applicable) and extras (if applicable).
 */
#define PACKET_NVALUE(pkt) \
    (PACKET_NBODY(pkt) - (PACKET_NKEY(pkt) + PACKET_EXTLEN(pkt)))


/**
 * Map a command 'subclass' so that its body field starts at the payload. Note
 * that the return value is actually an ephemeral pointer starting 24 bytes
 * _before_ the actual memory block, so only use the non-header part.
 */
#define PACKET_EPHEMERAL_START(pkt) \
    (const void *)(( ((const char *)(pkt)->payload) - 24 ))

/**
 * Read from an 'IOR' structure to parse the packet information. This will
 * always load a full packet.
 *
 * @param info the info structure to populate
 * @param ior the rope structure to read from
 * @param[out] required how much total bytes must remain in the buffer for the
 *  parse to complete.
 *
 * @return zero if more data is needed, a true value otherwise
 */
int
lcb_pktinfo_ior_get(packet_info *info, rdb_IOROPE *ior, unsigned *required);

void
lcb_pktinfo_ior_done(packet_info *info, rdb_IOROPE *ior);

#define lcb_pktinfo_ectx_get(info, ctx, n) lcb_pktinfo_ior_get(info, &(ctx)->ior, n)
#define lcb_pktinfo_ectx_done(info, ctx) lcb_pktinfo_ior_done(info, &(ctx)->ior)



#ifdef __cplusplus
}
#endif

#endif
