//
// Copyright 2014-2016 Ettus Research LLC
//
// This program 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 of the License, or
// (at your option) any later version.
//
// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
//

#ifndef INCLUDED_LIBUHD_SINK_NODE_CTRL_BASE_HPP
#define INCLUDED_LIBUHD_SINK_NODE_CTRL_BASE_HPP

#include <uhd/rfnoc/node_ctrl_base.hpp>
#include <uhd/rfnoc/constants.hpp>
#include <uhd/rfnoc/sink_node_ctrl.hpp>
#include <boost/thread.hpp>

namespace uhd {
    namespace rfnoc {

/*! \brief Abstract class for sink nodes.
 *
 * Sink nodes can have upstream blocks.
 */
class UHD_RFNOC_API sink_node_ctrl;
class sink_node_ctrl : virtual public node_ctrl_base
{
public:
    /***********************************************************************
     * Types
     **********************************************************************/
    typedef boost::shared_ptr<sink_node_ctrl> sptr;
    typedef std::map< size_t, boost::weak_ptr<sink_node_ctrl> > node_map_t;
    typedef std::pair< size_t, boost::weak_ptr<sink_node_ctrl> > node_map_pair_t;

    /***********************************************************************
     * Sink block controls
     **********************************************************************/
    /*! Connect another node upstream of this node.
     *
     * *Note:* If additional settings are required to make this connection work,
     * e.g. configure flow control, these need to be done separately.
     *
     * If the requested connection is not possible, this function will throw.
     *
     * \p upstream_node Pointer to the node class to connect
     * \p port Suggested port number on this block to connect the upstream
     *         block to.
     * \p args Any arguments that can be useful for determining the port number.
     *
     * \returns The actual port number used.
     */
    size_t connect_upstream(
            node_ctrl_base::sptr upstream_node,
            size_t port=ANY_PORT,
            const uhd::device_addr_t &args=uhd::device_addr_t()
    );

    /*! Call this function to notify a node about its streamer activity.
     *
     * When \p active is set to true, this means this block is now part of
     * an active tx streamer chain. Conversely, when set to false, this means
     * the node has been removed from an tx streamer chain.
     */
    virtual void set_tx_streamer(bool active, const size_t port);


protected:

    /*! Ask for a port number to connect an upstream block to.
     *
     * Typically, this will be overridden for custom behaviour.
     * The default is to return the suggested port, disregarding
     * \p args, unless \p port == ANY_PORT, in which case the first
     * unused input port is returned.
     *
     * When deriving this function for custom behaviour, consider:
     * - The result is used to call register_upstream_node(), which
     *   has its own checks in place.
     * - This function may throw if the arguments can't be resolved.
     *   The exception will propagate to the user space.
     * - Alternatively, the function may return ANY_PORT to signify
     *   failure.
     * - \p args and \p suggested_port should be treated as strong
     *   suggestions, but there's no reason to just return any valid
     *   port.
     *
     * *Note:* For reasons of thread safety, it is recommended to
     * never, ever call this function directly. It will be used by
     * connect_upstream() which will handle the connection process
     * in a thread-safe manner.
     *
     * \param suggested_port Try and connect here.
     * \param args When deciding on a port number, these arguments may be used.
     *
     * \returns A valid input port, or ANY_PORT on failure.
     */
    virtual size_t _request_input_port(
            const size_t suggested_port,
            const uhd::device_addr_t &args
    ) const;

private:
    /*! Makes connecting something to the input thread-safe.
     */
    boost::mutex _input_mutex;

    /*! Register a node upstream of this one (i.e., a node that can send data to this node).
     *
     * By definition, the upstream node must of type source_node_ctrl.
     *
     * This saves a *weak pointer* to the upstream node and checks the port is
     * available. Will throw otherwise.
     *
     * \param upstream_node A pointer to the node instantiation
     * \param port Port number the upstream node is connected to
     */
    void _register_upstream_node(
            node_ctrl_base::sptr upstream_node,
            size_t port
    );

}; /* class sink_node_ctrl */

}} /* namespace uhd::rfnoc */

#endif /* INCLUDED_LIBUHD_SINK_NODE_CTRL_BASE_HPP */
// vim: sw=4 et:
