#!/usr/bin/env perl
#
# 2018/01/17 Gabriel Moreau
#
# apt-get install yamllint libyaml-syck-perl libtemplate-perl libarchive-zip-perl

use strict;
use warnings;

use File::Copy qw{copy};    
use YAML::Syck;
use Getopt::Long();
use Cwd();
use Template;


my ($verbose);
Getopt::Long::GetOptions(
   'verbose' => \$verbose,
   );


my %CMD_DB = (
   'help'            => \&cmd_help,
   'version'         => \&cmd_version,
   'check'           => \&cmd_check,
   'make-link'       => \&cmd_make_link,
   'make-zip'        => \&cmd_make_zip,
   'make-author'     => \&cmd_make_author,
   'make-licence'    => \&cmd_make_licence,
   'make-copyright'  => \&cmd_make_copyright,
   'list-licence'    => \&cmd_list_licence,
   );

################################################################
# main program
################################################################

my $cmd = shift @ARGV || 'help';
if (defined $CMD_DB{$cmd}) {
   $CMD_DB{$cmd}->(@ARGV);
   }
else {
   print {*STDERR} "project-meta: command $cmd not found\n\n";
   $CMD_DB{'help'}->();
   exit 1;
   }

exit;

################################################################
# subroutine
################################################################

################################################################
# command
################################################################

sub cmd_help {
   print <<'END';
project-meta - opendata project metafile manager

 project-meta help
 project-meta version
 project-meta check
 project-meta make-link
 project-meta make-author
 project-meta make-licence
 project-meta make-copyright
 project-meta make-licence
 project-meta list-licence
END
   }

################################################################

sub cmd_version {
   print "0.0.2\n";
   }

################################################################

sub print_ok {
   my ($key, $test) = @_;
   
   printf "%-35s : %s\n", $key, $test ? 'yes' : 'no';
   }

################################################################

sub cmd_check {
   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");

   my $acronym     = $meta->{'project'}{'acronym'};
   my $current_dir = Cwd::getcwd();
   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};

   print_ok 'project/acronym',                  $acronym =~ m{\d\d\w[\w\d_]+};
   print_ok 'public-dap/dap-folder',            $dap_folder ne '' and $dap_folder =~ m{^/};
   print_ok 'dap-folder not match current_dir', $dap_folder !~ m{$current_dir};

   #print YAML::Syck::Dump($meta);
   }

################################################################

sub addfolder2list {
   my ($folderdb, $folder) = @_;
   
   $folder =~ s{/[^/]+$}{};
   
   return if $folder !~ m{/};

   $folderdb->{$folder}++;
   return addfolder2list($folderdb, $folder);
   }

################################################################

sub cmd_make_link {
   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
   my $current_dir = Cwd::getcwd();
   my $acronym     = $meta->{'project'}{'acronym'};
   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
   my $data_set    = $meta->{'public-dap'}{'data-set'};

   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENCE.txt';
   {
      # Remove doublon
      my %seen = ();
      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
      }

   # Create a list of the folder
   my %folders;
   for my $dataset (@{$data_set}) {
      addfolder2list(\%folders, $dataset);
      }

   print "chmod o+rX,o-w $current_dir\n";
   print "mkdir -p $dap_folder/$acronym\n" if not -d "$dap_folder/$acronym";
   for my $folder (sort keys %folders) {
      print "chmod o+rX,o-w $current_dir/$folder\n";
      print "mkdir -p $dap_folder/$acronym/$folder\n" if -d "$current_dir/$folder";
      }

   for my $dataset (@{$data_set}) {
      if ($dataset =~ m{/}) {
         # Folder case
         my $folder = $dataset =~ s{/[^/]+$}{}r;
         print "ln --symbolic --target-directory $dap_folder/$acronym/$folder/ $current_dir/$dataset\n";
         }
      else {
         # File case
         print "ln --symbolic --target-directory $dap_folder/$acronym/ $current_dir/$dataset\n";
         }

      }
   print "chmod -R o+rX,o-w $dap_folder/$acronym/\n";
   }

################################################################

sub cmd_make_zip {
   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
   my $current_dir = Cwd::getcwd();
   my $data_set    = $meta->{'public-dap'}{'data-set'};
   my $acronym     = $meta->{'project'}{'acronym'};

   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENCE.txt';
   {
      # Remove doublon
      my %seen = ();
      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
      }

   # Create a Zip file
   use Archive::Zip qw( :ERROR_CODES :CONSTANTS );

   my $zip = Archive::Zip->new();

   for my $dataset (@{$data_set}) {
      if (-d $dataset) {
         # Folder case
         $zip->addTree($dataset, "$acronym/$dataset");
         }
      elsif (-f $dataset) {
         # File case
         $zip->addFile($dataset, "$acronym/$dataset");
         }
      else {
         # Strange case
         print "Error: entry $dataset doesn't exists\n";
         }
      }

   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
   $year += 1900;
   $mon++;
   my $date = sprintf '%04i%02i%02i-%02i%02i', $year, $mon, $mday, $hour, $min;

   # Save the Zip file
   unless ($zip->writeToFileNamed("$current_dir/$acronym--$date.zip") == AZ_OK) {
      die 'Error: zip write error';
      }
   }

################################################################

sub cmd_make_author {
   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");

   my $current_dir = Cwd::getcwd();

   my $acronym    = $meta->{'project'}{'acronym'};
   my $authors_list = $meta->{'project'}{'authors'};

   if (-f "$current_dir/AUTHORS.txt") {
      # Test for manual or automatically generated file
      # Automatically generated file by project-meta
      my $automatic;
      open my $fh, '<', "$current_dir/AUTHORS.txt" or die $!;
      for my $line (<$fh>) {
         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
         }
      close $fh;

      if (not $automatic) {
         print "Warning: AUTHORS.txt already exists\n";
         return;
         }

      print "Warning: update AUTHORS.txt\n";
      }

   my $tt = Template->new(INCLUDE_PATH => '/usr/share/project-meta/template.d');
   my $msg_format = '';
   $tt->process('AUTHORS.tt',
      {
         acronym    => $acronym,
         authorlist => $authors_list,
      }, \$msg_format) || die $tt->error;

   open my $fh,  '>', "$current_dir/AUTHORS.txt" or die $!;
   print $fh "$msg_format\n\n";
   close $fh;
   }

################################################################

sub cmd_make_licence {
   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");

   my $current_dir = Cwd::getcwd();

   if (-f "$current_dir/LICENCE.txt") {
      print "Warning: LICENCE.txt already exists\n";
      return;
      }

   my $licence = $meta->{'public-dap'}{'data-licence'};

   if (not -f "/usr/share/project-meta/licence.d/$licence.txt") {
      print "Error: licence $licence doesn't exists in project-meta database\n";
      exit 1;
      }

   copy("/usr/share/project-meta/licence.d/$licence.txt", "$current_dir/LICENCE.txt")
      or die "Error: licence copy failed - $!";

   print "Info: LICENCE.txt file create\n";
   return;
   }

################################################################

sub cmd_make_copyright {
   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");

   my $current_dir = Cwd::getcwd();

   if (-f "$current_dir/COPYRIGHT.txt") {
      # Test for manual or automatically generated file
      # Automatically generated file by project-meta
      my $automatic;
      open my $fh, '<', "$current_dir/COPYRIGHT.txt" or die $!;
      for my $line (<$fh>) {
         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
         }
      close $fh;

      if (not $automatic) {
         print "Warning: COPYRIGHT.txt already exists\n";
         return;
         }

      print "Warning: update COPYRIGHT.txt\n";
      }
   
   my $tt = Template->new(
      INCLUDE_PATH   => '/usr/share/project-meta/template.d',
      POST_CHOMP     => 1, # Remove space and carriage return after %]
      );
   my $msg_format = '';
   $tt->process('COPYRIGHT.tt',
      {
         title       => $meta->{'project'}{'title'},
         acronym     => $meta->{'project'}{'acronym'},
         authorlist  => $meta->{'project'}{'authors'},
         description => $meta->{'project'}{'short-description'},
         licence     => $meta->{'public-dap'}{'data-licence'},
         doi         => $meta->{'publication'}{'doi'},
      }, \$msg_format) || die $tt->error;

   open my $fh,  '>', "$current_dir/COPYRIGHT.txt" or die $!;
   print $fh "$msg_format\n\n";
   close $fh;
   }

################################################################

sub cmd_list_licence {
   opendir my $dh, '/usr/share/project-meta/licence.d/' or die $!;
   for my $licence (readdir $dh) {
      # Keep only file
      next if not -f "/usr/share/project-meta/licence.d/$licence";
      
      # Keep only .txt file
      next if not $licence =~ m/\.txt$/;

      $licence =~ s/\.txt$//;
      print "$licence\n";
      }
   closedir $dh;
   }

################################################################
# documentation
################################################################

__END__

=head1 NAME

project-meta - opendata project metafile manager


=head1 USAGE

 project-meta help
 project-meta version
 project-meta check
 project-meta make-link
 project-meta make-author
 project-meta make-licence
 project-meta make-copyright
 project-meta make-licence
 project-meta list-licence

=head1 DESCRIPTION

project-meta is a small tool to maintain a set of open data files.

=head1 COMMANDS

Some command are defined in the source code but are not documented here.
Theses could be not well defined, not finished, not well tested...
You can read the source code and use them at your own risk
(like for all the Klask code).

=head2 check

 project-meta check

Check your F<PROJECT-META.yml> has the good key.
If your metafile is not a valid YAML file,
you can use C<yamllint> command to check just it's format.

=head2 make-licence

 project-meta make-licence

Copy the licence file from the project-meta licence database at the current folder with the file name: LICENCE.txt.

The licence is defined in the PROJECT-META.yml specification under the key C<public-dap/data-licence>.
The list of possible licence is given with the command L<list-licence>.

=head2 list-licence

 project-meta list-licence

Give the list of all the open data licence supported by the project-meta licence database.
At this time the possible licence are:

 license-ouverte-v2.0
 open-database-license-v1.0


=head1 METAFILE SPECIFICATION

Each project must have an open data metafile which describe the project : C<PROJECT-META.yml>.
The file is in YAML format because this is a human readable style of text file.

You can find in the project-meta software a C<PROJECT-META.sample.yml> example.
This one is actually the master reference specification!


=head1 BUG

 - make-link: folder/folder
 - make-link: support multiple doi
 - metafile: remove prop publication/url

=head1 SEE ALSO

yamllint


=head1 AUTHOR

Written by Gabriel Moreau, LEGI UMR5519, CNRS, Grenoble - France

=head1 SPECIAL THANKS

The list of people below did not directly contribute to project-meta's source code
but provided me with some data, returned bugs
or helped me in another small task like having new ideas ...
Maybe I forgot your contribution in recent years,
please forgive me in advance and send me an e-mail to correct this.

Joel Sommeria, Julien Chauchat, Cyrille Bonamy, Antoine Mathieu.


=head1 LICENSE AND COPYRIGHT

Licence GNU GPL version 2 or later and Perl equivalent

Copyright (C) 2017-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
