/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 QPID_LINEARSTORE_JOURNAL_JREC_H
#define QPID_LINEARSTORE_JOURNAL_JREC_H

#include <fstream>
#include "qpid/linearstore/journal/jcfg.h"
#include <stdint.h>

struct rec_hdr_t;

namespace qpid {
namespace linearstore {
namespace journal {

class Checksum;

/**
* \class jrec
* \brief Abstract class for all file jrecords, both data and log. This class establishes
*     the common data format and structure for these jrecords.
*/
class jrec
{
public:
    jrec() {}
    virtual ~jrec() {}

    /**
    * \brief Encode this instance of jrec into the write buffer at the disk-block-aligned
    *   pointer wptr starting at position rec_offs_dblks in the encoded record to a
    *   maximum size of max_size_dblks.
    *
    * This call encodes the content of the data contianed in this instance of jrec into a
    * disk-softblock-aligned (defined by JRNL_SBLK_SIZE) buffer pointed to by parameter
    * wptr. No more than paramter max_size_dblks data-blocks may be written to the buffer.
    * The parameter rec_offs_dblks is the offset in data-blocks within the fully encoded
    * data block this instance represents at which to start encoding.
    *
    * Encoding entails writing the record header (struct enq_hdr), the data and the record tail
    * (struct enq_tail). The record must be data-block-aligned (defined by JRNL_DBLK_SIZE),
    * thus any remaining space in the final data-block is ignored; the returned value is the
    * number of data-blocks consumed from the page by the encode action. Provided the initial
    * alignment requirements are met, records may be of arbitrary size and may span multiple
    * data-blocks, disk-blocks and/or pages.
    *
    * Since the record size in data-blocks is known, the general usage pattern is to call
    * encode() as many times as is needed to fully encode the data. Each call to encode()
    * will encode as much of the record as it can to what remains of the current page cache,
    * and will return the number of data-blocks actually encoded.
    *
    * <b>Example:</b> Assume that record r1 was previously written to page 0, and that this
    * is an instance representing record r2. Being larger than the page size ps, r2 would span
    * multiple pages as follows:
    * <pre>
    *       |<---ps--->|
    *       +----------+----------+----------+----...
    *       |      |r2a|   r2b    |  r2c   | |
    *       |<-r1-><----------r2---------->  |
    *       +----------+----------+----------+----...
    * page:      p0         p1         p2
    * </pre>
    * Encoding record r2 will require multiple calls to encode; one for each page which
    * is involved. Record r2 is divided logically into sections r2a, r2b and r2c at the
    * points where the page boundaries intersect with the record. Assuming a page size
    * of ps, the page boundary pointers are represented by their names p0, p1... and the
    * sizes of the record segments are represented by their names r1, r2a, r2b..., the calls
    * should be as follows:
    * <pre>
    * encode(p0+r1, 0, ps-r1); (returns r2a data-blocks)
    * encode(p1, r2a, ps);     (returns r2b data-blocks which equals ps)
    * encode(p2, r2a+r2b, ps); (returns r2c data-blocks)
    * </pre>
    *
    * \param wptr Data-block-aligned pointer to position in page buffer where encoding is to
    *   take place.
    * \param rec_offs_dblks Offset in data-blocks within record from which to start encoding.
    * \param max_size_dblks Maximum number of data-blocks to write to pointer wptr.
    * \returns Number of data-blocks encoded.
    */
    virtual uint32_t encode(void* wptr, uint32_t rec_offs_dblks, uint32_t max_size_dblks, Checksum& checksum) = 0;
    virtual bool decode(::rec_hdr_t& h, std::ifstream* ifsp, std::size_t& rec_offs) = 0;

    virtual std::string& str(std::string& str) const = 0;
    virtual std::size_t data_size() const = 0;
    virtual std::size_t xid_size() const = 0;
    virtual std::size_t rec_size() const = 0;
    inline virtual uint32_t rec_size_dblks() const { return size_dblks(rec_size()); }
    static inline uint32_t size_dblks(const std::size_t size)
            { return size_blks(size, QLS_DBLK_SIZE_BYTES); }
    static inline uint32_t size_sblks(const std::size_t size)
            { return size_blks(size, QLS_SBLK_SIZE_BYTES); }
    static inline uint32_t size_blks(const std::size_t size, const std::size_t blksize)
            { return (size + blksize - 1)/blksize; }
    virtual uint64_t rid() const = 0;

protected:
    virtual void clean() = 0;
};

}}}

#endif // ifndef QPID_LINEARSTORE_JRNL_JREC_H
