#!/usr/bin/env perl
use strict;
use warnings;
use OptArgs2;
use Path::Tiny;
use Text::vCard::Addressbook;
use Time::Piece;

our $VERSION = '1.0.0';

arg files => (
    isa => 'ArrayRef',
    default =>
      sub { [ ( -t STDIN ) ? ( die OptArgs2::usage(__PACKAGE__) ) : '-' ] },
    greedy  => 1,
    comment => 'file to tidy (default is stdin)',
);

opt help => ( ishelp => 1 );

opt no_update => (
    isa     => 'Flag',
    alias   => 'U',
    comment => 'do not update REV value'
);

opt version => (
    isa     => 'Flag',
    alias   => 'V',
    comment => 'print version information and exit',
    trigger => sub {
        require File::Basename;
        die File::Basename::basename($0) . ' version ' . $VERSION . "\n";
    },
);

my $opts    = optargs();
my $dtstamp = localtime->strftime('%Y-%m-%dT%H%M%SZ');

foreach my $f ( @{ $opts->{files} } ) {
    vcardtidy( $f, $opts->{no_update} );
}

sub vcardtidy {
    my $input  = shift;
    my $no_rev = shift;
    my $data;
    my $file;

    if ( $input eq '-' ) {
        local $/;
        $data = <STDIN>;
    }
    else {
        $file = path($input);
        $data = $file->slurp( { binmode => ':raw:encoding(UTF-8)' } );
    }

    my $tidy = eval {
        my $ab    = Text::vCard::Addressbook->new( { 'source_text' => $data } );
        my $clean = $ab->export;

        my $clean2 = $clean =~ s/^REV:.*\015\012//mr;
        my $data2  = $data  =~ s/^REV:.*\015\012//mr;
        if ( not($no_rev) and ( $clean2 eq $clean or $clean2 ne $data2 ) ) {
            foreach my $vcard ( $ab->vcards ) {
                $vcard->REV($dtstamp);
            }
            $ab->export;
        }
        else {
            $clean;
        }
    };

    if ($@) {
        warn "Invalid VCARD in '$input': $@";
        return;
    }
    elsif ( length($tidy) == 0 ) {
        warn "Invalid VCARD in '$input'\n";
        return;
    }

    # Fix for multiple categories
    while ( $tidy =~ s/^(CATEGORIES:.*?)\\,/$1,/mg ) { }

    if ( $input eq '-' ) {
        print $tidy;
    }
    else {
        $file->spew( { binmode => ':raw:encoding(UTF-8)' }, $tidy );
    }
}

__END__

=head1 NAME

vcardtidy - normalize the format of VCARD files

=head1 VERSION

1.0.0 (2022-04-10)

=head1 SYNOPSIS

  usage: vcardtidy [FILES...] [OPTIONS...]

    Arguments:
      FILES           Str  file to tidy (default is stdin)

    Options:
      --help,      -h      print a full help message and exit
      --no-update, -U      do not update REV value
      --version,   -V      print version information and exit

=head1 DESCRIPTION

B<vcardtidy> formats VCARD files, using L<Text::vCard::Addressbook> to
normalize field order and capitalization.

By default B<vcardtidy> acts like a filter, reading from C<stdin> and
writing to C<stdout>. Any file names given as arguments will be tidied
up in place.

=head1 GLOBAL OPTIONS

=over

=item --help, -h

Print the full usage message and exit.

=item --no-update, -U

By default B<vcardtidy> sets a new "REV" timestamp. Use this flag to
prevent that.

=item --version, -V

Print the version and exit.

=back

=head1 SUPPORT

This tool is managed via github:

    https://github.com/mlawren/p5-vcardtidy

=head1 SEE ALSO

L<githook-perltidy>(1)

=head1 AUTHOR

Mark Lawrence E<lt>nomad@null.netE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright 2022 Mark Lawrence <nomad@null.net>

This program 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 3 of the License, or (at your
option) any later version.

