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

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

#include "keep_one_in_n_impl.h"
#include <gnuradio/io_signature.h>

namespace gr {
namespace blocks {

keep_one_in_n::sptr keep_one_in_n::make(size_t itemsize, int n)
{
    return gnuradio::make_block_sptr<keep_one_in_n_impl>(itemsize, n);
}

keep_one_in_n_impl::keep_one_in_n_impl(size_t itemsize, int n)
    : block("keep_one_in_n",
            io_signature::make(1, 1, itemsize),
            io_signature::make(1, 1, itemsize)),
      d_count(n)
{
    // To avoid bad behavior with using set_relative_rate in this block with
    // VERY large values of n, we will keep track of things ourselves. Using
    // this to turn off automatic tag propagation, which will be handled
    // locally in general_work().
    set_tag_propagation_policy(TPP_DONT);

    set_n(n);
}

void keep_one_in_n_impl::set_n(int n)
{
    if (n < 1) {
        throw std::invalid_argument("N must be at least 1.");
    }

    d_n = n;
    d_count = n;

    // keep our internal understanding of the relative rate of this block
    // don't set the relative rate, though, and we will handle our own
    // tag propagation.
    d_decim_rate = 1.0 / (float)d_n;
}

int keep_one_in_n_impl::general_work(int noutput_items,
                                     gr_vector_int& ninput_items,
                                     gr_vector_const_void_star& input_items,
                                     gr_vector_void_star& output_items)
{
    const char* in = (const char*)input_items[0];
    char* out = (char*)output_items[0];

    size_t item_size = input_signature()->sizeof_stream_item(0);
    int ni = 0;
    int no = 0;

    while (ni < ninput_items[0] && no < noutput_items) {
        d_count--;
        if (d_count <= 0) {
            memcpy(out, in, item_size); // copy 1 item
            out += item_size;
            no++;
            d_count = d_n;
        }
        in += item_size;
        ni++;
    }

    // Because we have set TPP_DONT, we have to propagate the tags here manually.
    // Adjustment of the tag sample value is done using the float d_decim_rate.
    std::vector<tag_t> tags;
    get_tags_in_range(tags, 0, nitems_read(0), nitems_read(0) + ni);
    for (const auto& tag : tags) {
        tag_t new_tag = tag;
        new_tag.offset *= d_decim_rate;
        add_item_tag(0, new_tag);
    }

    consume_each(ni);
    return no;
}

} /* namespace blocks */
} /* namespace gr */