#!/usr/bin/env perl

use strict;
use warnings;
use utf8;

use open qw/:std :utf8/;

use Getopt::Long qw(:config no_ignore_case);
use Perl::Tidy;
use Pod::Usage;
use Term::ANSIColor qw/:constants/;
use List::Util qw/max/;

use Octets::To::Unicode;

my $parse_options_ok = GetOptions(
    'help|h' => \( my $help = 0 ),
    'man'    => \( my $man  = 0 ),

    'e|ext=s'       => \( my $ext       = 'pm,pl,plx,t,' ),
    'n|encodings=s' => \( my $encodings = $ENV{'RU-ENCODINGS'} // 'utf-8,cp1251,koi8-r' ),
    'b|in-branch'   => \( my $in_branch ),
);

if ( !$parse_options_ok ) {
    pod2usage(2);
}
elsif ($help) {
    pod2usage(
        -sections => "NAME|SYNOPSIS|DESCRIPTION|OPTIONS|SUBCOMMANDS",
        -verbose  => 99
    );
}
elsif ($man) {
    pod2usage( -exitval => 0, -verbose => 2 );
}
else {

    my @encodings = split /,/, $encodings;

    my @files = @ARGV;

    if ( !@files ) {
        $ext =~ s/^(.*),$/,$1/;
        $ext = join "|", map { $_ eq "" ? '' : "\\.$_" } split /,/, $ext;

        @files = grep length, split "\n",
          `git diff --name-only --diff-filter=AM origin/master...`
          if $in_branch;

        @files = map { file_find $_ }
          map { s/^\s*[\w\?]+\s+//; $_ } grep { !/^\s*D / } split /\n/,
          `git status -s`
          if !$in_branch;

        @files = grep test_perl_script($_), grep /($ext)$/, @files;
    }

    my $result     = 0;
    my $max_length = max map length, @files;

    for my $file (@files) {

        print "$file  ", " " x ( $max_length - length $file );

        my ( $unicode, $encoding ) = file_decode $file, \@encodings;

        Perl::Tidy::perltidy(
            source      => \$unicode,
            destination => \my $tidied_code,
        );

        if ( $unicode eq $tidied_code ) {
            print CYAN, "equals";
        }
        elsif ( length $tidied_code ) {
            file_encode $file, $encoding, $tidied_code;
            print GREEN, "tidied";
        }
        else {
            print RED, "failed";
            $result = 1;
        }

        print RESET, " in ", YELLOW, $encoding, RESET, "\n";
    }

    exit $result;
}

# Тест: если расширение отсутствует, то 1-я строка должна содержать perl
sub test_perl_script {
    my ($file) = @_;
    return 1 if $file =~ /\.[^.\/]*$/;
    open my $f, "<", $file or die "$file: $!";
    my $first_line = <$f>;
    close $f;
    return $first_line =~ /^#!.*?\bperl\b/;
}

__END__

=encoding utf-8

=head1 NAME

B<ru-perltidy> - утилита для форматирования файлов perltidy с определением их кодировки.

=head1 VERSION

Version 0.03

=head1 SYNOPSIS

    ru-perltidy [-h] [--man] [<files> ...] [--ext exts] [--in-branch]

=head1 DESCRIPTION

Форматирует файлы через perltidy c определением их кодировки.

Есть 3 основные режима работы:

	# Отформатировать все изменённые, но ещё не закомиченные файлы:
	$ ru-perltidy
	
	# Отформатировать изменённые и закомиченные файлы в ветке (branch-е):
	$ ru-perltidy --branch

	# Отформатировать указанные файлы:
	$ ru-perltidy file1 /root/file2
	
С помощью опции -e (--ext) можно указать расширения файлов для форматирования (по умолчанию это pm,pl,plx,t,):

	$ ru-perltidy -e pm,,t
	
Обратите внимание на пустое расширение — оно используется для исполняемых скриптов perl, которые расширения не имеют. При наличие такого расширения будет проверяться так же первая строка файла регуляркой C</^#!.*?\bperl\b/>.
	
Так же можно указать кодировки и порядок в котором они будут проверяться:

	$ ru-perltidy -n cp1251,utf-8
	
=head3 LEGENDS

На консоль утилита выведет файлы, которые были изменены с указанием: 

=over 4

=item C<equals> — код в файле совпадает с отформатированным C<perltidy>.

=item C<tidied> — код в файле отформатирован C<perltidy>.

=item C<failed> — что-то пошло не так.

=back

В случае, если хоть один из файлов C<failed>, код завершения процесса будет равен 1.

=head2 OPTIONS

=over 4

=item B<-h>, B<--help>

Показать помощь и выйти.

=item B<--man>

Распечатать мануал и завершиться.

=item B<-e> exts, B<--ext> exts

Список расширений через запятую.

По умолчанию: B<pm,pl,t,>.

Пустое расширение обозначает файлы без расширений.

=item B<-n> encodings, B<--encodings> encodings

Список кодировок через запятую.

По умолчанию берётся из переменной окружения B<RU-ENCODINGS>, а если она пуста, равняется: B<utf-8,cp1251,koi8-r>.

=item B<-b>, B<--in-branch>

Форматировать изменённые и закомиченные файлы в ветке (branch-е).

=back

=head2 ARGS

=over 4

=item B<files>...

Файлы или директории с файлами, которые нужно отформатировать.

=back

=head1 LICENSE

⚖ B<GPLv3>

=head1 AUTHOR

Yaroslav O. Kosmina E<lt>darviarush@mail.ruE<gt>

=cut
