#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;
use File::Compare;
use File::Slurper;
use List::Util qw(shuffle);
use Storable;
use XML::LibXML;

use Ernad::Erimp;
use Ernad::Common;

binmode(STDOUT,":utf8");

my $tmp_dump_file='/tmp/u.dump';

my $impna=$ARGV[0] // '';
if(not $impna) {
  print "I need an impna\n";
  exit;
}
our $e=Ernad::Erimp->new({'impna'=>$impna});

my $style_dir=$e->{'dir'}->{'style'};
my $xslt_dir=$e->{'dir'}->{'xslt'};
my $tmp_dir=$e->{'dir'}->{'home'}.'/tmp';
my $ernad_dir=$e->{'dir'}->{'ernad'} or die;
my $tmp_xml_file="$tmp_dir/document_xslt.xml";

my $ns='http://www.w3.org/1999/XSL/Transform';

my $verbose=3;
my $test_mode=0;
my $arrow="\x{2190}";

my $i;

$i=&build_index($style_dir,$i);
$i=&build_index($xslt_dir,$i);

#store($i,$tmp_dump_file);
&document();

print "\n";
foreach my $u (sort keys %{$i->{'unused'}}) {
  my $defined_at=$i->{'unused'}->{$u} // '';
  print "Template $u, defined in $defined_at, is unused.\n";
}


sub document {
  my $count_changes=1;
  ## count the loops as to avoid an infinite one
  my $count_loops=0;
  while($count_changes>0 and $count_loops < 10) {
    $count_changes=0;
    $count_changes=$count_changes + &implement_index($xslt_dir,$i);
    $count_changes=$count_changes + &implement_index($style_dir,$i);
    $count_loops++;
  }
}


sub implement_index {
  my $dir=shift;
  my $i=shift;
  my $count_changes=0;
  foreach my $file (shuffle (`find $dir`)) {
    chomp $file;
    if(&skip($file)) {
      next;
    }
    my $file_name=$file;
    ## define a file_name without the ernad_dir for clarity
    $file_name=~s|$ernad_dir/||;
    my @lines=&File::Slurper::read_lines($file);
    my $max_lines=$#lines;
    open my $fh, '<', $file or die "I can't open $file";
    binmode $fh; 
    my $doc = XML::LibXML->load_xml('IO' => $fh,
                                    'line_numbers' => 1);
    my $root=$doc->getDocumentElement;
    my @called_templates=$root->getElementsByTagNameNS($ns,'call-template')->get_nodelist();
    @called_templates=reverse @called_templates;
    foreach my $template (@called_templates) {
      my $name=$template->getAttribute('name') // next;
      my $line_no=$template->line_number();
      ## this template is being used
      if($i->{'unused'}->{$name}) {
        delete $i->{'unused'}->{$name};
      }
      my $defined_in_file=$i->{'file'}->{$name} // '';
      if(not $defined_in_file) {
        print "Template $name on $line_no in $file is not defined anywhere.\n";
        next;
      }
      my $defined_at_line=$i->{'line'}->{$name}->{$defined_in_file} // '';
      if($verbose > 1) {
        print "$file calls $name, defined in $defined_in_file at $defined_at_line\n";
      }
      ## print previous line
      my $prev_line=$lines[$line_no-2];
      my $curr_line=$lines[$line_no-1];
      $curr_line=~m|^(\s+)|;
      my $curr_blanks=$1;
      my $comment;
      if($defined_in_file eq $file_name) {
        $comment="$curr_blanks<!-- $arrow $defined_at_line -->";
      }
      else {
        $comment="$curr_blanks<!-- $arrow $defined_in_file $defined_at_line -->";
      }
      if($prev_line=~m|^\s+<!-- .* -->\s*$|) {
        $lines[$line_no-2]="$comment";
      }
      else {
        $lines[$line_no-2].="\n$comment";
      }
    }
    # ## remove extra whitespace
    my $count_lines=0;    
    while($lines[$count_lines]) {      
      $lines[$count_lines]=~s|\s+$||;
      $count_lines++;
    }
    my $out_txt=join("\n",@lines)."\n";
    &File::Slurper::write_text($tmp_xml_file,$out_txt);
    if(compare($tmp_xml_file,$file) == 0) {
      if($verbose > 1) {
        print "no change\n";
      }
      next;
    }
    $count_changes++;
    my $wf=`xmlwf $tmp_xml_file`;
    if($wf) {
      print "I see a problem with changes to $file\n";
      die $wf;
    }
    if($verbose or $test_mode) {
      print `diff $tmp_xml_file $file`;        
    }
    ## make sure we don't update the file time
    system("touch -r $file $tmp_xml_file");
    if($test_mode) {
      system("emacs $tmp_xml_file");
      exit;
    }
    system("cp -a $tmp_xml_file $file");
  }
  return $count_changes;
}

sub skip {
  my $file=shift;
  if(not $file=~m|\.xslt\.xml$|) {
    if($verbose > 2) {
      print "I skip $file.\n";
    }
    return 1;
  }
  if(-l $file) {
    if($verbose > 2) {
      print "I skip the link $file.\n";
    }
    return 1;
  }
  if($file=~m|/obsolete/|) {
    if($verbose > 2) {
      print "I skip the obsolete $file.\n";
    }
    return 1;
  }
  return 0;
}

sub build_index {
  my $dir=shift;
  my $i=shift;
  foreach my $file (shuffle `find $dir`) {
    chomp $file;
    if(-l $file) {
      next;
    }
    my $file_name=$file;
    ## define a file_name without the ernad_dir for clarity
    $file_name=~s|$ernad_dir/||;
    if(&skip($file)) {
      next;
    }
    if($verbose > 2) {
      print "I find $file\n";
    }
    ## load
    if(-z $file) {
      die "The file $file is empty.";
    }    
    if($verbose) {
      print "I scan $file\n";      
    }
    open my $fh, '<', $file or die "I can't open $file";
    binmode $fh; 
    my $doc = XML::LibXML->load_xml('IO' => $fh,
                                   'line_numbers' => 1);
    my $root=$doc->getDocumentElement;
    foreach my $template ($root->getElementsByTagNameNS($ns,'template')) {
      my $name=$template->getAttribute('name') // next;
      my $line=$template->line_number();
      my $defined_file=$i->{'file'}->{$name} // '';
      if($defined_file) {
        if($verbose) {
          print "template $name is defined in $file_name and in $defined_file\n";        
        }
        ## mark this as non-referencable 
        $i->{'no_ref'}->{$name}=1;
        exit;
        next;        
      }
      if($verbose > 1) {
        print "file $file defines template $name\n";
      }
      $i->{'file'}->{$name}=$file_name;
      ## mark it as unused, later remove from it
      $i->{'unused'}->{$name}=$file_name;
      push(@{$i->{'template'}->{$file}},$name);
      $i->{'line'}->{$name}->{$file_name}=$line;
      $i->{'line'}->{$name}->{$file}=$line;
    }
  }
  print "I return from $dir\n";
  return $i;
}

      
