#!/usr/bin/perl -w

use strict;
use Data::Dumper;

my @dirty;
my $git_top_abs;
my $git_top_rel;
my $home = $ENV{HOME};

sub Usage() {
    print"
chk_newer.pl:
\tfind symbols and subsheets which are newer then the .sch given on the cmd line

Example:
\t\$ ~/git/openhw/bin/find_sym.pl stm32f100_rn2483.sch
\tChecking: stm32f100_rn2483.sch
\tChecking: ~/git/openhw/share/gschem/_sub_page/driver_rs232_2_5V.max232.sch
";

    #my $r = git_top();
    #print Dumper($r);
    exit 0;
}

sub git_top_abs() {
    my $top_abs = `git rev-parse --show-toplevel`;
    chomp $top_abs;
    $top_abs;
}

sub git_top_rel() {
    my $top_rel = `git rev-parse --show-cdup`; # relative
    chomp $top_rel;
    $top_rel;
}

sub eval_env($) {
    my $line = shift;

    return "" unless defined($line);
    #print "$line\n";
    if ($line =~ m/\$\{([A-Z]+)\}/) {
	my $k = $1;
	my $v = $ENV{$k};
	#print " $k = $v\n";
	if ($v) {
	    $line =~ s/(\$\{$k\})/$v/g;
	}
    }
    #print "$line\n";

    return $line;
}

sub get_rc() {
    my @line;

    # first find the system libs
    my $lbin = `which lepton-schematic`;
    my $gbin = `which gschem`;
    chomp ($gbin, $lbin);

    my ($glib, $llib);
    if ($lbin) {
	my $pfx = $lbin;
	$pfx =~ s|/bin/lepton-schematic||;
	my $llib = $pfx . "/share/lepton-eda/sym";
	if (-d $llib) {
	    if (!$glib) {
		push @line, "cs $llib";
	    }
	    #print "lepton lib: $llib\n";
	} else { $llib = ""; }
	#print "$pfx $lbin $lib\n";
    }
    if ($gbin) {
	my $pfx = $gbin;
	$pfx =~ s|/bin/gschem||;
	my $glib = $pfx . "/share/gEDA/sym";
	if (-d $glib) {
	    if (!$llib) {
		push @line, "cs $glib";
	    }
	    #print "gschem lib: $glib\n";
	} else { $glib = ""; }
	#print "$pfx $gbin $lib\n";
    }

    # then find your configured libs
    push @line, `geda_lib.scm` ;
    chomp(@line);

    my @cmplib;
    my @srclib;

    for my $line (@line) {
	my ($k, $v) = split(/ /, $line, 2);
	$v = eval_env($v);

	if ($k eq "cs" || $k eq "c") {
	    unshift @cmplib, [ $k, $v ];
	} elsif ($k eq "s") {
	    unshift @srclib, $v;
	} elsif ($k eq "cr") {
	    @cmplib = ();
	} elsif ($k eq "sr") {
	    @srclib = ();
	}
	#print "$k|$v|\n";
    }

    #print Dumper(\@cmplib);
    #print Dumper(\@srclib);
    ( \@cmplib, \@srclib );
}

sub get_dirty() {
    # get list of checked in files reported as modified
    my @dirty = `git diff-index --name-only HEAD`;
    chomp @dirty;
    @dirty;
}

sub is_dirty($) {
    my $file = shift;

    #print "abs: " . $git_top_abs . "\n";
    #print "rel: " . $git_top_rel . "\n";
    #print "fil: " . $file . "\n";

    for my $dd (@dirty) {
	#print " " . $dd . "\n";
	if ($file =~ m/$dd/) {
	    #print " " . $dd . " dirty\n";
	    return 1;
	}
    }

    return 0;
}

sub print_cmp($) {
    my $cmp = shift;
    print "components:\n";
    for my $k (sort keys %$cmp) {
	my $v = $$cmp{$k};
	print "$v\t$k\n";
    }
}
sub print_src($) {
    my $src = shift;
    print "sources:\n";
    for my $k (sort keys %$src) {
	my $v = $$src{$k};
	print "$v\t$k\n";
    }
}

sub find_cmp($$) {
    my $file_name = shift;
    my $cmplib = shift;

    for my $r (@$cmplib) {
	my ($type, $dir) = @$r;
	if ($type eq "cs") {
	    my @list = `find $dir -type f -readable -name $file_name`;
	    chomp @list;
	    for my $ee (@list) {
		$ee =~ m|^(.*/)([^/]+)$|;
		my $dn = $1;
		my $bn = $2;
		#print "$dn | $bn |\n";
		#last;
		return ($dn, $ee);
	    }
	} elsif ($type eq "c") {
	    my $file = $dir . "/" . $file_name;
	    $file =~ s|//|/|;
	    #print "$file\n";
	    if (-r $file) {
		return ($dir, $file);
	    }
	} else {
	    print "find_cmp(): invalid rc entry: \[ $type, $dir\]\n";
	}
    }
    return ();
}

sub find_src($$) {
    my $file_name = shift;
    my $srclib = shift;

    for my $dir (@$srclib) {
	my $file = $dir . "/" . $file_name;
	$file =~ s|//|/|;
	#print "$file\n";
	if (-r $file) {
	    return ($dir, $file);
	}
    }
    return ();
}

sub commit_date($) {
    # get commit unix timestamp
    my $file = shift;
    my $ts = `git log -n1 --format="%ct" $file 2>/dev/null`;
    chomp $ts;

    $ts = -1 unless $ts;

    $ts;
}
sub get_file_data($) {
    my $file = shift;

    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($file);
    my $ts = commit_date($file);
    my $dirty = is_dirty($file);

    my %fd = (
	dirty => $dirty,
	mtime => $mtime,
	rtime => $ts,
    );

    %fd;
}

sub compare_file_data($$) {
    my $ad     = shift;
    my $bd     = shift;

    my $time_a;
    my $time_b;

    #print Dumper{%$ad};
    #print Dumper{%$bd};

    if ($$ad{dirty} || $$ad{rtime} < 0) {
	$time_a = $$ad{mtime};
    } else {
	$time_a = $$ad{rtime};
    }
    if ($$bd{dirty} || $$bd{rtime} < 0) {
	$time_b = $$bd{mtime};
    } else {
	$time_b = $$bd{rtime};
    }
    my $diff = $time_b - $time_a;

    return $diff;
}

sub chk_cmp($$$) {
    my $fd     = shift;
    my $cmp    = shift;
    my $cmplib = shift;
    #print_cmp($cmp);
    for my $k (sort keys %$cmp) {
	my $v = $$cmp{$k};
	my ($dir,$cmp_file) = find_cmp($k, $cmplib);
	#print "$v\t$k\t $cmp_file\n";
	if ($cmp_file) {
	    my %cd = get_file_data($cmp_file);
	    my $diff = compare_file_data($fd, \%cd);
	    if ($diff > 0) {
		if ($home) { $cmp_file =~ s|$home|~|; }
		print " Newer: $diff $cmp_file\n";
	    }
	} else {
	    print " Not found: $k\n";
	}
    }
}
sub chk_src($$$$) {
    my $fd     = shift;
    my $src    = shift;
    my $cmplib = shift; # for recursion
    my $srclib = shift;
    #print_src($src);
    for my $k (sort keys %$src) {
	my $v = $$src{$k};
	my ($dir,$src_file) = find_src($k, $srclib);
	#print "$v\t$k\t $src_file\n";
	if ($src_file) {
	    my %sd = get_file_data($src_file);
	    my $diff = compare_file_data($fd, \%sd);
	    if ($diff > 0) {
		if ($home) { $src_file =~ s|$home|~|; }
		print " Newer: $diff $src_file\n";
	    }
	    chk_file($src_file, $cmplib, $srclib);
	    # TODO recursion, new gafrc ?
	} else {
	    print " Not found: $k\n";
	}
	last;
    }
}

sub chk_file($$$) {
    my $file   = shift;
    my $cmplib = shift;
    my $srclib = shift;
    my %cmp;
    my %src;

    if ($home) { $file =~ s|$home|~|; }
    print "Checking: $file\n";

    for (`grep '^C\\s' $file`) {
	my @fld = split;
	my $v = $fld[6];
	$cmp{$v}++;
    }
    for (`grep '^source=' $file`) {
	chomp;
	s/^source=//;
	$src{$_}++;
    }

    return if (%cmp == 0 && %src == 0);

    my %fd = get_file_data($file);
    #print_cmp(\%cmp);
    #print_src(\%src);
    #print "Components:\n";
    chk_cmp(\%fd, \%cmp, $cmplib);
    #print "\nSources:\n";
    chk_src(\%fd, \%src, $cmplib, $srclib);
}

sub main() {
    my @file = @ARGV;

    if (@file == 0) {
	Usage();
    }

    # TODO: should we do get_rc() in $file's directory ?
    my ( $cmplib, $srclib ) = get_rc();
    @dirty = get_dirty();
    $git_top_abs = git_top_abs();
    $git_top_rel = git_top_rel();


    for my $file (@file) {
	chk_file($file, $cmplib, $srclib);
    }
}

###########

main();

__END__
