#!/usr/bin/perl -w

use strict;
use File::Find::Rule ();
use File::Slurp qw(read_file write_file);
use File::Copy qw(copy);
use Getopt::Long;
use Data::Dumper;

my $verbose = 0;
my (@bases, @matches, @excludes);
my $potent = 0;
my $backup = 0;
my $file_limit = 0;
my $file_count = 0;

# Simplistic. Assumes you've got
# [$ foreach $blah at least one element $] which needs to be
# [$ foreach $blah (at least one element) $]
# Doesn't match brackets so "($check) ? @a : func($b)" will fool it.
my $foreach_re = qr,(\[\$\s*foreach\s+\S+\s+)([^\(\s].+?)(\s*\$\]),;

# We want any plus block which
# a) end in a semicolon; or
# b) contains a semicolon and doesn't start with a "do {"
#
# FIXME: Make this into a single regex pass if at all possible?
#
#my $plus_re = qr,\[\+\s*((?:do\s*\{.*?\});|(?<!do \{).*;.*?)\s*\+\],;
#my $plus_re = qr,\[\+\s*(do\s+{)((?(1).*\};|.*;.*?))\s*\+\],;
my $plus_re = qr,\[\+\s*(.*?)\s*\+\],;
my $plus_sub = sub {
  my $c = shift;
  return $1 if $c =~ m/^(do\s*{.*});?$/;
  return $1 if $c =~ m/^([^;]*);?$/;
  return "do { $c }" if $c =~ m/;/;
  return $c;
};

sub usage {
  die "
Usage: $0 --dir directory [--dir directory2]
          --match filematch [--match filematch2]
          [--potent] [--verbose] [--exclude path]

 -d, --dir      A base directory to recurse from. May be given
                more than once.
 -m, --match    A file glob or regex to match filename. May be
                given more than once
 -b, --backup   Back up each matched file with a .bak extension
 -e, --exclude  A full directory name to exclude. It is anchored
                at the beginning of the file path when tested.
 -l, --limit    Change at most this many files
 -p, --potent   Actually perform file content substitutions.
 -v, --verbose  Increase verbosity. May be given more than
                once

Eg: $0 -d /var/www/mysite -m '*.epl' \\
  -m '*.html' -e /var/www/mysite/not_embperl -v -v
";
}

usage() unless GetOptions( 'd|dir=s' => \@bases,
			   'm|match=s' => \@matches,
			   'e|exclude=s' => \@excludes,
			   'b|backup+' => \$backup,
			   'l|limit=i' => \$file_limit,
			   'p|potent+' => \$potent,
			   'v|verbose+' => \$verbose,
			 );
usage() unless @bases && @matches;

@excludes = map { qr/^$_/ } @excludes;

my $rule = File::Find::Rule->file    ()
			   ->readable()
			   ->writable()
			   ->name    ( @matches )
			   ->start   ( @bases );

while ( my $file = $rule->match ) {
  next if grep { $file =~ $_ } @excludes;

  # This is a file we should consider
  $file_count++;
  print "Processing $file ... " if $verbose;

  my $text = read_file( $file );

  $text =~ s/$foreach_re/$1($2)$3/igsm;
  $text =~ s/$plus_re/'[+ '.$plus_sub->($1).' +]'/iegsm;

  if ( $potent ) {
    if ( $backup ) {
      print "backing up ... " if $verbose;
      copy( $file, "$file.bak" ) or die "Cannot copy $file to $file.bak: $!";
    }
    print "saving ... " if $verbose;
    # may croak if can't overwrite file. undef returned on error.
    write_file( $file, \$text ) or die "Cannot overwrite $file: $!";
  } elsif ( $verbose > 1 ) {
    print "\nOld:\n".read_file($file)."\n--------\nNew:\n$text\n";
  }

  print "done\n" if $verbose;

  # stop if we've changed enough files
  last if ($file_limit > 0 && $file_count == $file_limit);
}

print "Changed $file_count files\n";
