/* -*- c++ -*- */
/* Copyright 2015-2016 Free Software Foundation, Inc.
 *
 * This file is part of GNU Radio
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 */

#ifndef INCLUDED_DIGITAL_HEADER_FORMAT_DEFAULT_H
#define INCLUDED_DIGITAL_HEADER_FORMAT_DEFAULT_H

#include <gnuradio/digital/api.h>
#include <gnuradio/digital/header_buffer.h>
#include <gnuradio/digital/header_format_base.h>
#include <gnuradio/logger.h>
#include <pmt/pmt.h>

namespace gr {
namespace digital {

/*!
 * \brief Default header formatter for PDU formatting.
 * \ingroup packet_operators_blk
 *
 * \details
 * Used to handle the default packet header.
 *
 * See the parent class header_format_base for details of how
 * these classes operate.
 *
 * The default header created in this base class consists of an
 * access code and the packet length. The length is encoded as a
 * 16-bit value repeated twice:
 *
 * \verbatim
     | access code | hdr | payload |
   \endverbatim
 *
 * Where the access code is <= 64 bits and hdr is:
 *
 * \verbatim
     |  0 -- 15 | 16 -- 31 |
     | pkt len  | pkt len  |
   \endverbatim
 *
 * The access code and header are formatted for network byte order.
 *
 * This header generator does not calculate or append a CRC to the
 * packet. Use the CRC32 Async block for that before adding the
 * header. The header's length will then measure the payload plus
 * the CRC length (4 bytes for a CRC32).
 *
 * The default header parser produces a PMT dictionary that
 * contains the following keys. All formatter blocks MUST produce
 * these two values in any dictionary.
 *
 * \li "payload symbols": the number of symbols in the
 * payload. The payload decoder will have to know how this relates
 * to the number of bits received. This block knows nothing about
 * the payload modulation or the number of bits/symbol. Use the
 * gr::digital::header_format_counter for that purpose.
 *
 * \sa header_format_counter
 * \sa header_format_crc
 * \sa header_format_ofdm
 */
class DIGITAL_API header_format_default : public header_format_base
{
public:
    typedef std::shared_ptr<header_format_default> sptr;
    header_format_default(const std::string& access_code, int threshold, int bps);
    ~header_format_default() override;

    /*!
     * Creates a header from the access code and packet length and
     * creates an output header as a PMT vector in the form:
     *
     * \verbatim
         | access code | pkt len | pkt len |
       \endverbatim
     *
     * \param nbytes_in The length (in bytes) of the \p input payload
     * \param input An array of unsigned chars of the packet payload
     * \param output A pmt::u8vector with the new header prepended
     *        onto the input data.
     * \param info A pmt::dict containing meta data and info about
     *        the PDU (generally from the metadata portion of the
     *        input PDU). Data can be extracted from this for the
     *        header formatting or inserted.
     */
    bool format(int nbytes_in,
                const unsigned char* input,
                pmt::pmt_t& output,
                pmt::pmt_t& info) override;

    /*!
     * Parses a header of the form:
     *
     * \verbatim
         | access code | pkt len | pkt len | payload |
       \endverbatim
     *
     * This is implemented as a state machine that starts off
     * searching for the access code. Once found, the access code is
     * used to find the start of the packet and the following
     * header. This default header encodes the length of the payload
     * a 16 bit integer twice. The state machine finds the header
     * and checks that both payload length values are the same. It
     * then goes into its final state that reads in the payload
     * (based on the payload length) and produces a payload as a PMT
     * u8 vector of packed bytes.
     *
     * \param nbits_in The number of bits in the input array.
     * \param input The input as hard decision bits.
     * \param info A vector of pmt::dicts to hold any meta data or
     *        info about the PDU. When parsing the header, the
     *        formatter can add info from the header into this dict.
     *        Each packet has a single PMT dictionary of info, so
     *        the vector length is the number of packets received
     *        extracted during one call to this parser function.
     * \param nbits_processed Number of input bits actually
     *        processed; If all goes well, this is nbits_in. A
     *        premature return after a bad header could be less than
     *        this.
     */
    bool parse(int nbits_in,
               const unsigned char* input,
               std::vector<pmt::pmt_t>& info,
               int& nbits_processed) override;

    /*!
     * Returns the length of the formatted header in bits.
     */
    size_t header_nbits() const override;

    /*!
     * Updates the access code. Must be a string of 1's and 0's and
     * <= 64 bits.
     */
    bool set_access_code(const std::string& access_code);

    /*!
     * Returns the formatted access code as a 64-bit register.
     */
    unsigned long long access_code() const;

    /*!
     * Sets the threshold for number of access code bits can be in
     * error before detection. Defaults to 0.
     */
    void set_threshold(unsigned int thresh = 0);

    /*!
     * Returns threshold value for access code detection.
     */
    unsigned int threshold() const;

    /*!
     * Factory to create an async packet header formatter; returns
     * an sptr to the object.
     *
     * \param access_code An access code that is used to find and
     * synchronize the start of a packet. Used in the parser and in
     * other blocks like a corr_est block that helps trigger the
     * receiver. Can be up to 64-bits long.
     * \param threshold How many bits can be wrong in the access
     * code and still count as correct.
     * \param bps The number of bits/second used in the payload's
     * modulator.
     */
    static sptr make(const std::string& access_code, int threshold, int bps = 1);

protected:
    uint64_t d_access_code;   //!< register to hold the access code
    size_t d_access_code_len; //!< length in bits of the access code

    uint16_t d_bps; //!< bits/sec of payload modulation

    unsigned long long d_data_reg; //!< used to look for access_code
    unsigned long long d_mask;     /*!< masks access_code bits (top N bits are set where
                                     N is the number of bits in the access code) */
    unsigned int d_threshold;      //!< how many bits may be wrong in sync vector

    int d_pkt_len;   //!< Length of the packet to put into the output buffer
    int d_pkt_count; //!< Number of bytes bits already received

    int d_nbits; //!< num bits processed since reset

    //! Access code found, start getting the header
    void enter_have_sync() override;

    //! Header found, setup for pulling in the hard decision bits
    void enter_have_header(int payload_len) override;

    //! Verify that the header is valid
    bool header_ok() override;

    /*! Get info from the header; return payload length and package
     *  rest of data in d_info dictionary.
     */
    int header_payload() override;
};

} // namespace digital
} // namespace gr

#endif /* INCLUDED_DIGITAL_HEADER_FORMAT_DEFAULT_H */
