#!/usr/bin/perl -w

use strict;
use utf8;
use warnings;

our $pophome;
BEGIN {
    $pophome = $ENV{POPHOME} || '@POPHOME@';
    unshift @INC, "$pophome/lib"
      unless $ENV{KUNDE_NO_PERLPATH};
}

use Dbase::Getopt qw(:DEFAULT :kunden);
use Dbase::Globals qw(get_kunde unterkunden);
use Dbase::Help qw(DoSelect DoTrans in_list qquote);
use Encode qw(encode);
use Loader qw(show_progress);
use Umlaut;

my $AngrenzendeMonate = 1;
GetOptions(
    'angrenzende-monate=i' => sub {
        ( my $option, $AngrenzendeMonate ) = @_;
        die "-$option akzeptiert nur natürliche Zahlen oder 0.\n"
          if $AngrenzendeMonate < 0;
    },
    'chart=s'        => \my $Chart,
    'chart-height=i' => \( my $ChartHeight = 900 ),
    'chart-type=s'   => \( my $ChartType = 'LinesPoints' ),
    'chart-width=i'  => \( my $ChartWidth = 1272 ),
    'max-kunden=i'   => \( my $MaxKunden = 10 ),
    'max-monate=i'   => \( my $MaxMonate = 24 ),
    'min-stunden=f'  => \( my $MinStunden = 8 ),
    'top-monate=i'   => \( my $TopMonate = 1 ),
    'verbose!'       => \( my $Verbose = 1 ),
);

die "-top-monate kann nicht größer sein als -max-monate.\n"
  if $TopMonate > $MaxMonate;

sub jjjj_mm($$) { sprintf '%d-%02d', @_ }

sub vormonat($) {
    my ( $jahr, $monat ) = split /-/, shift, 2;
    unless ( --$monat ) {
        $monat = 12;
        $jahr--;
    }
    jjjj_mm( $jahr, $monat );
}

my $zeit_seit = vormonat(
    my $zeit_vor = do {
        my ( $jahr, $monat ) = (localtime)[ 5, 4 ];
        jjjj_mm( $jahr + 1900, $monat + 1 );
      }
);

my $where = join '', map "\t     AND $_\n",
  in_list( 'stunden.kunde', '', @Kunden ),
  in_list( 'stunden.kunde', NOT => @OhneKunden );

my ( %zeit4kunde, @monate );
my $cut = '';
DoTrans {
  Monat: while () {

        show_progress("$zeit_seit... ") if $Verbose;
        splice @$_, 1, 0, 0 for values %zeit4kunde;
        DoSelect {
            my ( $kunde, $sekunden ) = @_;
            $zeit4kunde{$kunde} ||= [ 0, 0 ];
            $zeit4kunde{$kunde}[$_] += $sekunden for 0, 1;
        }
        <<_
	SELECT   kunde.name, SUM(stunden.zeit) Sekunden
	FROM     stunden
	JOIN     kunde ON stunden.kunde = kunde.id
	WHERE    stunden.beginn BETWEEN UNIX_TIMESTAMP(${\ qquote("$zeit_seit-01") })
                                AND UNIX_TIMESTAMP(${\ qquote("$zeit_vor-01")  }) -1
_
          . $where
          . ( $cut
              && "\t     AND "
              . in_list( 'kunde.name', '', keys %zeit4kunde )
              . "\n" )
          . <<'_'
	GROUP BY stunden.kunde
	ORDER BY Sekunden DESC, kunde.name
_
          . (     $MaxKunden > 0
              and $TopMonate == 1 || @monate >= $TopMonate
              and <<_ );
    LIMIT    $MaxKunden
_
    }
    continue {
        unshift @monate, $zeit_seit;
        if ( @monate == $TopMonate and $MinStunden || $MaxKunden ) {
            $cut = 1;
            if ($MinStunden) {
                my $min_sekunden = $TopMonate * $MinStunden * 3600;
                while ( my ( $kunde, $zeit ) = each %zeit4kunde ) {
                    delete $zeit4kunde{$kunde} if $zeit->[0] < $min_sekunden;
                }
            }
            delete @zeit4kunde{
                (
                    sort { $zeit4kunde{$b}[0] <=> $zeit4kunde{$a}[0] }
                      keys %zeit4kunde
                  )[ $MaxKunden .. keys(%zeit4kunde) - 1 ]
              }
              if $MaxKunden && keys %zeit4kunde > $MaxKunden;
            show_progress(
                'Kunden: ' . join( ', ', sort keys %zeit4kunde ) . "\n" )
              if $Verbose;
        }
        last if $MaxMonate && @monate >= $MaxMonate;
        $zeit_seit = vormonat( $zeit_vor = $zeit_seit );
    }
};

unless ( defined $Chart && $Chart eq '-' ) {
    print join( ',', 'Kunde', @monate ) . "\n";
    print join( ',',
        $_,
        map sprintf( '%.f', ( $_ // 0 ) / 3600 ),
        @{ $zeit4kunde{$_} }[ 1 .. @monate ] )
      . "\n"
      for sort { $zeit4kunde{$b}[0] <=> $zeit4kunde{$a}[0] } keys %zeit4kunde;
}

if ( defined $Chart ) {
    if ($AngrenzendeMonate) {
        show_progress('Berechne gleitende Durchschnitte...') if $Verbose;
        require List::Util and List::Util->import('sum');
        while ( my ( $kunde, $zeit4kunde ) = each %zeit4kunde ) {
            my @zeiten = $zeit4kunde->[0];
            for ( 1 .. @monate ) {
                my $min_monat = $_ - $AngrenzendeMonate;
                $min_monat = 1 if $min_monat < 1;
                my $max_monat = $_ + $AngrenzendeMonate;
                $max_monat = @monate if $max_monat > @monate;
                push @zeiten,
                  sum( @{$zeit4kunde}[ $min_monat .. $max_monat ] ) /
                  ( 1 + $max_monat - $min_monat );
            }
            @$zeit4kunde = @zeiten;
        }
        show_progress(" erledigt.\n") if $Verbose;
    }

    eval 'require ' . ( my $chart_module = "Chart::$ChartType" );
    my @kunden =
      sort { $zeit4kunde{$b}[0] <=> $zeit4kunde{$a}[0] } keys %zeit4kunde;
    my $chart = $chart_module->new( $ChartWidth, $ChartHeight );
    $chart->set(
        colors          => { dataset0 => [ 0,          0, 0 ] },
        f_y_tick        => sub        { sprintf '%.f', shift },
        gif_border      => 0,
        grey_background => 0,
        legend          => 'bottom',
        legend_labels   => \@kunden,
        title           => encode(
            latin1 => "Arbeitszeit nach Kunden für $monate[0] bis $monate[-1]"
              . (
                $AngrenzendeMonate > 0
                  && ' (gleitende '
                  . ( 1 + ( $AngrenzendeMonate << 1 ) )
                  . 'er-Durchschnitte)'
              )
        ),
        y_label => 'Stunden'
    );
    $chart->png(
        $Chart,
        [
            \@monate,
            map [ map $_ / 3600, @{ $zeit4kunde{$_} }[ 1 .. @monate ] ], @kunden
        ]
    );
    exit if $Chart eq '-';
}

__END__

=encoding utf8

=head1 NAME

stunden-statistik - Statistik zu auf Tickets gebuchte Arbeitszeit nach Kunden

=head1 SYNOPSE

    stunden-statistik \
        -ohne-kunde-und-unterkunden POP \
        -ohne-ap-technik thomas \
        -ohne-ap-technik jschneider \
        -max-monate 12

=head1 BESCHREIBUNG

Das Script gibt auf der Standardausgabe eine CSV-Tabelle aus, die für diejenigen
Kunden, auf die zuletzt die meiste Arbeitszeit auf Tickets gebucht wurde, den
Verlauf der gebuchten Arbeitszeit pro Monat in den letzten Monaten enthält.

=head1 OPTIONEN

=over 4

=item -ap-technik Person

=item -ap-vertrieb Person

=item -kunde Kunde

=item -kunde-und-unterkunden Kunde

=item -ohne-ap-technik Person

=item -ohne-ap-vertrieb Person

=item -ohne-kunde Kunde

=item -ohne-kunde-und-unterkunden Kunde

Per Default werden Zeiterfassungseinträge für alle Kunden ausgewertet.
Dies lässt sich vermittels dieser Optionen modifizieren.
Details dazu siehe L<Dbase::Getopt/:kunden>.

=item -max-monate Anzahl

maximal die C<Anzahl> vergangenen Monate auswerten.

Ansonsten bricht das Script erst ab, wenn es für einen Monat gar keine
Arbeitszeit mehr findet.

Default: 24

=item -max-kunden Anzahl

Auswertung für maximal C<Anzahl> Kunden (mit der meisten auf Tickets gebuchten
Arbeitszeit im vergangenen Monat)

Default: 10

=item -top-monate Anzahl

anhand der Zeiterfassungseinträge der letzten C<Anzahl> Monate entscheiden,
welche Kunden in die Statistik kommen; Default: 1

=item -min-stunden Anzahl

Auswertung nur für Kunden, auf deren Tickets pro Monat (innerhalb der
L</-top-monate Anzahl|-top-monate>) mindestens so viele Stunden gebucht wurden

Default: 8

=item -verbose

um Fortschrittsinformationen aufs Terminal zu bekommen.
(Ist per Default aber sowieso an.)

=item -noverbose

um die Anzeige von Fortschrittsinformationen zu unterdrücken

=item -angrenzende-monate Anzahl

als Wert jedes Monats im Diagramm den Durchschnitt der Werte aus dem Monat
selbst sowie jeweils der Anzahl Monate davor und danach einzeichnen.
(Für die ersten und letzten Monate wird abweichend davon ein Durchschnitt aus
weniger Werten gebildet.)

Default: 1, also gleitende 3er-Durchschnitte

=item -chart Datei

Diagramm als PNG in angegebene Datei ausgeben

=item -chart-type Typ

zur Erzeugung des L</-chart Datei|Diagramms> zu verwendendes Chart::*-Modul.
Default: C<LinesPoints>

=item -chart-width Pixel

Breite des L</-chart Datei|Diagramms>; Default: 1272

=item -chart-height Pixel

Höhe des L</-chart Datei|Diagramms>; Default: 900

=item -help

=item -?

um (nur) diese Dokumentation anzeigen zu lassen

=back

=head1 BEKANNTE FEHLER

=over 4

=item *

Es wird generell nur Arbeitszeit ausgewertet, die auf Tickets gebucht wurde.

=item *

Die Zuordnung der Arbeitszeit erfolgt anhand des Kunden des jeweiligen Tickets,
nicht nach dem in der Zeiterfassung eingetragenen Kunden.

=back

=head1 AUTOR

 Martin H. Sluka <fany@noris.net>
 für die noris network AG
