/*
 * ggcov - A GTK frontend for exploring gcov coverage data
 * Copyright (c) 2003-2020 Greg Banks <gnb@fastmail.fm>
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "cov_priv.H"
#include "logging.H"

static logging::logger_t &_log = logging::find_logger("suppress");

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

cov_line_t::cov_line_t()
{
}

cov_line_t::~cov_line_t()
{
    blocks_.remove_all();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

cov_function_t *
cov_line_t::function() const
{
    if (!blocks_.head())
	return 0;
    return blocks_.head()->function();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

void
cov_line_t::calculate_count()
{
    count_t minc = COV_COUNT_MAX, maxc = 0;
    unsigned int nsupp = 0;
    unsigned int len = 0;

    assert(!count_valid_);
    count_valid_ = true;

    /*
     * TODO: implement the new smarter algorithm from gcov 3.3 here
     */
    for (list_iterator_t<cov_block_t> itr = blocks_.first() ; *itr ; ++itr)
    {
	cov_block_t *b = *itr;

	len++;
	if (b->is_suppressed())
	{
	    nsupp++;
	    continue;
	}
	if (b->count() > maxc)
	    maxc = b->count();
	if (b->count() < minc)
	    minc = b->count();
    }

    count_ = maxc;

    if (len == 0)
	status_ = cov::UNINSTRUMENTED;
    else if (nsupp == len)
	status_ = cov::SUPPRESSED;
    else if (maxc == 0)
	status_ = cov::UNCOVERED;
    else
	status_ = (minc == 0 ? cov::PARTCOVERED : cov::COVERED);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

void
cov_line_t::suppress(const cov_suppression_t *s)
{
    if (s && !suppression_)
    {
	count_valid_ = true;
	status_ = cov::SUPPRESSED;
	_log.debug("suppressing line: %s\n", s->describe());
	suppression_ = s;

	/* suppress any arcs out of blocks on this line */
	for (list_iterator_t<cov_block_t> biter = blocks_.first() ; *biter ; ++biter)
	    (*biter)->suppress(s);
	/* TODO: suppress any blocks wholly on this line??? */
    }
}

void
cov_line_t::finalise()
{
    if (!suppression_)
    {
	/* suppress lines, where all the blocks
	 * on the line are suppressed */
	cov_suppression_combiner_t c(cov_suppressions);
	for (list_iterator_t<cov_block_t> biter = blocks_.first() ; *biter ; ++biter)
	    c.add((*biter)->suppression_);
	suppress(c.result());
    }
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

unsigned int
cov_line_t::format_blocks(char *buf, unsigned int maxlen)
{
    unsigned int len;
    unsigned int start = 0, end = 0;
    char *base = buf;

    for (list_iterator_t<cov_block_t> itr = blocks_.first() ;
	 *itr && maxlen > 0 ;
	 ++itr)
    {
	cov_block_t *b = *itr;

	assert(b->bindex() != 0);
	if (start > 0 && b->bindex() == end+1)
	{
	    end++;
	    continue;
	}
	if (start == 0)
	{
	    start = end = b->bindex();
	    continue;
	}

	if (start == end)
	    snprintf(buf, maxlen, "%u,", start);
	else
	    snprintf(buf, maxlen, "%u%c%u,",
		    start, (end == start+1 ? ',' : '-'), end);
	len = strlen(buf);
	buf += len;
	maxlen -= len;
	start = end = b->bindex();
    }

    if (maxlen > 0 && start > 0)
    {
	if (start == end)
	    snprintf(buf, maxlen, "%u", start);
	else
	    snprintf(buf, maxlen, "%u%c%u",
			start, (end == start+1 ? ',' : '-'), end);
	len = strlen(buf);
	buf += len;
	maxlen -= len;
    }

    *buf = '\0';

    return (buf - base);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*END*/
