The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl

use warnings;
use strict;

use Data::Dumper;
use Carp;

use 5.010;

use File::Path qw/make_path/;
use DateTime;
use File::Spec::Functions qw/catfile/;
use Bio::Gonzales::Util::File qw/slurpc/;
use Cwd qw/fastcwd/;
use File::Touch;
use Getopt::Long::Descriptive;
use Bio::Gonzales::Util::Development::File;

our $ANALYSIS_DIR     = 'analysis';
our @STD_PROJECT_DIRS = qw(
  lib
  doc
  data
  analysis
);

our @REQUIRED_PROJECT_DIRS = qw(
  .git
  doc
  analysis
);

my ( $opt, $usage ) = describe_options(
  '%c %o <cmd> <name>', [ 'help', "print usage message and exit", ],
  [], ['<cmd> can be "init, analysis, ls, tag'],
);

print( $usage->text ), exit if $opt->help;

@ARGV = ('ls') unless ( @ARGV >= 1 );

my ( $cmd, @args ) = @ARGV;

my $date = DateTime->today->strftime("%F");

if ( $cmd =~ /^i(nit)?/ ) {
  my $name = shift @args;

  confess 'you have to supply a name' unless ($name);
  confess "name already exists" if ( -e $name );

  my @dirs = ($name);
  push @dirs, map { catfile( $name, $_ ) } @STD_PROJECT_DIRS;

  for my $d (@dirs) {
    say STDERR "Creating $d";
    make_path($d);
  }

  chdir $name;

  say STDERR "Creating Makefile";
  open my $make_fh, '>', "Makefile" or confess "Can't open filehandle: $!";
  say $make_fh ".PHONY: all clean test doc\n";
  $make_fh->close;

  create_readme( '.', $name, $date );
  say STDERR "creating git repo";

  open my $gi_fh, '>', '.gitignore' or confess "Can't open filehandle: $!";
  say $gi_fh ".rsync_backup_filter";
  say $gi_fh '/analysis/*/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*/';
  $gi_fh->close;

  system qw/git init/;
  system qw(git add Makefile README .gitignore);
  system qw/git commit -m/, "initial commit";

  say STDERR "Finished";

} elsif ( $cmd =~ /^a(nalysis)?/ ) {
  my $name = shift @args;

  my $project_dir = find_project_root();
  my $rel_dir;
  my $dir;
  if ($project_dir) {
    $project_dir = File::Spec->rel2abs($project_dir);
    $rel_dir     = catfile( $ANALYSIS_DIR, $name );
    $dir         = catfile( $project_dir, $rel_dir );

  } else {
    $rel_dir = $name;
    $dir = catfile( File::Spec->rel2abs('.'), $rel_dir );
  }
  say STDERR "Creating $dir";
  confess "name already exists" if ( -e $dir );
  make_path($dir);

  say STDERR "Creating $dir/bin";
  make_path( catfile( $dir, 'bin' ) );

  say STDERR "Creating $dir/data";
  make_path( catfile( $dir, 'data' ) );

  say STDERR "Creating $dir/playground";
  make_path( catfile( $dir, 'playground' ) );

  chdir $dir;
  say STDERR "Creating Makefile";
  open my $make_fh, '>', "Makefile" or confess "Can't open filehandle: $!";
  print $make_fh <<'EOF';
.PHONY: all

ifndef ANALYSIS_VERSION
  ifneq ("$(wildcard av)","")
    ANALYSIS_VERSION=$(shell cat av)
  else
    ANALYSIS_VERSION="."
  endif
endif

$(info analysis version: $(ANALYSIS_VERSION))
export ANALYSIS_VERSION

AV=$(ANALYSIS_VERSION)
NAME=$(lastword $(subst /, ,$(abspath .)))

all: dir

dir:
	@[ -d $(AV) ] || mkdir $(AV)

help:
	@grep -E '^[a-z0-9A-Z_.-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[1m%-30s\033[0m %s\n", $$1, $$2}'

EOF
  $make_fh->close;

  open my $av_fh, '>', 'av' or confess "Can't open filehandle: $!";
  say $av_fh $date;
  $av_fh->close;

  open my $rsync_fh, '>', '.rsync_backup_filter' or confess "Can't open filehandle: $!";
  say $rsync_fh '- [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*/';
  $rsync_fh->close;

  say STDERR "Creating IO files dictionary";
  open my $gonzconf_fh, '>', 'gonz.conf.yml' or die "Can't open filehandle: $!";
  say $gonzconf_fh '---';
  close $gonzconf_fh;
  #touch("INDEX");

  ( my $pretty_name = $name );    # =~ s/^\d+_//;
  create_readme( '.', $pretty_name, $date );

  #system qw/git init/;
  system qw/git add Makefile README av gonz.conf.yml/;
  system qw/git commit -m/, "added $name";

  if ($project_dir) {
    say STDERR 'updating project makefile';
    open my $main_mkf_fh, '>>', catfile( $project_dir, 'Makefile' ) or confess "Can't open filehandle: $!";
    say $main_mkf_fh "";
    say $main_mkf_fh "# created on $date";
    say $main_mkf_fh "$name:";
    say $main_mkf_fh "\tcd $rel_dir && \$(MAKE)";
    $main_mkf_fh->close;
  } else {
    say STDERR 'NOT updating project makefile';
  }
  say STDERR "Finished";
} elsif ( $cmd eq 'ls' ) {
  my $dir = catfile( find_project_root(), $ANALYSIS_DIR );

  my @all_analysis_runs;
  confess "could not find $dir" unless ( -d $dir );

  opendir( my $dh, $dir ) || confess "could not open dir $dir";
  my @analysis_dirs = readdir($dh);
  @analysis_dirs = grep { !/^\./ && -d catfile( $dir, $_ ) } @analysis_dirs;
  closedir($dh);

  for my $ad (@analysis_dirs) {
    my $abs_ad = catfile( $dir, $ad );
    opendir( my $adh, $abs_ad ) || confess "could not open $abs_ad";
    my @runs = grep { /^\d{4}-\d\d-\d\d$/ && -d catfile( $abs_ad, $_ ) } readdir($adh);
    closedir($adh);
    push @all_analysis_runs, map { [ $ad, $_ ] } @runs;
  }
  @all_analysis_runs = sort { $a->[1] cmp $b->[1] } @all_analysis_runs;

  for my $ar (@all_analysis_runs) {
    say $ar->[1] . "    " . catfile(@$ar);
  }
} elsif ( $cmd eq 'tag' ) {
  confess "no analysis version file found" unless ( -f 'av' );

  my $analysis_version = ( slurpc('av') )[0];
  my $curdir           = ( File::Spec->splitpath(fastcwd) )[2];
  say $curdir . "_" . $analysis_version;
} else {
  print( $usage->text ), exit;
}

sub create_readme {
  my ( $path, $name, $date ) = @_;

  say STDERR "Creating README file";
  open my $readme_fh, '>', catfile( $path, 'README' ) or confess "Can't open filehandle: $!";
  say $readme_fh uc($name);
  say $readme_fh "=" x length($name);
  say $readme_fh "";
  say $readme_fh "Joachim Bargsten <jw\@bargsten.org>";
  say $readme_fh "$name erstellt am $date.";
  say $readme_fh "\n";
  $readme_fh->close;
}

sub find_project_root {
  my $home = Bio::Gonzales::Util::Development::File::find_root(
    { location => '.', dirs => [@REQUIRED_PROJECT_DIRS], files => ['Makefile'] } );

  unless ( defined($home) ) {
    carp("Could not find project home, using current dir");
    return;
  }

  return $home;
}