#!/usr/bin/perl

use strict;
use Text::Wrap;
my %output;
my $text;
my %stash;

use Getopt::Long;

my %opts = (
	    c => 'CHANGES.new',
	    n => 'NEWS.new',
	   );

LocalGetOptions(\%opts,
		['s|start-regexp=s','A regexp to look for in files to start converting at'],
		['e|end-regexp=s','A regexp to look for in files to end converting at'],
		"",
		['c|changes-file=s','A file to save CHANGES entries to'],
		['n|news-file=s','A file to save NEWS entries to'],
		['d|debug-line=s', 'Debugging output for lines matching STRING'],
		"",
		['GUI:otherargs_text','Input files to parse'],
	       );

my $maybecontinue = 0;
my $lasttext = "";
my $lastfile;
my $lastcomponent;

foreach my $argv (@ARGV) {
    open(I, $argv);
    if ($opts{'s'}) {
	while (<I>) {
	    last if (/$opts{'s'}/o);
	}
    }

    while (<I>) {
	my ($file, $component, $patbug, $nums, $text);

	last if ($opts{'e'} && /$opts{e}/o);

	print STDERR "here: $_" if ($opts{'d'} && /$opts{'d'}/o);

	# don't use this:
	#   FILE: BUGS: 123,456: text
	($file, $patbug, $nums, $text) = 
	  /(NEWS|CHANGES):\s*-*\s*\[*(BUG|PATCH)(?:ES|S|):*\s*([\d,\s*]*)\]*:*\s*-*\s*(.*)/;

	print STDERR "  1:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o);

	# or this:
	#   FILE: component - text
	($file, $component, $text) = 
	  /(NEWS|CHANGES):\s*(\w+)\s*-+\s*(.*)/ if (!$file);

	print STDERR "  2:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o);

	# what you should use:
	#   FILE: component: BUGS: 123,456: text
	#
	#      or
	#   FILE: component: PATCH: 123,456: from someone text
	#   FILE: component: PATCH: 123,456: from "someone long" text
	($file, $component, $patbug, $nums, $text) = 
	  /(NEWS|CHANGES):\s*([^:]+):\s*-*\s*\[*(BUG|PATCH)(?:ES|S):*\s*([\d,\s*]*)\]*:*\s*-*\s*(?:from ["'][^"]+["']|from \w+|):*\s*(.*)/ if (!$file);

	print STDERR "  3:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o);

	#      or at least:
	#   FILE: component: text
	($file, $component, $text) = 
	  /(NEWS|CHANGES):\s*([^:]+):\s*-*\s*(.*)/ if (!$file);

	print STDERR "  4:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o);

	# component left out
	# FILE: [BUGS: 123,456]: text
	($file, $patbug, $nums, $text) = 
	  /(NEWS|CHANGES):\s*\[*(BUG|PATCH)*(?:ES|S|):*\s*([\d,\s*]*)\]*:*\s*-*\s*(.*)/ if (!$file);
	
	print STDERR "  5:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o);

	if ($opts{'d'} && /$opts{'d'}/o) {
	    my $bogus = 1; # breakable point
	}

	if (!$file && $maybecontinue) {
	    if (/^\s*(.+)$/) {
		$text = $1;
		$file = $lastfile;
		$component = $lastcomponent;

		# we're going to re-add these
		pop @{$output{$opts{'c'}}{$component}};
		pop @{$output{$opts{'n'}}{$component}} if ($file eq 'NEWS');
	    } else {
		$maybecontinue = 0;
		$lasttext = "";
		next;
	    }
	} elsif (!$file) {
	    next;
	}
		
	next if (exists($stash{$text}));
	$stash{$text} = 1;
	
	$component = "unspecified" if (!$component);
	if ($patbug) {
	    $lasttext .= " [$patbug $nums]: $text";
	} else {
	    $lasttext .= " $text";
	}
	$lasttext =~ s/^ //;                   # get rid of leading spaces
	$lasttext =~ s/^([a-z])/uc($1)/e;      # capitalize the first letter 
	$text = wrap("      - ","        ","$lasttext") . "\n";
	
	#
	#  Assist with displaying categories in a sensible order
	#     snmplib first
	#     snmpd/snmp{apps}
	#     various other
	#     O/S specific  (relies on upper case)
	#
	$component =~ s/^snmplib/00snmplib/;
	$component =~ s/^snmp/0snmp/;
	$component =~ s/^agent/0snmpd/;		# Merge "agent" into "snmpd"
	$component =~ s/^([A-Z])/zz\1/;
	print STDERR "  t:$file, $component, $patbug, $nums, $text\n" if ($opts{'d'} && /$opts{'d'}/o);
	push @{$output{$opts{'c'}}{$component}}, $text;
	push @{$output{$opts{'n'}}{$component}}, $text if ($file eq 'NEWS');
	$lastfile = $file;
	$lastcomponent = $component;
	$maybecontinue = 1;
    }
}



# save the news and changes to appropriate files
foreach my $f ($opts{'c'}, $opts{'n'}) {
    my $cat2;
    open(O,">$f");
    foreach my $cat (sort (keys(%{$output{$f}}))) {
	($cat2 = $cat) =~ s/^00?|^zz//;
	print O "    $cat2:\n";
	print O sort @{$output{$f}{$cat}};
	print O "\n";
    }
    close(O);
}

#######################################################################
# getopt long gui portability code
#
sub LocalGetOptions {
    if (eval {require Getopt::GUI::Long;}) {
	import Getopt::GUI::Long;
	Getopt::GUI::Long::Configure(qw(display_help no_ignore_case));
	return GetOptions(@_);
    } else {
	require Getopt::Long;
	Getopt::Long::Configure(qw(auto_help no_ignore_case));
	import Getopt::Long;
    }
    GetOptions(LocalOptionsMap(@_));
}

sub LocalOptionsMap {
    my ($st, $cb, @opts) = ((ref($_[0]) eq 'HASH')
			    ? (1, 1, $_[0]) : (0, 2));
    for (my $i = $st; $i <= $#_; $i += $cb) {
	if ($_[$i]) {
	    next if (ref($_[$i]) eq 'ARRAY' && $_[$i][0] =~ /^GUI:/);
	    push @opts, ((ref($_[$i]) eq 'ARRAY') ? $_[$i][0] : $_[$i]);
	    push @opts, $_[$i+1] if ($cb == 2);
	}
    }
    return @opts;
}
