/* -*- c++ -*- */
/* Copyright 2016 Free Software Foundation, Inc.
 *
 * This file is part of GNU Radio
 *
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifndef INCLUDED_DIGITAL_HEADER_FORMAT_BASE_H
#define INCLUDED_DIGITAL_HEADER_FORMAT_BASE_H

#include <pmt/pmt.h>
#include <gnuradio/digital/api.h>
#include <gnuradio/digital/header_buffer.h>
#include <gnuradio/logger.h>
#include <boost/enable_shared_from_this.hpp>

namespace gr {
  namespace digital {

    /*!
     * \brief Base header formatter class.
     * \ingroup packet_operators_blk
     *
     * \details
     *
     * Creates a base class that other packet formatters will inherit
     * from. The child classes create and parse protocol-specific
     * headers. To add a new protocol processing class, create a class
     * that inherits from this and overload the necessary
     * functions. The main functions to overload are:
     *
     * \li header_format_base::format: takes in a payload and
     * creates a header from it.
     *
     * \li header_format_base::parse: receive bits and extract
     * the header info. These are expected to be hard bits (0 or 1)
     * that have either been sliced or gone through an FEC decoder.
     *
     * \li header_format_base::header_nbits: the number of bits
     * in the full header (including an access code).
     *
     * \li header_format_base::header_ok: checks to see if the
     * received header is ok. Since the header often specifies the
     * length of the frame to decode next, it is important that this
     * information be correct.
     *
     * \li header_format_base::header_payload: unpacks the header
     * register (from the class header_buffer) as a set of bits into
     * its component parts of the header. For example, this may find
     * and extract the frame length field as a 16-bit value and/or
     * flags related to the type of modulation and FEC codes used in
     * the frame's payload.
     *
     * Protected functions of this class that the child class should
     * overload include:
     *
     * \li enter_search
     * \li enter_have_sync
     * \li enter_have_header
     *
     * These three function represent the different states of the
     * parsing state machine. Expected behavior is that the protocol
     * has some known word that we are first looking for the identify
     * the start of the frame. The parsing FSM starts in a state to
     * search for the beginning of the header, normally by looking for
     * a known word (i.e., the access code). Then it changes state to
     * read in the full header. We expect that the protocol provides
     * the length of the header for processing, so the parsing looks
     * pulls in the full length of the header. Then it changes state
     * to the "have header" state for checking and processing. The
     * base class provides the basic functionality for this state
     * machine. However, most likely, each child class must manage
     * these states for themselves.
     *
     * This class is specifically designed to work with packets/frames
     * in the asynchronous PDU architecture of GNU Radio. See the
     * packet_format_async block for formatting the headers onto
     * payloads and packet_parse_b block for parsing headers in a
     * receiver.
     *
     * The Packet Format block takes in a PDU and uses a formatter
     * class derived from this class to add a header onto the
     * packet. The Packet Format blocks takes in the PDU, unpacks the
     * message, and passes it to a formatter class' format function,
     * which builds a header based on the payload. The header is
     * passed back and emitted from formatter block as a separate
     * output. The async format block, packet_format_async, has two
     * message output ports. The 'header' port passes the header out
     * as a PDU and the 'payload' passes the payload out as a PDU. The
     * flowgraph can then separately modulate and combine these two
     * pieces in the follow-on processing.
     *
     * The packet_sync_b block uses the formatter class by calling the
     * 'parse' function to parse the received packet headers. This
     * parser block is a sink for the data stream and emits a message
     * from an 'info' port that contains an PMT dictionary of the
     * information in the header. The formatter class determines the
     * dictionary keys.
     *
     * This is the base class for dealing with formatting headers for
     * different protocols and purposes. For other header formatting
     * behaviors, create a child class from here and overload the
     * format, parse, and parsing state machine functions as
     * necessary.
     *
     * \sa header_format_default
     * \sa header_format_counter
     */
    class DIGITAL_API header_format_base
      : public boost::enable_shared_from_this<gr::digital::header_format_base>
    {
     public:
      typedef boost::shared_ptr<header_format_base> sptr;

      header_format_base();
      virtual ~header_format_base();

      sptr base() { return shared_from_this(); };
      sptr formatter() { return shared_from_this(); };

      /*!
       * Function to creates a header. The child classes overload this
       * function to format the header in the protocol-specific way.
       *
       * \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.
       *
       * MUST be overloaded.
       */
      virtual bool format(int nbytes_in,
                          const unsigned char *input,
                          pmt::pmt_t &output,
                          pmt::pmt_t &info) = 0;

      /*!
       * Parses a header. This function is overloaded in the child
       * class, which knows how to convert the incoming hard bits (0's
       * and 1's) back into a packet header.
       *
       * \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.
       *
       * MUST be overloaded.
       */
      virtual bool parse(int nbits_in,
                         const unsigned char *input,
                         std::vector<pmt::pmt_t> &info,
                         int &nbits_processed) = 0;

      /*!
       * Returns the length of the formatted header in bits.
       * MUST be overloaded.
       */
      virtual size_t header_nbits() const = 0;

      /*!
       * Returns the length of the formatted header in bytes.
       * Auto-calculated from the overloaded header_nbits().
       */
      size_t header_nbytes() const;

    protected:
      enum state_t {STATE_SYNC_SEARCH, STATE_HAVE_SYNC};

      state_t d_state;          //!< state of the state machine
      header_buffer d_hdr_reg;  //!< header_buffer object to hold header bits
      pmt::pmt_t d_info;        //!< info captured from the header

      //! Enter Search state of the state machine to find the access code.
      virtual void enter_search();

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

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

      //! Verify that the header is valid
      virtual bool header_ok() = 0;

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

      /*! Used by blocks to access the logger system.
       */
      gr::logger_ptr d_logger;
      gr::logger_ptr d_debug_logger;
    };

  } // namespace digital
} // namespace gr

#endif /* INCLUDED_DIGITAL_HEADER_FORMAT_BASE_H */