package Devel::Cover::Report::Text2;
use strict;
use warnings;

our $VERSION = '1.40'; # VERSION

use Devel::Cover::DB;
use Devel::Cover::Truth_Table;

my %format = (
    line      => "%4s ",
    err       => "%3s ",
    statement => "%4s ",
    condition => "%-24s ",
    branch    => "%-6s ",
    time      => "%6s ",
    code      => "| %s\n",
);

#-------------------------------------------------------------------------------
# Subroutine : headers()
# Purpose    : Determine field headers for report.
# Notes      :
#-------------------------------------------------------------------------------
sub headers {
    my ($db, $options) = @_;
    my ($fmt, @data);

    for (qw/line err/) {
        $fmt .= $format{$_};
        push @data, $_;
    }

    my %cr;
    @cr{$db->criteria} = $db->criteria_short;
    foreach my $c ($db->criteria) {
        next unless $options->{show}{$c};
        $fmt .= $format{$c};
        push @data, $cr{$c};
    }
    $fmt .= $format{code};
    push @data, 'code';

    return $fmt, @data;
}


#-------------------------------------------------------------------------------
# Subroutine : get_metrics()
# Purpose    : Determine which metrics to include in report.
# Notes      :
#-------------------------------------------------------------------------------
sub get_metrics {
    my ($db, $options, $file_data, $line) = @_;
    my %m;

    for my $c ($db->criteria) {                   # find all metrics available in db
        next unless $options->{show}{$c};         # skip those we don't want in report
        my $criterion = $file_data->$c();         # check if metric collected for this file
        if ($criterion) {                         # if it exists...
            my $li = $criterion->location($line); #   get the metric info for the current line
            $m{$c} = $li ? [@$li] : undef;        #   and stash it
        }
    }
    return %m;
}


#-------------------------------------------------------------------------------
# Subroutine : print_file()
# Purpose    : Print report for file.
# Notes      :
#-------------------------------------------------------------------------------
sub print_file {
    my ($db, $file, $options) = @_;

    open(F, '<', $file) or warn("Unable to open '$file' [$!]\n"), return;

    my $pct  = sprintf("%.1f%%", $db->{summary}{$file}{total}{percentage});
    my $pver = join('.', map {ord} split(//, $^V));
    print <<EOT;
#         File: $file
#     Coverage: $pct
# Perl Version: $pver
#     Platform: $^O

EOT

    my ($fmt, @out) = headers($db, $options);
    printf $fmt, @out;

    my $file_data = $db->cover->file($file);
    while (my $line = <F>) {
        chomp $line;

        my $error;
        my %metric = get_metrics($db, $options, $file_data, $.);
        my @out    = ([$.], ['']);

        foreach my $c ($db->criteria) {
            next unless $options->{show}{$c};
            push(@out, []), next unless $metric{$c};

            my $value = [];
            if ($c eq 'branch') {
                @$value  = $file_data->branch->branch_coverage($.);
                $error ||= $file_data->branch->error($.);
            } elsif ($c eq 'condition') {
                @$value  = map {$_->[0]->text}
                               $file_data->condition->truth_table($.);
                $error ||= $file_data->condition->error($.);
            } else {
                while (my $o = shift @{$metric{$c}}) {
                    push @$value, ($c =~ /statement|pod|time/)
                    ? $o->covered : $o->percentage;
                    $error ||= $o->error;
                }
            }
            push @out, $value;
        }

        $out[1] = ['***'] if $error; # flag missing coverage
        push @out, [$line];

        foreach my $i (0 .. max(map {$#$_} @out)) {
            no warnings 'uninitialized';
            printf $fmt, map{$_->[$i]} @out;
        }

        last if $line =~ /^__(END|DATA)__/;
    }
    close F or die "Unable to close '$file' [$!]";
    print "\n\n";
}


#-------------------------------------------------------------------------------
# Subroutine : max()
# Purpose    : Return the maximum from a list of numbers.
# Notes      :
#-------------------------------------------------------------------------------
sub max {
    my $max = shift;
    foreach (@_) {
        $max = $_ if $_ > $max;
    }
    return $max;
}


#-------------------------------------------------------------------------------
# Subroutine : report()
# Purpose    : Entry point for creating textual reports.
# Notes      :
#-------------------------------------------------------------------------------
sub report {
    my ($pkg, $db, $options) = @_;
    foreach my $file (@{$options->{file}}) {
        print_file($db, $file, $options);
    }
}

1;

__END__

=head1 NAME

Devel::Cover::Report::Test2 - Text backend for Devel::Cover

=head1 VERSION

version 1.40

=head1 SYNOPSIS

 cover -report text2

=head1 DESCRIPTION

This module provides a textual reporting mechanism for coverage data.
It is designed to be called from the C<cover> program.

=head1 SEE ALSO

 Devel::Cover

=head1 BUGS

Huh?

=head1 LICENCE

Copyright 2001-2023, Paul Johnson (paul@pjcj.net)

This software is free.  It is licensed under the same terms as Perl itself.

The latest version of this software should be available from my homepage:
http://www.pjcj.net

=cut
