#!/usr/bin/perl
#    Big Sister network monitor
#    Copyright (C) 1998-2002  Thomas Aeby
#
#    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., 675 Mass Ave, Cambridge, MA 02139, USA.
#

use strict;

$BigSister::common::Usage = "[-f feature1 ...] [-d device] [-t testname] [-F xml|man] (known|tests|man)";

@BigSister::common::options = ( "d=s", "f=s\@", "t=s", "F=s" );
use lib "."; use lib "platforms"; 		#inslib
use BigSister::common;

proginit();

require Monitor::Tester;

my $mode = shift;
$mode = (($BigSister::common::opt_t || @BigSister::common::opt_f) ? "tests" : "known") unless( $mode );
my $device = $BigSister::common::opt_d;

my %types = (
    "kbsize" => "size in kBytes",
    "kbitrate" => "rate in kbit/s",
    "percentage" => "percentage",
    "int" => "integer number",
    "pattern" => "regular expression/string",
    "bool" => "boolean (1: true, 0: false)"
);

($mode eq "known") && known();
($mode eq "tests") && tests();
($mode eq "man") && mandocu();


sub known {
    my( @tests ) = Monitor::Tester::testnames();
    my %devices = ();
    my %names = ();
    my %features = ();

    foreach my $testname (@tests) {
        my $test = Monitor::Tester::gettest( $testname );
	$test = newfrom Monitor::Tester( $test );
	next unless( $test );
	my $dev = $test->expand_value( "device" );
	next if( $device && ($dev ne $device) );
	$devices{$dev} = 1;
	$names{$test->expand_value( "name" )} = 1;
	foreach my $feature (@{$test->expand_values( "features" )}) {
	    $features{$feature} = 1;
	}
    }

    print "Device types:\n";
    print "============\n\n";
    fancy_print( %devices );

    print "\nFeature names:\n";
    print "=============\n\n";
    fancy_print( %features );

    print "\nTests:\n";
    print "=====\n\n";
    fancy_print( %names );
}


sub fancy_print {
    my( %list ) = @_;

    foreach my $name (sort keys %list) {
	 next unless( $name );
         print "  $name\n";
    }
} 



sub tests {
    my $selected_test = $BigSister::common::opt_t;
    my @features = @BigSister::common::opt_f;

    my $args = {
        "features" => join( ",", @features )
    };
    if( $device ) {
         $args->{"device"} = $device;
    }
    my %done = ();
    foreach my $testname (Monitor::Tester::testnames()) {
        my $test = newfrom Monitor::Tester( Monitor::Tester::gettest( $testname ) );
	next unless( $test );
	my $thisname = $test->expand_value( "name" );
	next if( $done{$thisname} || ($selected_test && ($thisname ne $selected_test)) );
	$done{$thisname} = 1;
	$args->{"name"} = $thisname;
	my $best = new Monitor::Tester( $args );
	next unless( $best );
	print $best->expand_value( "name" );
	print "\n\n";
	my $description = $best->expand_value( "description" );
	multiline_print( $description, "  ", 62 );
	print "\n";
	foreach my $arg ( @{$best->expand_values( "arguments")} ) {
	    arg_print( $arg );
        }
	print "\n---------------------------------------\n\n";
    }
} 



sub arg_print {
    my( $arg ) = @_;

    return unless( $arg =~ /^(.*?):(.*?):(.*)/ );
    my( $name, $type, $desc ) = ($1,$2,$3);
    print "  $name: ";
    print (expand_types($type))."\n";
    multiline_print( $desc, "    ", 60 );
}


sub expand_types {
    my( $typedef ) = @_;

    my @types = split( /\|/, $typedef );
    foreach my $type (@types) {
        $type = expand_type( $type );
    }
    return join( " OR ", @types );
}


sub expand_type {
    my( $type ) = @_;

    my $exp = "";
    if( $type =~ /^\@/ ) {
        $type = $';
	$exp = "list of ";
    }
    if( defined $types{$type} ) {
        return "$exp$types{$type}";
    }
    return $exp.$type;
}


sub multiline_print {
    my( $string, $prefix, $length ) = @_;

    while( length( $string ) > $length ) {
        my $max = $length;
        for( my $i=0; $i<=$length; $i++ ) {
	    if( substr( $string, $i, 1 ) =~ /[\s\t\-]/ ) {
	        $max = $i;
	    }
        }
	print $prefix.(substr( $string, 0, $max))."\n";
	$string = substr( $string, $max+1 );
	$string =~ s/^[\s\t]+//;
    }
    print "$prefix$string\n" if( $string  );
}


sub mandocu {
    my $format = $BigSister::common::opt_F || "xml";
    if( $format eq "xml" ) {
        print <<END;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE reference SYSTEM "dtd/docbookx.dtd">
<reference id="refnewhealth">
END
    }
    if( $BigSister::common::opt_t ) {
	man_one_test( $BigSister::common::opt_t );
    }
    else {
	my( @tests ) = Monitor::Tester::testnames();
	my %names = ();

	foreach my $testname (@tests) {
	    my $test = Monitor::Tester::gettest( $testname );
	    $test = newfrom Monitor::Tester( $test );
	    next unless( $test );
	    $names{$test->expand_value( "name" )} = 1;
	}
	foreach my $name (sort keys %names) {
	    next unless( $name );
	    man_one_test( $name );
        }
    }
	print <<END;
</reference>
END
}


sub man_one_test {
    my( @selected_tests ) = @_;

    my %testdescription = ();
    foreach my $testname (Monitor::Tester::testnames()) {
        my $test = newfrom Monitor::Tester( Monitor::Tester::gettest( $testname ) );
	next unless( $test );
	my $thisname = $test->expand_value( "name" );
	next unless( grep( $thisname eq $_, @selected_tests ) );
	if( $testdescription{$thisname} ) {
	    push( @{$testdescription{$thisname}}, $test );
	}
	else {
	    $testdescription{$thisname} = [ $test ];
  	}
    }

    while( my( $testname, $descriptions ) = each %testdescription ) {
	man_from_descriptions( @$descriptions );
    }
}


sub man_from_descriptions {
    my( @descriptions ) = @_;

    my %descriptions = ();
    my %arguments = ();
    my $count = 0;
    foreach my $description (@descriptions) {
	my $unique_name = $description->{"test"}->{"name"};
	insert_selectively( \%descriptions, $description->expand_value( "description" ), $unique_name );
	foreach my $argument ( @{$description->expand_values( "arguments" )} ) {
	    insert_selectively( \%arguments, $argument, $unique_name );
	}
    }

    my $nummatching = 1;
    my $shortdescription = $descriptions[0]->expand_value("description");
    while( my( $description, $tests ) = each %descriptions ) {
	if( $#$tests >= $nummatching ) {
	    $shortdescription = $description;
	    $nummatching = $nummatching + 1;
	}
    }
    my $name = $descriptions[0]->expand_value( "name" );

    my $fulldescription = "";
    if( open( DESC, "<$name.desc" ) ) {
	while( <DESC> ) {
	    $fulldescription .= $_;
	}
    }

    my $mode = $BigSister::common::opt_F || "xml";
    ($mode eq "xml") && emit_xml( \@descriptions, \%descriptions, \%arguments, $name, $shortdescription, $fulldescription );
    ($mode eq "man") && emit_man( \@descriptions, \%descriptions, \%arguments, $name, $shortdescription, $fulldescription );

}


sub emit_xml {
    my( $tests, $descriptions, $arguments, $name, $shortdescription, $fulldescription ) = @_;

    my $fullname = xml_escape( "bs::$name" );
    my $shortdescription = xml_escape( $shortdescription );
    my $name = xml_escape( $name );
    print <<END;
<refentry id=\"$fullname\">
        <refmeta>
            <refentrytitle>$fullname</refentrytitle>
            <manvolnum>1</manvolnum>
        </refmeta>
        <refnamediv>
            <refname>$fullname</refname>
            <refpurpose>$shortdescription</refpurpose>
        </refnamediv>
        <refsynopsisdiv>
            <cmdsynopsis>
                <arg> <option>parameter1=value1</option> 
		</arg>
                <arg> <option>parameter2=value2</option> 
		</arg>
		<arg> ...
                </arg>
                <command> $name </command>
            </cmdsynopsis>
        </refsynopsisdiv>
	<refsect1>
            <title>Description</title>
            <para> $shortdescription </para>
END
    print "            <para> $fulldescription </para>\n" if( $fulldescription );

    print <<END;
        </refsect1>
	<refsect1>
	    <title>Availability</title>
	    <itemizedlist>
END

    my @platforms = ();
    foreach my $test (@$tests) {
	push( @platforms, platform_name( $test ) );
    }
    foreach my $platform( sort @platforms ) {
	print "            <listitem><para> $platform </para></listitem>\n";
    }
    print <<END;
	    </itemizedlist>
	</refsect1>
END
    if( %$arguments ) {
        print <<END;
	<refsect1>
	    <title>Options</title>
	    <refsect2>
	    	<title>Global Options</title>
		<variablelist>
END
	foreach my $option (sort keys %$arguments) {
	    my $assoc_tests = $arguments->{$option};
	    next if( $#$assoc_tests < $#$tests );
	    delete $arguments->{$option};
	    next unless( $option =~ /^(.*?):(.*?):(.*)/ );
	    my( $name, $type, $desc ) = ($1,$2,$3);
	    $type = expand_types( $type );
	    ($name, $type, $desc) = xml_escape( $name, $type, $desc );
	    print "     <varlistentry><term>$name [$type]</term>\n";
	    print "<listitem><para> $desc </para></listitem>\n";
	    print "     </varlistentry>\n";
	}

	print <<END;
		</variablelist>
		</refsect2>
END

	my %referred_tests = ();
	foreach my $option (sort keys %$arguments) {
	    foreach my $testname (@{$arguments->{$option}}) {
		$referred_tests{$testname} = 1;
	    }
	}

	foreach my $testname ( keys %referred_tests ) {
	    my( $test ) = grep( $_->{"test"}->{"name"} eq $testname, @$tests );
	    next unless( $test );
	    my $platform = platform_name( $test );
	    print <<END;
	    <refsect2>
	    	<title>$platform</title>
		<para>
END
	
	    print ( $test->expand_value( "description" ) )."\n";
	    print <<END;
		</para>
		<variablelist>
END
	    my @args = ();
	    while( my( $option, $tests ) = each %$arguments ) {
		push( @args, $option ) if( grep( $_ eq $testname, @$tests ) );
	    }
	    foreach my $option (@args) {
		next unless( $option =~ /^(.*?):(.*?):(.*)/ );
		my( $name, $type, $desc ) = ($1,$2,$3);
		$type = expand_types( $type );
		($name, $type, $desc) = xml_escape( $name, $type, $desc );
		print "     <varlistentry><term>$name [$type]</term>\n";
		print "<listitem><para> $desc </para></listitem>\n";
		print "     </varlistentry>\n";
	    }
	    print <<END;
	        </variablelist>
	    </refsect2>
END
	}
    print <<END;
	</refsect1>
END
    }
    print <<END;
	<refsect1>
	    <title>FILES</title>
	    <para>
	      <varname>/etc/bigsister/uxmon-net</varname>
	    </para>
	</refsect1>
</refentry>
END
}



sub xml_escape {
    my( @args ) = @_;

    my @result = ();
    foreach my $arg (@args) {
        $arg =~ s/</&lt;/g;
        $arg =~ s/>/&lt;/g;
	push( @result, $arg );
    }
    return( wantarray ? @result : $result[0] );
}



sub emit_man {
    my( $tests, $descriptions, $arguments, $name, $shortdescription, $fulldescription ) = @_;

    print ".TH \"$name\" \"1\"\n";
    print ".SH NAME\n";
    print man_escape("$name \- $shortdescription\n");
    print ".SH SYNOPSIS\n";
    print man_escape("[arg1=value1] [arg2=value2] ... $name\n");
    print ".SH DESCRIPTION\n";
    print ".PP\n";
    print man_escape("$shortdescription\n\n");
    if( $fulldescription ) {
	print ".PP\n";
	print man_escape("$fulldescription\n\n");
    }

    print ".SH AVAILABILITY\n";
    my @platforms = ();
    foreach my $test (@$tests) {
	push( @platforms, platform_name( $test ) );
    }
    foreach my $platform( sort @platforms ) {
	print ".TP\n".(man_escape( $platform))."\n";
    }

    print ".SH OPTIONS\n";
    print ".IP \"\\fIGlobal Options\\fR\"\n";

    foreach my $option (sort keys %$arguments) {
	my $assoc_tests = $arguments->{$option};
	next if( $#$assoc_tests < $#$tests );
	delete $arguments->{$option};
	next unless( $option =~ /^(.*?):(.*?):(.*)/ );
	my( $name, $type, $desc ) = ($1,$2,$3);
	print ".TP\n";
	$type = expand_types( $type );
	print "\\fB$name\\fR [$type]\n";
	print "$desc\n";
    }

    my %referred_tests = ();
    foreach my $option (sort keys %$arguments) {
	foreach my $testname (@{$arguments->{$option}}) {
	    $referred_tests{$testname} = 1;
	}
    }

    foreach my $testname ( keys %referred_tests ) {
	my( $test ) = grep( $_->{"test"}->{"name"} eq $testname, @$tests );
	next unless( $test );
	my $platform = platform_name( $test );
	print ".IP \"\\fI$platform\\fR\n";
	print ".PP\n";
	print man_escape( $test->expand_value( "description" ) )."\n";
	my @args = ();
	while( my( $option, $tests ) = each %$arguments ) {
	    push( @args, $option ) if( grep( $_ eq $testname, @$tests ) );
	}
	foreach my $option (@args) {
	    next unless( $option =~ /^(.*?):(.*?):(.*)/ );
	    my( $name, $type, $desc ) = ($1,$2,$3);
	    print ".TP\n";
	    $type = expand_types( $type );
	    print "\\fB$name\\fR [$type]\n";
	    print "$desc\n";
	}
    }
}



sub platform_name {
    my( $test ) = @_;

    my $device = $test->expand_value( "device" );
    my $manufacturer = $test->expand_value( "manufacturer" );
    my $name = join( " / ", @{$test->expand_values( "features" )} );
    $name .= " $device" if( $device );
    $name .= " from $manufacturer" if( $manufacturer );
    return( $name ? $name : "unlimitted" );
}



sub man_escape {
    my( $text ) = @_;

    $text =~ s/\n\./\n\\./gsm;
    $text =~ s/-/\\-/gsm;
    return $text;
}


sub insert_selectively {
    my( $list, $value, $unique_name ) = @_;

    if( defined $list->{$value} ) {
	push( @{$list->{$value}}, $unique_name )
	  unless( grep( $_ eq $unique_name, @{$list->{$value}} ) );
    }
    else {
	$list->{$value} = [ $unique_name ];
    }
}

