#!/usr/bin/perl -w -I /home/latest/repos/slaby-scripts/perl
use strict;
use Error qw(:try);
use Git;
use Storable;
use StableHelper;
use Getopt::Long qw(GetOptions);
use Term::ANSIColor qw(colored color);

my $myver = '4.12';
my $sdir = glob '~/linux';
GetOptions("ver=s" => \$myver,
	   "ldir=s" => \$sdir);

die "invalid arguments" if (!scalar @ARGV);

my $srepo = Git->repository($sdir);
my $repo = Git->repository();
my $newer = qr/4\.(1[3-9]|[2-9][0-9])|[5-9]\.|[1-9][0-9]\./;
my $first_git = "1da177e4c3f41524e886b7f1b8a0c1fc7321cac2";
my $renames = "/usr/share/slaby-scripts/renames-$myver";
my %renames;

print colored("Parsing renames for v$myver\n", 'green');
if (-f $renames) {
	open(REN, "<$renames") or die "cannot open $renames";
	while (<REN>) {
		chomp;
		next if /^#/;
		die "bad format of renames" unless /^(.*)\{(.*) => (.*)\}(.*)$/;
		my $from = $1 . $2 . $4;
		my $to = $1 . $3 . $4;

		$from =~ s@//@/@g;
		$to =~ s@//@/@g;

		$renames{$to} = $from;
	}
	close REN;
} else {
	print "  No renames at $renames\n"
}

print colored("Parsing full history up to v$myver\n", 'green');

my %full_history;
my $full_history_file = "/tmp/full-hist-$myver";
if (-f "$full_history_file") {
	print "  ... from cache $full_history_file\n";
	my $h = retrieve($full_history_file);
	%full_history = %$h;
} else {
	%full_history = map {
		chomp;
		$_ => 1
	} $srepo->command('rev-list', "$first_git..v$myver");
	$full_history{$first_git} = 1;
	store(\%full_history, $full_history_file);
}

print colored("Parsing history v$myver..stable/linux-$myver.y\n", 'green');

my @history = $srepo->command('log', '--format=%B', "v$myver..stable/linux-$myver.y");
@history = StableHelper::get_upstream_commits(@history);
my %history;
@history{@history} = undef;

my %remaining;
my %maybe_remove;
my %localSHAs;
my @removed;
my %header_off;

sub remove($$) {;
	my ($msg, $file) = @_;
	print $msg, " ", scalar $repo->command('rm', $file);
	push @removed, $file;
}

print color('green'), "Parsing ", scalar @ARGV, " patches\n", color('reset');

foreach my $patch (@ARGV) {
	print "  Handling $patch\n";
	open(FILE, "<", $patch) || die "cannot open $patch";
	my @header = ();
	my $has_delim = 0;
	while (my $line = <FILE>) {
		chomp $line;
		if ($line eq "---") {
			$has_delim = 1;
			last;
		}
		push @header, $line;
	}
	close FILE;

	die "no '---' in $patch" unless ($has_delim);

	$header_off{$patch} = scalar @header;

	# remove duplicates
	my @SHAs = StableHelper::get_upstream_commits(@header);
	if (scalar @SHAs == 1 && exists $history{$SHAs[0]}) {
		remove("DUP", $patch);
		next;
	}

	# remove patches for later stables only
	if (scalar @SHAs == 1) {
		my @stabletags;
		git_cmd_try {
			@stabletags = grep(/^cc:.*stable@.*[\s#]v?$newer/i,
				$srepo->command('show', '--format=%B', '-q',
					$SHAs[0]));
		} "SHA $SHAs[0] does not exist for $patch";
		if (scalar @stabletags == 1 && $stabletags[0] !~ /$myver/) {
			remove("LATER", $patch);
			next;
		}
	}

	# lines with "Fixes: ..."
	my @fixes = map { /fixes.*\s([0-9a-f]{8,})/i ? ($1) : ()  } @header;
	if (scalar @fixes) {
		my $remove = 1;
		foreach my $fix (@fixes) {
			my $bad = 0;
			try {
				$fix = $srepo->command_oneline('rev-parse', $fix);
			} catch Git::Error::Command with {
				print "\tBad fixes SHA: $fix\n";
				print "\tNot removing\n";
				$remove = 0;
				$bad = 1;
			};
			last if $bad;
			if (exists $history{$fix}) {
				print "\tFixes stable history\n";
				$remove = 0;
				last;
			}
			if (defined $full_history{$fix}) {
				print "\tFixes full history\n";
				$remove = 0;
				last;
			}
		}
		if ($remove) {
			$maybe_remove{$patch} = [ @fixes ];
		}
	}

	$remaining{$patch} = 1;
	foreach my $sha (@SHAs) {
		$localSHAs{$sha} = $patch;
	}
}

print colored("Removing\n", 'green');

whole:
foreach my $patch (keys %maybe_remove) {
	my $fixes = $maybe_remove{$patch};
	foreach my $fix (@$fixes) {
		if (exists $localSHAs{$fix}) {
			print "not removing $patch as it fixes local $localSHAs{$fix}\n";
			next whole;
		}
	}

	remove("  FIX", $patch);
	delete $remaining{$patch};
}

print colored("Mangling patches (s-o-b, renames, ...)\n", 'green');

foreach my $patch (keys %remaining) {
	open(FILE, "<", $patch) || die "cannot open $patch for r";
	my @file = <FILE>;
	close FILE;

	for (my $i = $header_off{$patch}; $i >= 0; $i--) {
		last if ($file[$i] =~ s#Signed-off-by: Greg Kroah-Hartman <gregkh\@linuxfoundation.org>#Signed-off-by: Jiri Slaby <jslaby\@suse.cz>#);
	}

	foreach (@file) {
		next unless (/^(?:--- a\/|\+\+\+ b\/)(.*)$/);
		if (defined $renames{$1}) {
			print "P $patch\n";
			print "\twas=  $_";
			substr($_, 6) = $renames{$1} . "\n";
			print "\tfinal=$_";
		}
	}

	open(FILE, ">", $patch) || die "cannot open $patch for w";
	map { print FILE } @file;
	close FILE;
}

my $rmregexp = join '|', @removed;

if (-f "series") {
	print colored("Updating series\n", 'green');

	open(FILE, "<", "series") || die "cannot open series for r";
	my @series = <FILE>;
	close FILE;

	@series = grep(!/^$rmregexp$/, @series);
	if (scalar @series) {
		open(FILE, ">", 'series') || die "cannot open series for w";
		print FILE @series;
		close FILE;
	} else {
		$repo->command_noisy('rm', 'series');
	}
}

1;
