#!/usr/bin/perl -w

# Copyright 2016, 2017, 2019, 2020, 2021 Kevin Ryde

# This file is part of Upfiles.
#
# Upfiles 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, or (at your option) any later
# version.
#
# Upfiles is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with Upfiles.  If not, see <http://www.gnu.org/licenses/>.


# Generate HTML file listings pages for the author's data directories.
# This sort of thing is quite common, and often done dynamically by
# server-side code.  These are static listings for use from a static server.
# The form is a matter of personal preference but the code here might give
# some inspiration.


use strict;
use warnings;
use sort 'stable';
use FindBin;
use App::Upfiles;
use File::Copy;
use File::Slurp ();
use File::Temp;
use File::chdir;
use File::stat ();
use File::Find ();
use HTML::Entities::Interpolate;
use POSIX ();
use Regexp::Tr;
use Sort::Key::Natural;

# uncomment this to run the ### lines
# use Smart::Comments;

my $top = "$ENV{HOME}/tux/data";
my $canonical_top = "https://download.tuxfamily.org/user42/";
my $author = "Kevin Ryde";
my $generator = $FindBin::Script;

chdir $top or die;
my @dirs = ('.', grep {-d} File::Slurp::read_dir('.'));
### @dirs
my $year = POSIX::strftime('%Y', localtime(time()));

# $n is a file size in bytes.
# Return a string which is a human-readable form, like "50 kbytes".
sub bytes_to_human {
  my ($n) = @_;
  if ($n <  1_000) { return "$n bytes"; }
  if ($n <  10_000) { return sprintf "%.1f kbytes", $n/1000; }
  if ($n <  1_000_000) { return sprintf "%.0f kbytes", $n/1000; }
  if ($n <  10_000_000) { return sprintf "%.1f mbytes", $n/1_000_000; }
  return sprintf "%.0f mbytes", $n/1_000_000;
}

# $n is an integer like 12500.
# Insert commas between thousands like "12,500".
sub number_commas {
  my ($n) = @_;
  while ($n =~ s/(\d)(\d{3})(,|$)/$1,$2/) {};
  return $n;
}

# $str is a string.  filename_sort_key() returns a mangled form which sort()
# will put in a good human-readable order.
{
  # Mangle so sort() will have "." before "-" before "_".
  # "." is tr'ed to whichever of the three sorts first, etc.
  my $chars = '.-_';
  my $tr = Regexp::Tr->new($chars,
                           join('', sort {$a cmp $b} split //, $chars));

  sub filename_sort_key {
    my ($str) = @_;
    return Sort::Key::Natural::mkkey_natural($tr->trans($str));
  }
}

foreach my $dir (@dirs) {
  local $CWD = $dir;
  my $out_filename = 'index.html';

  my @filenames;
  if ($dir eq '.') {
    @filenames = File::Slurp::read_dir('.');
  } else {
    File::Find::find({ no_chdir => 1,
                       wanted => sub {
                         my $filename = $File::Find::name;
                         $filename =~ s{^./}{};
                         push @filenames, $filename;
                       },
                     },
                     '.');
  }

  @filenames = grep {! -d} @filenames;
  @filenames = grep {$_ ne $out_filename} @filenames;  # skip own index.html
  @filenames = grep {$_ ne App::Upfiles->DATABASE_FILENAME} @filenames;
  if ($dir eq 'debian') {
    @filenames = grep {! m{^(db|conf)/}} @filenames;
  }

  @filenames =sort {filename_sort_key($a) cmp filename_sort_key($b)} @filenames;
  ### @filenames

  my $favicon = '';
  if (-e "favicon.png") {
    $favicon = "\n<link rel=\"icon\" href=\"favicon.png\" type=\"image/png\"/>";
  }

  my $out = File::Temp->new;
  my $dir_str = ($dir eq '.' ? 'top' : $dir);
  my $dir_insert = ($dir eq '.' ? '' : "$dir/");
  my $canonical = (defined $canonical_top
                   ? "\n<link rel=\"canonical\" href=\"$canonical_top$dir_insert$out_filename\"/>"
                   : '');
  my $dateModified = POSIX::strftime('%Y-%m-%d', gmtime(time()));

  print $out <<"HERE";
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<!-- generated by $generator, DO NOT EDIT -->
<head>
<title>Directory listing - $Entitize{$dir_str}</title>
<meta name="description" content="Directory listing of $Entitize{$dir_str}.">
<meta name="Author" content="$Entitize{$author}">
<meta name="Generator" content="$Entitize{$generator}">$favicon
<meta name="viewport" content="width=device-width">$canonical
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<meta itemprop="inLanguage" content="en"/>
<meta itemprop="dateModified" content="$dateModified"/>

HERE

  if ($dir eq '.') {
    print $out <<"HERE";
<p>
<h1 itemprop="name">Directory Listing of Toplevel</h1>
HERE

    if (@dirs > 1) {
      my $join = '<p>';
      foreach my $dir (@dirs) {
        next if $dir eq '.';
        print $out "$join subdirectory <a href=\"$Entitize{$dir}/index.html\"><code>$Entitize{$dir}</code></a>\n";
        $join = '<br>';
      }
    }

  } else {
    print $out <<"HERE"
<p>
<h1 itemprop="name">Directory Listing of Subdirectory "<code>$Entitize{$dir}</code>"</h1>
(go <a href="../index.html">up directory</a>)
HERE
  }

  my $count = 0;
  my $total_bytes = 0;
  my $join = "\n<p>";
  foreach my $filename (@filenames) {
    my $bytes = -s $filename;
    $total_bytes += $bytes;
    my $st = File::stat::stat($filename);
    my $date = POSIX::strftime('<time datetime="%Y-%m-%d">%d %b %Y</time>',
                               gmtime($st->mtime));
    my $type = ($filename =~ /\.gp$/ ? 'type="text/x-pari-gp" '
                : $filename =~ /\.gri$/ ? 'type="text/x-gri" '
                : '');
    print $out $join,
      " <a ${type}href=\"$Entitize{$filename}\"><code>$Entitize{$filename}</code></a>\n",
      "    -- ",number_commas($bytes)," bytes, $date\n";
    $join = '<br>';
    $count++;
  }

  my $size_str = bytes_to_human($total_bytes);
  print $out <<"HERE";
<p>
Total $count files, $size_str.
</p>

<hr width="100%">
<p>
<small>This page Copyright $year <span itemprop="copyrightHolder">$Entitize{$author}</span>.</small>
</p>

</body>
</html>
HERE

  close $out or die;

  my $old_content = File::Slurp::read_file($out_filename, {err_mode=>'quiet'})
    // '';
  my $new_content = File::Slurp::read_file($out->filename);
  foreach ($old_content, $new_content) {  # compare sans mod dates
    s/^<meta itemprop="dateModified".*//m;
    s/Copyright \d+//m;
  }

  my $diff;
  if ($old_content eq $new_content) {
    $diff = 'unchanged';
  } else {
    $diff = 'new      ';
    File::Copy::copy($out->filename, $out_filename);
  }
  my $bytes = -s $out_filename;
  print "$diff $dir  $bytes bytes\n";

  system 'weblint',$out_filename;
}
exit 0;




__END__

# sub sort_key_dot_dash {
#   my ($str) = @_;
#   $str =~ s{(.)}{$1     eq '.' ? ".0000001."
#                    : $1 eq '-' ? ".0000002."
#                    : $1 eq '_' ? ".0000003."
#                    : $1
#                  }ge;
#   return $str;
# }
