#!/usr/local/bin/perl -w

use Date::Format;

use App::Options (
    options => [ qw(dbhost dbname dbuser dbpass repository table params columns headings compact verbose) ],
    option => {
        repository => {
            default => "default",
            description => "Name of the repository to get the rows from",
        },
        table => {
            required => 1,
            description => "Table name (i.e. --table=customer)",
        },
        params => {
            required => 1,
            description => "List of params (var=value pairs) (i.e. --params=\"last_name=Jones|first_name=Mike\")",
        },
        columns => {
            description => "List of columns (comma-separated list) (i.e. --columns=first_name,last_name)",
        },
        headings => {
            description => "List of heading abbreviations (comma-separated) (i.e. --headings=first,last)",
        },
        compact => {
            description => "Trim titles to make compact table",
        },
        verbose => {
            default => 1,
            description => "Verbose level",
        },
    },
);

use App;
use strict;

$| = 1;  # autoflush stdout

{
    my $context  = App->context();
    my $db       = $context->repository($App::options{repository});
    my $table    = $App::options{table};
    my ($columns);
    if ($App::options{columns}) {
        $columns  = [ split(/,/, $App::options{columns}) ];
    }
    else {
        $columns  = $db->_get_default_columns($table);
    }
    my $params   = { split(/[=>\|]+/, $App::options{params}) };
    my $headings = $App::options{headings} ? [ split(/,/, $App::options{headings}) ] : [];
    my $verbose  = $App::options{verbose};
    my $rows     = $db->get_rows($table, $params, $columns);
    my $formats  = [];
    &print_table($rows, $columns, $formats, { compact => $App::options{compact}, headings => $headings });
}

sub print_table {
    my ($rows, $columns, $formats, $options) = @_;
    my ($row, $r, $c, $elem, $format, $len, $f, $heading);
    my (@autoformat);
    my $headings = $options->{headings};
    my $max_columns = 0;
    for ($r = 0; $r <= $#$rows; $r++) {
        $row = $rows->[$r];
        if ($max_columns < $#$row + 1) {
            $max_columns = $#$row + 1;
        }
    }
    for ($c = 0; $c < $max_columns; $c++) {
        if (! defined $autoformat[$c]) {
            $autoformat[$c] = {
                max_length => 0,
                type => 0,        # 0=string, 1=integer, 2=float
                min => undef,
                max => undef,
            };
        }
        $f = $autoformat[$c];
        $heading = ($headings && $headings->[$c]) ? $headings->[$c] : "";
        if ($heading) { 
            $len = length($heading);
            if ($len > $f->{max_length}) {
                $f->{max_length} = $len;
            }
        }
        elsif (! $options->{compact}) { 
            $len = length($columns->[$c]);
            if ($len > $f->{max_length}) {
                $f->{max_length} = $len;
            }
        }
        for ($r = 0; $r <= $#$rows; $r++) {
            $row = $rows->[$r];
            if ($c <= $#$row && defined $row->[$c]) {
                $elem = $row->[$c];
                $len = length($elem);
                if ($elem =~ /^-?[0-9]*\.[0-9]+$/) {
                    $len = length(sprintf("%.2f",$elem));
                    $f->{type} = 2;
                    if (!defined $f->{min} || $elem < $f->{min}) {
                        $f->{min} = $elem;
                    }
                    if (!defined $f->{max} || $elem < $f->{max}) {
                        $f->{max} = $elem;
                    }
                }
                elsif ($elem =~ /^-?[0-9]+$/) {
                    $f->{type} = 1 if ($f->{type} < 1);
                    if (!defined $f->{min} || $elem < $f->{min}) {
                        $f->{min} = $elem;
                    }
                    if (!defined $f->{max} || $elem < $f->{max}) {
                        $f->{max} = $elem;
                    }
                }
                $f->{max_length} = $len if ($len > $f->{max_length});
            }
        }
        &determine_sprintf_fmt($f);
    }
    for ($c = 0; $c <= $#$columns; $c++) {
        $format = $autoformat[$c]->{title_fmt} || "%s";
        print " " if ($c > 0);
        $heading = ($headings && $headings->[$c]) ? $headings->[$c] : $columns->[$c];
        printf($format, $heading);
    }
    print "\n";
    for ($r = 0; $r <= $#$rows; $r++) {
        $row = $rows->[$r];
        for ($c = 0; $c <= $#$row; $c++) {
            $format = $autoformat[$c]->{fmt} || "%s";
            print " " if ($c > 0);
            printf($format, $row->[$c]);
        }
        print "\n";
    }
}

sub determine_sprintf_fmt {
    my ($f) = @_;
    my ($width, $int_len, $fract_len);
    if ($f->{type} == 2) {     # float
        $f->{title_fmt} = "%$f->{max_length}.$f->{max_length}s";
        $f->{fmt} = "%$f->{max_length}.2f";
    }
    elsif ($f->{type} == 1) {  # integer
        $f->{title_fmt} = "%$f->{max_length}.$f->{max_length}s";
        $f->{fmt} = "%$f->{max_length}d";
    }
    else {                     # string
        $f->{title_fmt} = "%-$f->{max_length}.$f->{max_length}s";
        $f->{fmt} = "%-$f->{max_length}s";
    }
}

exit (0);

