#!/usr/bin/perl
#
# Copyright 2014-2019 - Giovanni Simoni
#
# This file is part of PFT.
#
# PFT 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.
#
# PFT 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 PFT.  If not, see <http://www.gnu.org/licenses/>.

=encoding utf8

=head1 NAME

pft gen-rss - Generate a Feed RSS for the website

=head1 SYNOPSIS

B<pft gen-rss>

=head1 DESCRIPTION

This command will generate an XML file according to the RSS (Really Simple
Syndacation) specification.

The default behaviour is to position the generated file in the root of the
C<build> directory, and named C<feed.rss>.  The generated feed will contain
one C<item> element for each of last 10 blog entries.

The global PFT configuration allows to change the default path of the file
and the number of listed blog entries (see L<pft-init(1)>).

=head1 OPTIONS

=over

=item B<--help>

Show this guide

=back

=head1 EXIT STATUS

=over

=item * 1 in case of option parsing failure.

=item * 2 if it was impossible to construct the filesystem tree.

=item * 3 in case of corrupt configuration.

=back

=head1 BUGS

In PFT blog entries are timestamped with a day resolution.  There is no
obvious way to timestamp the publishing date (C<pubDate>) of blog entries
with a better time resolution, so the hour is rounded up to midnight.

The generated RSS feed will use the publishing date of the most recent entry
as C<lastBuildDate>.  This makes the implementation simple, but yields
inaccurate results should the user update an old blog entry.

=head1 SEE ALSO

L<pft(1)>, L<pft-init(1)>

=cut

use strict;
use warnings;
use utf8;
use v5.16;

use Carp;
use Digest::MD5;
use HTML::Escape qw/escape_html/;
use PFT::Tree;
use POSIX qw/strftime/;
use Encode;
use Encode::Locale;

use File::Spec::Functions qw/catfile/;
use File::Path qw/make_path/;
use File::Basename qw/dirname/;

use Pod::Usage;
use Getopt::Long;
Getopt::Long::Configure qw/bundling/;

GetOptions(
    'help|h!'       => sub {
        pod2usage
            -exitval => 1,
            -verbose => 2,
            -input => App::PFT::help_of 'gen-rss',
    },
) or exit 1;

my $tree = eval{ PFT::Tree->new } || do {
    say STDERR $@ =~ s/ at.*$//rs;
    exit 2
};

my $conf = eval{ $tree->conf } || do {
    say STDERR 'Configuration error: ', $@ =~ s/ at.*$//rs;
    exit 3;
};

my $digest = Digest::MD5->new;

my $site_title  = $conf->{site}{title};
my $site_url    = $conf->{site}{url};
my $feed_path   = $conf->{site}{feed}{path} || "feed.rss";
my $feed_url    = "$site_url/$feed_path";
my $encoding    = $conf->{site}{encoding};
my $description = $conf->{site}{feed}{description} || "News from $site_title";
my $length      = $conf->{site}{feed}{length} || 10;

my $outfile;
do {
    my $path = catfile($tree->dir_build, $feed_path);

    make_path encode(locale_fs => dirname($path));
    open($outfile, ">:encoding($encoding)", encode(locale_fs => $path))
        or die "opening $path $!";
    select $outfile;
};

sub node_to_href {
    my $node = shift;
    confess unless $node;

    join '/', $site_url, do {
        my $hdr = $node->header;
        my $k = $node->content_type;

        if ($k =~ /::Blog$/) {(
            'blog',
            sprintf('%04d-%02d', $hdr->date->y, $hdr->date->m),
            sprintf('%02d-%s.html', $hdr->date->d, $hdr->slug),
        )} elsif ($k =~ /::Month$/) {(
            'blog',
            sprintf('%04d-%02d.html', $hdr->date->y, $hdr->date->m),
        )} elsif ($k =~ /::Page$/) {(
            'pages',
            $hdr->slug . '.html',
        )} elsif ($k =~ /::Tag$/) {(
            'tags',
            $hdr->slug . '.html',
        )} elsif ($k =~ /::Picture$/) {(
            'pics',
            $node->content->relpath
        )} elsif ($k =~ /::Attachment$/) {(
            'attachments',
            $node->content->relpath
        )} else { die $k }
    }
}

print <<"END";
<?xml version="1.0" encoding="$encoding"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
 <channel>
  <title>$site_title</title>
  <link>$site_url</link>
  <generator>App::PFT</generator>
  <description>$description</description>
  <atom:link href="$feed_url" rel="self" type="application/rss+xml"/>
END


my $first = 1;
foreach my $node ($tree->content_map->blog_recent($length)) {
    my $guid;
    my $pubDate;

    $digest->add($node->title);
    $guid = $digest->hexdigest;
    $digest->reset;

    $pubDate = do {
        my($y, $m, $d) = @{$node->date};
        strftime("%a, %d %b %Y %H:%M:%S %z", 0, 0, 0, $d, $m, $y - 1900)
    };

    my $content = escape_html $node->html(\&node_to_href);

    if ($first) {
        $first = 0;

        say "<lastBuildDate>$pubDate</lastBuildDate>"
    }

    say '<item>',
        ' <title>', $node->title, '</title>',
        ' <guid isPermaLink="false">', $guid, '</guid>',
        ' <pubDate>', $pubDate, '</pubDate>',
        ' <link>', node_to_href($node), '</link>',
        ' <description>', $content, '</description>',
        '</item>'
}

print <<END;
 </channel>
</rss>
END

close $outfile;
