#!/usr/bin/perl -w

use strict;
use warnings;
use Gear::Rules;
use File::Basename;
use RPM::Source::Editor;

my $rules=Gear::Rules->new();

my $specfile=$rules->get_spec();
print "specfile=$specfile\n";
my $spec=RPM::Source::Editor->new(
    SPECFILE=>$specfile,
    );

# todo:
# if -d .git check if matching file is in index

my $modified_files=`git status --porcelain --untracked-files=no`;
chomp $modified_files;
if ($modified_files) {
    die "You have not commited changes (see git status). Commit first.
gear-rules-verify should be run in clean repository.\n";
}

my $error_no_object_found=0;

foreach my $rule (@{$rules->{RULES}}) {
    if (not $rule->{-optional}) {
	my @git_paths=$rule->git_paths();
	foreach my $git_path (@git_paths) {
	    if ($git_path->{'commit'} eq '.') {
		my @found=glob($git_path->{'path'});
		if (not @found or not -e $found[0]) {
		    $error_no_object_found=1;
		    warn "line ".$rule->{-line}.": pattern $git_path->{'path'} not found. In ".$rule->{-rulestring}."\n";
		}
	    } else {
		my $commit=$git_path->{'commit'};
		unless ($commit) {
		    $error_no_object_found=1;
		    # in Rules.pm
		    #warn "line ".$rule->{-line}.": tree_path $git_path->{'tag_template'}:$git_path->{'path_template'}: can't resolve tag $git_path->{'tag'}. In ".$rule->{-rulestring}."\n";
		} else {
		    my $found=`git ls-tree --name-only "$commit" "$git_path->{'path'}"`;
		    if (not defined $found or $found eq '') {
			warn "line ".$rule->{-line}.": git tree path $git_path->{'tag_template'}:$git_path->{'path_template'} not found. In ".$rule->{-rulestring}."\n";
			$error_no_object_found=1;
		    }
		}
	    }
	}
#	foreach my $pattern (@{$rule->{-outpatterns}}) {
#	    print STDERR "out patterns: $pattern\n";
#	}
    }
}

my $found_missing_sources=0;

#foreach source patch in spec:
# find matching rule
&match_srpm_files($rules,$spec->raw_sourcelist_ref,'Source');
&match_srpm_files($rules,$spec->raw_patchlist_ref,'Patch');

sub match_srpm_files {
    my ($rules,$filestringref,$tagstem)=@_;
    for(my $i=0; $i<@$filestringref; $i++) {
	my $src=$filestringref->[$i];
	if ($src) {
	    $src=$spec->macros->macro_subst($src);
	    my $matching_rule=$rules->match_srpm_file($src,$specfile);
	    if ($matching_rule) {
		#print STDERR "$tagstem$i: $src is matched by rule ".$matching_rule->describe()."\n";
	    } else {
		$found_missing_sources=1;
		warn "WARNING: $tagstem$i: $src: matching rule not found\n";
	    }
	}
    }
}

&report_result("rules..........",$error_no_object_found);
&report_result("spec file......",$found_missing_sources);

print "\nRobot readiness:\n";

my $source0=$spec->raw_sourcelist_ref->[0];
my $source0rule;
if ($source0) {
    $source0rule=$rules->match_srpm_file($spec->macros->macro_subst($source0),$specfile);
}

my $has_external_commits=$rules->__has_external_commits();
my $has_gear_upstream_remotes=-e '.gear/upstream/remotes';
my $specsubst=$rules->get_specsubst;
my $specsubst_nvr=0;
if ($specsubst) {
    $specsubst_nvr=1 if grep {/(?:\@\w+\@|\%\{?version)/} $rules->get_nvr;
}

&report_yes_no("Source0 rule ..............",$source0rule);
if ($has_gear_upstream_remotes) {
    &report_yes_no(".gear/upstream/remotes ....",$has_gear_upstream_remotes ? 1 : 0);
} else {
    &report_yes_no("no foreign commits ........",$has_external_commits ? 0 : 1);
}
print          "specsubst..................",$specsubst? ($specsubst_nvr ? 'on Name/Version/Release':'not on changelog (still problematic)'):'not used (good)',"\n";


if (not $source0rule) {
    print "Not ready - can't find a rule for Source0.\n";
} else {
    my $not_ready_other_reason=0;
    my $layout_analysis=$rules->__is_gear_layout_not_robot_recognizeable($source0rule);
    if ($layout_analysis) {
	print "Gear layout is not ready for automatic update:\nReason: $layout_analysis\n";
    } else {
	if ($specsubst && $specsubst_nvr) {
	    $not_ready_other_reason=1;
	    print "specsubst: changes Name or Version or Release: automatic update not implemented yet\n";
	}
	if ($source0rule->{-tree} eq '.') {
	    print  "upstream sources ..........here\n";
	    my $upstream_branch=`git branch | grep upstream`;
	    chomp $upstream_branch;
	    if ($upstream_branch and $upstream_branch=~/^\s+upstream$/ and not $has_external_commits and not $has_gear_upstream_remotes) {
		my $current_git_branch=&Gear::Git::Helper::get_current_git_branch();
		print "WARNING: found 'upstream' branch not mentioned in gear rules.
If you secretly track upstream tarball releases in 'upstream' branch,
but pack tarball from ".$current_git_branch? $current_git_branch: 'this'." branch, 
then your 'upstream' branch will be lost at git.alt/gears.
Also, GPL and other Open Source Licenses specify that you should clearly
indicate your changes to the original upstream code. For those purpose,
please, add 
diff: upstream:. . 
rule to explicitly specify that you use the 'upstream' branch,
even if the resulting diff currently is empty.
"
	    }
	} else {
	    print  "separate upstream branch ..",$source0rule->{-tree},"\n";
	    my @source0_git_paths=$source0rule->git_paths();
	    my $merge_strategy=$rules->__guess_merge_strategy($source0_git_paths[0]->{'commit'},'HEAD');
	    print  "merge to master strategy ..", $merge_strategy ? $merge_strategy : 'default', "\n";
	}
	my @diffs=$rules->sorted_diff_rules();
	if (@diffs) {
	    print "Diff merge order:\n";
	    foreach my $diff (@diffs) {
		print $diff->rulestring(),"\n";
	    }
	}
	if ($has_external_commits and not $has_gear_upstream_remotes) {
	    print "Foreign commits found but no .gear/upstream/remotes.\n";
	    print "Not compatible with tarball update.\n";
	} else {
	    print $not_ready_other_reason ? 'Not ready':'Ready', ' for ',
	    $has_gear_upstream_remotes ? 'external VCS':'tarball', " update.\n";
	}
    }
}

exit ($found_missing_sources or $error_no_object_found);

sub report_result {
    my ($message,$code)=@_;
    print $message;
    if (not defined $code) {
	print 'not tested';
    } elsif ($code) {
	print 'FAIL';
    } else {
	print 'OK';
    }
    print "\n";
}

sub report_yes_no {
    my ($message,$code)=@_;
    print $message;
    if (not defined $code) {
	print 'unknown';
    } elsif ($code) {
	print 'yes';
    } else {
	print 'no';
    }
    print "\n";
}

1;

=head1	NAME

gear-rules-verify - check consistency of .gear/rules file.

=head1	DESCRIPTION

gear-rules-verify - ALTLinux Gear

=head1	AUTHOR

Written by Igor Vlasenko <viy@altlinux.org>.

=head1	COPYING

Copyright (c) 2010-2017 Igor Vlasenko, ALT Linux Team.

This 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.

=cut

