# -*- perl -*-

#
# $Id: BBBikeStats.pm,v 1.12 2005/05/21 11:48:54 eserte Exp $
# Author: Slaven Rezic
#
# Copyright (C) 2002 Slaven Rezic. All rights reserved.
# This package is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# Mail: slaven@rezic.de
# WWW:  http://bbbike.sourceforge.net
#

# XXX schön, aber ein großes Problem gibt es noch: wenn ein neuer Punkt
# in das Netz eingefügt wird, dann wird er in den neuen Nets *nicht*
# berücksichtigt. Lösung? Evtl. AddNet in get_net analysieren und
# die zusätzlichen Punkte bei Bedarf einfügen. Sollte eigentlich gehen.

package BBBikeStats;

use strict;
use vars qw($VERSION);
$VERSION = sprintf("%d.%02d", q$Revision: 1.12 $ =~ /(\d+)\.(\d+)/);

use Strassen::Util;
use BBBikeUtil;
use BBBikeUtil qw(min max);

BEGIN {
    if (!eval '
use Msg;
1;
') {
	warn $@ if $@;
	eval 'sub M ($) { $_[0] }';
	eval 'sub Mfmt { sprintf(shift, @_) }';
    }
}

sub calculate {
    my($route, $dataset, %args) = @_;
    my $res = {};
    $res->{Length} = $route->len;
    my(@path) = $route->path_s_list;

    my(%net, %len, %coords);

    $net{Quality}      = $dataset->get_net("str","q","all",-nettype => "cat");
    $net{Handicap}     = $dataset->get_net("str","h","all",-nettype => "cat");
    $net{Cyclepaths}   = $dataset->get_net("str","rw","all",-nettype => "cat");
    $net{Category}     = $dataset->get_net("str","s","all",-nettype => "cat");
    #XXX to be continued.... $net{Fragezeichen} = $dataset->get_net("str","fz","all",-nettype => "cat");

    for my $i (0 .. $#path-1) {
	my $hop_len = Strassen::Util::strecke_s($path[$i], $path[$i+1]);

	for my $def ([Quality => "Q0"],
		     [Handicap => "q0"],
		     [Cyclepaths => "RW0"],
		     [Category => "N"],#XXX better solution? use unknown category?
		    ) {
	    my($member, $fallback) = @$def;

	    my $cat = $net{$member}{Net}{$path[$i]}{$path[$i+1]} || $fallback;
	    $cat = "RW1" if $cat eq 'RW'; # comments_cyclepath special handling
	    $len{$member}->{$cat} += $hop_len;
	    push @{ $coords{$member}->{$cat} }, [$path[$i], $path[$i+1]];
	}
    }

    # XXX more: ampeln, tragen, steigungen, höhen etc.

    foreach my $member (qw(Quality Handicap Cyclepaths Category)) {
	$res->{$member} = $len{$member};
	while(my($k,$v) = each %{ $res->{$member} }) {
	    $res->{$member . "%"}{$k} = (($v||0)/$res->{Length})*100;
	}
	$res->{$member . "Coords"} = $coords{$member};
    }

    $res;
}

# XXX split into widget creation and update data subroutine
#XXX maybe use a Scrolled Pane?
sub tk_display_result {
    my($top, $res, %args) = @_;
    if (!$args{-markcommand}) {
	$args{-markcommand} = sub {
	    $top->messageBox(-message => "No mark command defined");
	};
    }
    if (!defined $args{-chart}) {
	$args{-chart} = 1;
    }

    my $t;
    if ($args{-reusewindow} &&
	Tk::Exists($t = $top->Subwidget("Statistics"))) {
	$_->destroy for ($t->children);
    } else {
	$t = $top->Toplevel(-title => M("Statistik"));
	$top->Advertise("Statistics" => $t);
	# make transient?

	Hooks::get_hooks('new_route')->add
 	    (sub {
		 my $upd_b = $t->Subwidget("UpdateButton");
		 if (Tk::Exists($upd_b)) {
		     $upd_b->configure(-text => M("Update needed!"));
		 }
	     }, 'bbbike-statistics');
	

	$t->OnDestroy
	    (sub {
		 if ($t->{Photos}) {
		     foreach (@{ $t->{Photos} }) {
			 $_->delete;
		     }
		 }
		 delete $t->{Photos};
		 Hooks::get_hooks('new_route')->del('bbbike-statistics');
	     });

	(my $path = $t->PathName) =~ s/^.//;
	$t->optionAdd("*$path*Button*Pad", 0);
	$t->optionAdd("*$path*Button*anchor", "w"); # for the text labels
	$t->optionAdd("*$path*Label*anchor", "e"); # for the numbers
    }

    # XXX!!!
    my $category_attrib = \%main::category_attrib;
    my %font = %main::font;
    my %category_color = %main::category_color;

    my %grid_row;

    Tk::grid($t->Label(-text => M("Länge").":", -font => $font{"bold"}),
	     $t->Label(-text => sprintf "%.1f km", $res->{Length}/1000),
	     -sticky => "w");

    Tk::grid($t->Frame(-height => 1, -background => "black"),
	     -sticky => "ew", -columnspan => 10);

    Tk::grid($t->Label(-text => M("Straßenqualität"), -font => $font{"bold"}),
	     -sticky => "w", -columnspan => 3);
    $grid_row{"Quality1"} = ($t->gridSize)[1]-1;
    for my $cat (sort grep { /^Q\d$/ } keys %$category_attrib) {
	Tk::grid($t->Button(-text => $category_attrib->{$cat}[0],
			    -bg => $category_color{$cat}||"white",
			    -fg => _readable_fg($t,$category_color{$cat}),
			    -command => [$args{-markcommand}, $res->{QualityCoords}{$cat}]),
		 $t->Label(-text => sprintf "%.1f km", ($res->{Quality}{$cat}||0)/1000),
		 $t->Label(-text => sprintf "%d%%", ($res->{"Quality%"}{$cat}||0)),
		 -sticky => "we");
    }
    $grid_row{"Quality2"} = ($t->gridSize)[1]-1;

    Tk::grid($t->Frame(-height => 1, -background => "black"),
	     -sticky => "ew", -columnspan => 10);

    Tk::grid($t->Label(-text => M("Sonstige Beeinträchtigungen"), -font => $font{"bold"}),
	     -sticky => "w", -columnspan => 3);
    $grid_row{"Handicap1"} = ($t->gridSize)[1]-1;
    for my $cat (sort grep { /^q\d$/ } keys %$category_attrib) {
	Tk::grid($t->Button(-text => $category_attrib->{$cat}[0],
			    -bg => $category_color{$cat}||"white",
			    -fg => _readable_fg($t,$category_color{$cat}),
			    -command => [$args{-markcommand}, $res->{HandicapCoords}{$cat}]),
		 $t->Label(-text => sprintf "%.1f km", ($res->{Handicap}{$cat}||0)/1000),
		 $t->Label(-text => sprintf "%d%%", ($res->{"Handicap%"}{$cat}||0)),
		 -sticky => "we");
    }
    $grid_row{"Handicap2"} = ($t->gridSize)[1]-1;

    Tk::grid($t->Frame(-height => 1, -background => "black"),
	     -sticky => "ew", -columnspan => 10);

    Tk::grid($t->Label(-text => M("Radwege"), -font => $font{"bold"}),
	     -sticky => "w", -columnspan => 3);
    $grid_row{"Cyclepaths1"} = ($t->gridSize)[1]-1;
    for my $cat (sort grep { /^RW\d$/ } keys %$category_attrib) {
	Tk::grid($t->Button(-text => $category_attrib->{$cat}[0],
			    -bg => $category_color{$cat}||"white",
			    -fg => _readable_fg($t,$category_color{$cat}),
			    -command => [$args{-markcommand}, $res->{CyclepathsCoords}{$cat}]),
		 $t->Label(-text => sprintf "%.1f km", ($res->{Cyclepaths}{$cat}||0)/1000),
		 $t->Label(-text => sprintf "%d%%", ($res->{"Cyclepaths%"}{$cat}||0)),
		 -sticky => "we");
    }
    $grid_row{"Cyclepaths2"} = ($t->gridSize)[1]-1;

    Tk::grid($t->Frame(-height => 1, -background => "black"),
	     -sticky => "ew", -columnspan => 10);

    Tk::grid($t->Label(-text => M("Straßenkategorien"), -font => $font{"bold"}),
	     -sticky => "w", -columnspan => 3);
    $grid_row{"Category1"} = ($t->gridSize)[1]-1;
    for my $cat (@main::strcat_order) {
	Tk::grid($t->Button(-text => $category_attrib->{$cat}[0],
			    -bg => $category_color{$cat}||"white",
			    -fg => _readable_fg($t,$category_color{$cat}),
			    -command => [$args{-markcommand}, $res->{CategoryCoords}{$cat}]),
		 $t->Label(-text => sprintf "%.1f km", ($res->{Category}{$cat}||0)/1000),
		 $t->Label(-text => sprintf "%d%%", ($res->{"Category%"}{$cat}||0)),
		 -sticky => "we");
    }
    $grid_row{"Category2"} = ($t->gridSize)[1]-1;

    if ($args{-updatecommand}) {
	Tk::grid($t->Frame(-height => 1, -background => "black"),
		 -sticky => "ew", -columnspan => 10);
	my $upd_b;
	Tk::grid($upd_b = $t->Button
		 (-text => M("Update"),
		  -anchor => "c",
		  -font => $font{"bold"},
		  -command => sub {
		      $args{-updatecommand}->();
		      $t->Subwidget("UpdateButton")->configure(-text => M("Update"));
		  },
		 ), -sticky => "ew", -columnspan => 10);
	$t->Advertise(UpdateButton => $upd_b);
    }

    $t->update;

    if ($args{-chart} &&
	eval { require Chart::ThreeD::Pie;
	       require GD::Convert;
	       1;
	   }) {
	for my $member (qw(Cyclepaths Handicap Quality Category)) {
	    my $pie_width = 300; # XXX don't hardcode
	    my $pie_height;
	    my(@bbox, $pie);
	    @bbox = $t->gridBbox(0, $grid_row{$member."1"},
				 0, $grid_row{$member."2"});
	    $pie_height = $bbox[3];
	    $pie = Chart::ThreeD::Pie->new($pie_width,$pie_height,"");
	    $pie->transparent(1);
	    $pie->fgcolor('#000000');
	    $pie->bgcolor(_rgb($t,$t->cget('-background')));
	    $pie->thickness(20);
	    # XXX maximum radius is 100, otherwise: core dumps!
	    $pie->radius(min($pie_width-50, $pie_height-$pie->thickness-40, 100));

	    while(my($cat,$v) = each %{ $res->{$member} }) {
		$pie->add($v, _rgb($t,$category_color{$cat}) || "#ff0000", $category_attrib->{$cat}[0]);
	    }
	    my $xpm_data = $pie->plot->xpm;
#open(PNG,">/tmp/bla.png")or die;print PNG $pie->plot->png;close PNG;#XXX
	    my $xpm = $t->Pixmap(-data => $xpm_data);
	    push @{ $t->{Photos} }, $xpm;
	    $t->Label(-image => $xpm)->grid
		(-row => $grid_row{$member."1"}, -column => 3,
		 -rowspan => $grid_row{$member."2"}-$grid_row{$member."1"}+1);
	}
    } else {
	warn $@;
    }
}

sub _rgb {
    my($t,$color) = @_;
    sprintf("#%02x%02x%02x", map { $_/0x100 } $t->rgb($color||"white"));
}

sub _readable_fg {
    my($t,$color) = @_;
    my($r,$g,$b) = $t->rgb($color||"white");
    max($r,$g,$b)>0xa000 ? "black" : "white"; # XXX better formula???
}

1;

__END__

Gesamtlänge des Straßennetzes:

perl -Ilib -MStrassen::Util -MStrassen -MObject::Iterate=iterate -e '$s=Strassen->new("strassen");iterate { for $i (0 .. $#{$_->[Strassen::COORDS]}-1) { $len += Strassen::Util::strecke_s($_->[Strassen::COORDS][$i], $_->[Strassen::COORDS][$i+1])} } $s; warn $len/1000'
