/* -*- c++ -*- */
/* 
 * Copyright (c) 2021 Satlab A/S <satlab@satlab.com>
 * 
 * This 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.
 * 
 * This software 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 software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <endian.h>
#include <gnuradio/io_signature.h>
#include "idle_frame_gen_impl.h"

namespace gr {
    namespace satlab {

        idle_frame_gen::sptr
        idle_frame_gen::make(unsigned int size, unsigned int count, unsigned int max_in_flight)
        {
            return gnuradio::get_initial_sptr
                (new idle_frame_gen_impl(size, count, max_in_flight));
        }

        void idle_frame_gen_impl::handle_frame(pmt::pmt_t msg)
        {
            // Drop frame if max number of frames is in-flight
            if (d_in_flight >= d_max_in_flight)
                    return;

            // Otherwise, count frame as in-flight, reset idleframe count and
            // publish message on output
            d_in_flight++;
            d_count_remain = d_count;
            message_port_pub(pmt::mp("pdu"), msg);
        }

        void idle_frame_gen_impl::handle_trigger(pmt::pmt_t msg)
        {
            // One less frame in-flight
            if (d_in_flight > 0)
                d_in_flight--;

            // Publish idle frame on output if no other frames are in flight
            // and we haven't sent count idleframes since last data frame
            if (d_in_flight == 0 && d_count_remain > 0) {
                d_in_flight++;
                d_count_remain--;
                pmt::pmt_t vector_out = pmt::make_u8vector(d_size, 0);
                pmt::pmt_t idle = pmt::cons(pmt::PMT_NIL, vector_out);
                message_port_pub(pmt::mp("pdu"), idle);
            }
        }

        void idle_frame_gen_impl::set_size(unsigned int bytes)
        {
            d_size = bytes;
        }

        idle_frame_gen_impl::idle_frame_gen_impl(unsigned int size, unsigned int count, unsigned int max_in_flight)
            : gr::block("idle_frame_gen",
                    gr::io_signature::make(0, 0, 0),
                    gr::io_signature::make(0, 0, 0)),
            d_size(size),
            d_count(count),
            d_max_in_flight(max_in_flight),
            d_in_flight(0),
            d_count_remain(0)
        {
            message_port_register_out(pmt::mp("pdu"));

            message_port_register_in(pmt::mp("frame"));
            set_msg_handler(pmt::mp("frame"), [this](pmt::pmt_t msg) {
                this->handle_frame(msg);
            });

            message_port_register_in(pmt::mp("trigger"));
            set_msg_handler(pmt::mp("trigger"), [this](pmt::pmt_t msg) {
                this->handle_trigger(msg);
            });
        }
    } /* namespace satlab */
} /* namespace gr */
