#!/usr/athena/bin/perl

require "lib.pl";

$list1 = "list.posted";
$list2 = "list";

while ($_ = $ARGV[0], /^-/) {
    shift;
    if (/^-list1$/) {
	die "$0: missing argument to \"-list1\".\n" if (! @ARGV);
	$list1 = shift;
	next;
    }
    if (/^-list2$/) {
	die "$0: missing argument to \"-list2\".\n" if (! @ARGV);
	$list2 = shift;
	next;
    }
    if (/^-picky$/) {
	$picky++;
	next;
    }
    if (/^-out$/) {
	die "$0: missing argument to \"-out\".\n" if (! @ARGV);
	$outfile = shift;
	next;
    }
    die "$0: unknown argument \"$_\".\n";
}

$* = 1;
$special_chars = $lopip'special_chars;
$hidden_char = $lopip'hidden_char;

$oldselect = select(STDOUT); $| = 1; select($oldselect);

print "Reading $list1... ";

@posted = &lopip'read_data($list1);

print "indexing... ";

%posted = &index(*posted);

print "done\nReading $list2... ";

@list = &lopip'read_data($list2);

print "indexing... ";

%current = &index(*list);

print "done\nComparing... ";

foreach $oldone (keys %posted) {
    if (! $current{$oldone}) {
	$newrec = $posted{$oldone};
	$newrec =~ s/^[$special_chars]*/- /g;
	$processed{$posted{$oldone}} = $newrec;
	next;
    }
    if ($current{$oldone} ne $posted{$oldone}) {
	$newrec = &compare($posted{$oldone}, $current{$oldone});
	$processed{$current{$oldone}} = $newrec if ($newrec);
    }
}

foreach $newone (keys %current) {
    if (! $posted{$newone}) {
	$newrec = $current{$newone};
	$newrec =~ s/^[$special_chars]*/+ /g;
	$processed{$current{$newone}} = $newrec;
    }
}

print "done\n";

%current = %posted = ();

@processed_keys = keys %processed;
&lopip'sort_data(*processed_keys);

print "Writing...\n";

if ($outfile) {
    open(OUTPUT, ">$outfile") || die "$0: opening $outfile for write: $!.\n";
}
else {
    open(OUTPUT, ">&STDOUT") || die "$0: error dup'ing stdout: $!.\n";
}

foreach $record (@processed_keys) {
    print OUTPUT $processed{$record}, "\n";
}

close(OUTPUT);

sub index {
    local(*records) = @_;
    local(%posted);
    local($id, $_);
    local($archive_name);

    while ($_ = shift @records) {
	s/^--\n//;
	local($newrec);
	next if (&lopip'is_special($_, "Subject"));
	$id = &lopip'field_value($_, 'id');
	s/^[$special_chars]*[$hidden_char][$special_chars]*([^$special_chars:]+):.*\n([ \t]+.*\n)*//g;
	($_) = &lopip'strip_fields($_, 'date');
	foreach $archive_name (&lopip'archive_names($_)) {
	    ($_) = &lopip'strip_fields($_, $archive_name);
	}
	($_) = &lopip'strip_fields($_, 'summary');
	$posted{$id} = $_;
    }
    %posted;
}

# Algorithm: 
# 
# 1. Get the names of the fields in both records, because we want the
#    fields in the diff posting entry to be in the same order (as much
#    as possible) as the fields in the original posting.  Merge the
#    two field lists together by taking the first field list and
#    adding to the end of it any fields in the second field list that
#    weren't in the first.
# 
# 2. Parse each entry into an associative array whose keys are the
#    field names and whose values are the lines from the entry
#    corresponding to that field.  The individual field values in the
#    values of the associative array are separated by "^--\n", so they
#    can be easily parsed into a list.
# 
# 3. foreach field name found in step 1 {
#      foreach value of the field in the old entry {
#        Check if the same value appears in the new entry.  If it
#        does, then
#          Add the field and its value to the diff entry with "  "
#          in front of it, and remove the value of the field from
#          the new entry.
#        otherwise
#          Add the field and its value to the diff entry with "- "
#          in front of it.  Check if any of the values in the new
#          entry are marked important.  If so, then set the $changed
#          variable to true.  Or, set $changed to true if there are no
#          values at all for this field in the new entry.
#      }
#      foreach remaining value of the field in the new entry {
#        Add the field to the diff entry with "+ " in front of it.  If
#        the field is marked important or we are being picky, set the
#        $changed variable to true.
#      }
# 
# 4. If the $changed variable is true, then return the diff entry.
#   Otherwise, return undef.

sub compare {
    local($old, $new) = @_;
    local(%fields, @fields);
    local($_);
    local(%oldassoc, %newassoc);
    local($field_name, $old_value, $new_value);
    local(%old_values, %new_values);
    local($*) = 1;
    local($diff, $changed);
    local($tmp);

    for (&lopip'field_names($old), &lopip'field_names($new)) {
	push(@fields, $_) if (! $fields{$_}++);
    }

    %oldassoc = &entry_into_fields($old);
    %newassoc = &entry_into_fields($new);

    foreach $field_name (@fields) {
	%old_values = &by_value($field_name, $oldassoc{$field_name});
        %new_values = &by_value($field_name, $newassoc{$field_name});

	foreach $old_value (keys %old_values) {
	    if ($new_values{$old_value}) {
		$tmp = &lopip'strip_special($old_values{$old_value});
		$tmp =~ s/^/  /g;
		$diff .= $tmp;
		delete $new_values{$old_value};
	    }
	    else {
		$tmp = &lopip'strip_special($old_values{$old_value});
		$tmp =~ s/^/- /g;
		$diff .= $tmp;
		$changed++ if (!$newassoc{$field_name} ||
			       &lopip'is_important($new, $field_name));
	    }
	}
	foreach $new_value (keys %new_values) {
	    $tmp = $new_values{$new_value};
	    if ($picky || &lopip'is_important($tmp, $field_name)) {
		$changed++;
	    }
	    $tmp = &lopip'strip_special($tmp);
	    $tmp =~ s/^/+ /g;
	    $diff .= $tmp;
	}	    
    }

    if ($changed) {
	$diff;
    }
    else {
	undef;
    }
}

sub by_value {
    local($field_name, $values) = @_;
    local($*) = 1;
    local(@values) = split(/^--\n/, $values);
    local(%values);
    local($_);
    local($value);

    for (@values) {
	$value = &lopip'field_value($_, $field_name);
	die "$0: duplicate value:\n$_"
	    if ($values{$value});
	$values{$value} = $_;
    }
    %values;
}

sub entry_into_fields {
    local($entry) = @_;
    local($*) = 1;
    local($name, $value);
    local(%assoc, @values);

    $entry =~ s/\n([^ \t])/\n--\n$1/g;

    @values = split(/^--\n/, $entry);

    for (@values) {
	($name) = &lopip'field_names($_);
	die "$0: malformed field:\n$_" if (! $name);
	if ($assoc{$name}) {
	    $assoc{$name} .= "--\n" . $_;
	}
	else {
	    $assoc{$name} = $_;
	}
    }

    %assoc;
}
