# -*- mode: perl -*-
# Copyright (C) 2017–2021  Alex Schroeder <alex@gnu.org>

# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
# This program 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 Affero General Public License for more
# details.
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.

=encoding utf8

=head1 NAME

App::Phoebe::Galleries - serving sitelen mute image galleries via Gemini


This extension only makes sense if you have image galleries created by
F<fgallery> or L<App::sitelenmute>.

If you do, you can serve them via Gemini. You have to configure two things: in
which directory the galleries are, and under what host they are served (the code
assumes that when you are virtual hosting multiple domains, only one of them has
the galleries).

C<$galleries_dir> is the directory where the galleries are. This assumes that
your galleries are all in one directory. For example, under
F</home/alex/alexschroeder.ch/gallery> you’d find F<2016-altstetten> and many
others like it. Under F<2016-altstetten> you’d find the F<data.json> and
F<index.html> files (both of which get parsed), and the various subdirectories.

In your F<config> file:

    package App::Phoebe::Galleries;
    our $galleries_dir = "/home/alex/alexschroeder.ch/gallery";
    our $galleries_host = "alexschroeder.ch";
    use App::Phoebe::Galleries;


package App::Phoebe::Galleries;
use App::Phoebe qw(@extensions $log port success result print_link);
use File::Slurper qw(read_dir read_binary read_text);
use Encode qw(encode_utf8);
use Modern::Perl;
use Mojo::JSON qw(decode_json encode_json);

# galleries

push(@extensions, \&galleries);

our $galleries_dir = "/home/alex/alexschroeder.ch/gallery";
our $galleries_host = "alexschroeder.ch";

sub gallery_title {
  my $dir = shift;
  $dir =~ s/-/ /g;
  return join(' ', map {ucfirst} split(' ', $dir));

sub galleries {
  my $stream = shift;
  my $url = shift;
  my $host = $galleries_host;
  my $port = port($stream);
  if ($url =~ m!^gemini://$host(?::$port)?/do/gallery$!) {
    $log->info("Serving galleries");
    $stream->write("# Galleries\n");
    for my $dir (
      sort {
	my ($year_a, $title_a) = split(/-/, $a, 2);
	my ($year_b, $title_b) = split(/-/, $b, 2);
	return ($year_b <=> $year_a || $title_a cmp $title_b);
      } grep {
	-d "$galleries_dir/$_"
      } read_dir($galleries_dir)) {
      gallery_print_link($stream, $host, gallery_title($dir), "do/gallery/$dir");
    return 1;
  } elsif (my ($dir) = $url =~ m!^gemini://$host(?::$port)?/do/gallery/([^/?]*)$!) {
    if (not -d "$galleries_dir/$dir") {
      result($stream, "40", "This is not actuall a gallery");
      return 1;
    if (not -r "$galleries_dir/$dir/data.json") {
      result($stream, "40", "This gallery does not contain a data.json file like the one created by sitelen-mute or fgallery");
      return 1;
    my $bytes = read_binary("$galleries_dir/$dir/data.json");
    if (not $bytes) {
      result($stream, "40", "Cannot read the data.json file in this gallery");
      return 1;
    my $data;
    eval { $data = decode_json $bytes };
    $log->error("decode_json: $@") if $@;
    if ($@ or not %$data) {
      result($stream, "40", "Cannot decode the data.json file in this gallery");
      return 1;
    $log->info("Serving gallery $dir");
    if (-r "$galleries_dir/$dir/index.html") {
      my $dom = Mojo::DOM->new(read_text("$galleries_dir/$dir/index.html"));
      $log->info("Parsed index.html");
      my $title = $dom->at('*[itemprop="name"]');
      $title = $title ? $title->text : gallery_title($dir);
      $stream->write(encode_utf8 "# $title\n");
      my $description = $dom->at('*[itemprop="description"]');
      $stream->write(encode_utf8 $description->text . "\n") if $description;
      $stream->write("## Images\n");
    } else {
      $stream->write(encode_utf8 "# " . gallery_title($dir) . "\n");
    for my $image (@{$data->{data}}) {
      $stream->write(encode_utf8 join("\n", grep /\S/, @{$image->{caption}}) . "\n") if $image->{caption};
      gallery_print_link($stream, $host, "Thumbnail", "do/gallery/$dir/" . $image->{thumb}->[0]);
      gallery_print_link($stream, $host, "Image", "do/gallery/$dir/" . $image->{img}->[0]);
    return 1;
  } elsif (my ($file, $extension) = $url =~ m!^gemini://$host(?::$port)?/do/gallery/([^/?]*/(?:thumbs|imgs)/[^/?]*\.(jpe?g|png))$!i) {
    if (not -r "$galleries_dir/$file") {
      $stream->write(encode_utf8 "40 Cannot read $file\r\n");
    } else {
      success($stream, $extension =~ /^png$/i ? "image/png" : "image/jpeg");
      $log->info("Serving image $file");
    return 1;

sub gallery_print_link {
  my $stream = shift;
  my $host = shift;
  my $title = shift;
  my $id = shift;
  return print_link($stream, $host, undef, $title, $id);