The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Catmandu::Exporter::Atom;

use namespace::clean;
use Catmandu::Sane;
use XML::Atom::Feed;
use XML::Atom::Entry;
use POSIX qw(strftime);
use Moo;

with 'Catmandu::Exporter';

has title       => (is => 'ro', default => sub { undef });
has subtitle    => (is => 'ro', default => sub { undef });
has id          => (is => 'ro', default => sub { undef });
has icon        => (is => 'ro', default => sub { undef });
has logo        => (is => 'ro', default => sub { undef });
has generator   => (is => 'ro', default => sub { undef });
has updated     => (is => 'ro', default => sub { undef });
has rights      => (is => 'ro', default => sub { undef });
has ns          => (is => 'ro', default => sub { undef });

has link        => (is => 'ro', isa => sub {
                    Catmandu::BadArg->throw("Link needs to be an array hash")
                        unless !defined $_[0] || ref $_[0] eq 'ARRAY';
                 });
has author      => (is => 'ro', isa => sub {
                    Catmandu::BadArg->throw("Author needs to be an array hash")
                        unless !defined $_[0] || ref $_[0] eq 'ARRAY';
                 });
has contributor => (is => 'ro', isa => sub {
                    Catmandu::BadArg->throw("Contributor needs to be an array hash")
                        unless !defined $_[0] || ref $_[0] eq 'ARRAY';
                 });
has category    => (is => 'ro', isa => sub {
                    Catmandu::BadArg->throw("Category needs to be an array hash")
                        unless !defined $_[0] || ref $_[0] eq 'ARRAY';
                 });

has atom        => (is => 'ro', lazy => 1, builder => '_build_atom');
has extra       => (is => 'ro', default => sub { undef });

sub BUILDARGS {
   my ( $class, @args ) = @_;
   my %args = ();
   
   if (@args > 0 && @args % 2 == 0) { 
    (%args) = (@args);
    for (keys %args) {
       next unless /^[^:]+:/;
       $args{extra}->{$_} = $args{$_};
    }
   }
   
   return \%args;
}
 
sub _build_atom {
    my ($self) = @_;
    my $atom = XML::Atom::Feed->new;
    
    if (defined $self->author) {
        for (@{$self->author}) {
             my $author = XML::Atom::Person->new;
             $author->name($_->{name}) if defined $_->{name};
             $author->email($_->{email}) if defined $_->{email};
             $author->uri($_->{uri}) if defined $_->{uri};
             $author->url($_->{url}) if defined $_->{url};
             $author->homepage($_->{homepage}) if defined $_->{homepage};
             $atom->author($author);
        }
    }
    
    if (defined $self->category) {
        for (@{$self->category}) {
             my $category = XML::Atom::Category->new;
             $category->term($_->{term}) if defined $_->{term};
             $category->label($_->{label}) if defined $_->{label};
             $category->scheme($_->{scheme}) if defined $_->{scheme};
             $atom->add_category($category, 'test');
        }
    }
    
    if (defined $self->contributor) {
        for (@{$self->contributor}) {
             my $contributor = XML::Atom::Person->new;
             $contributor->name($_->{name}) if defined $_->{name};
             $contributor->email($_->{email}) if defined $_->{email};
             $contributor->uri($_->{uri}) if defined $_->{uri};
             $contributor->url($_->{url}) if defined $_->{url};
             $contributor->homepage($_->{homepage}) if defined $_->{homepage};
             $atom->contributor($contributor);
        }
    }
    
    $atom->generator($self->generator) if defined $self->generator;
    $atom->icon($self->icon) if defined $self->icon;
    $atom->id($self->id) if defined $self->id; 
    
    if (defined $self->link) {
        for (@{$self->link}) {
             my $link = XML::Atom::Link->new;
             $link->type($_->{type}) if defined $_->{type};
             $link->rel($_->{rel}) if defined $_->{rel};
             $link->href($_->{href}) if defined $_->{href};
             $link->hreflang($_->{hreflang}) if defined $_->{hreflang};
             $link->title($_->{title}) if defined $_->{title};
             $link->lenth($_->{length}) if defined $_->{length};
             $atom->add_link($link);
        }
    }
    
    $atom->logo($self->logo) if defined $self->logo;
    $atom->rights($self->rights) if defined $self->rights;
    $atom->subtitle($self->subtitle) if defined $self->subtitle;    
    $atom->title($self->title) if defined $self->title;

    my $updated = $self->updated  ? $self->updated  : strftime("%Y-%m-%dT%H:%M:%SZ",gmtime(time));
    $atom->updated($updated);
    
    if (defined $self->ns) {
        for my $key (keys %{$self->extra}) {
            next unless $key =~ /^([^:]+):(\S+)/;
            my ($prefix,$name) = ($1,$2);
            my $url = $self->ns->{$prefix};
            next unless defined $url;
            my $ns  = XML::Atom::Namespace->new( $prefix => $url );
            my $value = $self->extra->{$key};
            $atom->set($ns, $name, $value);
        }
    }
    
    $atom;
}

sub add {
    my ($self, $data) = @_;
    my $entry = XML::Atom::Entry->new;
 
    if (defined $data->{author}->{name} || defined $data->{author}->{email}) {
        my $author = XML::Atom::Person->new;
        $author->name($data->{author}->{name}) if defined $data->{author}->{name};
        $author->email($data->{author}->{email}) if defined $data->{author}->{email};
        $author->uri($data->{author}->{uri}) if defined $data->{author}->{uri};
        $author->url($data->{author}->{url}) if defined $data->{author}->{url};
        $author->homepage($data->{author}->{homepage}) if defined $data->{author}->{homepage};
        $entry->author($author);
    }
    
    if (defined $data->{category}) {
        for (@{$data->{category}}) {
            my $category = XML::Atom::Category->new;
            $category->term($_->{term}) if defined $_->{term};
            $category->label($_->{label}) if defined $_->{label};
            $category->scheme($_->{scheme}) if defined $_->{scheme};
            $entry->add_category($category);
        }
    }
    
    if (defined $data->{content}) {
        my $content = XML::Atom::Content->new;
        $content->mode($data->{content}->{mode} ? $data->{content}->{mode} : "xml");
        $content->body($data->{content}->{body});
        $entry->content($content);
    }
    
    if (defined $data->{contributor}->{name} || defined $data->{contributor}->{email}) {
           my $contributor = XML::Atom::Person->new;
           $contributor->name($data->{contributor}->{name}) if defined $data->{contributor}->{name};
           $contributor->email($data->{contributor}->{email}) if defined $data->{contributor}->{email};
           $contributor->uri($data->{contributor}->{uri}) if defined $data->{contributor}->{uri};
           $contributor->url($data->{contributor}->{url}) if defined $data->{contributor}->{url};
           $contributor->homepage($data->{contributor}->{homepage}) if defined $data->{contributor}->{homepage};
           $entry->contributor($contributor);
    }
    
    $entry->id($data->{id}) if defined $data->{id}; 
    
    if (defined $data->{link}) {
        for (@{$data->{link}}) {
            my $link = XML::Atom::Link->new;
            $link->type($_->{type}) if defined $_->{type};
            $link->rel($_->{rel}) if defined $_->{rel};
            $link->href($_->{href}) if defined $_->{href};
            $link->hreflang($_->{hreflang}) if defined $_->{hreflang};
            $link->title($_->{title}) if defined $_->{title};
            $link->length($_->{length}) if defined $_->{length};
            $entry->add_link($link);
        }
    }
    
    my $published = $data->{published} ? $data->{published} : strftime("%Y-%m-%dT%H:%M:%SZ", gmtime(time));
    $entry->published($published);
    
    $entry->rights($data->{rights}) if defined $data->{rights};
    $entry->source($data->{source}) if defined $data->{source};
    $entry->summary($data->{summary}) if defined $data->{summary};
    $entry->title($data->{title}) if defined $data->{title}; 
   
    my $updated = $data->{updated} ? $data->{updated} : strftime("%Y-%m-%dT%H:%M:%SZ", gmtime(time));
    $entry->updated($updated);
    
    # Other metadata can be in a namespace
    if (defined $self->ns) {
        for my $key (keys %{$data}) {
            next unless $key =~ /^([^:]+):(\S+)/;
            my ($prefix,$name) = ($1,$2);
            my $url = $self->ns->{$prefix};
            next unless defined $url;
            my $ns  = XML::Atom::Namespace->new( $prefix => $url );
            my $value = $data->{$key};
            $entry->set($ns, $name, $value);
        }
    }
    
    $self->atom->add_entry($entry);
}

sub commit {
    my ($self) = @_;
    my $fh = $self->fh;
    say $fh $self->atom->as_xml;
}

=head1 NAME

Catmandu::Exporter::Atom - a Atom exporter

=head1 SYNOPSIS

    use Catmandu::Exporter::Atom;

    my $blog_args = {
     id => "urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6" ,
     title => "My Blog" , 
     subtitle => "testing 1.2.3" ,
     icon => "http://icons.org/test.jpg" ,
     generator => "Catmandu::Exporter::Atom" ,
     rights => "Beer license",
     link => [
                {
         'type' => 'text/html' ,
         'rel'  => 'alternate' ,
         'href' => 'http://www.example.com' ,
                 } 
     ],
     author => [ 
               {
         'name' => 'Daffy' ,
         'email' => 'duck@toons.be' ,
               }
     ] ,
     contributor => [
                {
          'name'  => 'Bugs' ,
          'email' => 'bunny@toons.be'
                }
     ],
     ns => {
         'dc' => 'http://purl.org/dc/elements/1.1/',
     },
     'dc:source' => 'test',
    };
    
    my $exporter = Catmandu::Exporter::Atom->new(%$blog_args);

    $exporter->add_many($arrayref);
    $exporter->add_many($iterator);
    $exporter->add_many(sub { });

    $exporter->add($hashref);
    
    $exporter->add({
     'title'    => 'My Little Pony' ,
     'subtitle' => 'Data testing for you and me' ,
     'content'  => "sdsadas" ,
     'summary'  => 'Brol 123' ,
     'id'       => '1291821827128172817' ,
     'author' => {
        'name' => 'John Doe' ,
        'email' => 'john@farwaway.org' ,
     } ,
     'contributor' => {
        'name' => 'Rabbit, R' ,
        'email' => 'r.rabbit@farwaway.org' ,
        'homepage' => 'http://faraway.org/~rabbit' ,
     } ,
     'link' => [
               {
        'type' => 'text/html' ,
        'rel'  => 'alternate' ,
        'href' => 'http://www.example.com' ,
        'title' => 'Test test' ,
        'length' => '1231' ,
        'hreflang' => 'eng' ,
                } ,
               {
        'type' => 'text/html' ,
        'rel'  => 'alternate' ,
        'href' => 'http://www.example2.com' ,
                }
     ] ,
     'category' => [
                {
        'scheme' => 'http://localhost:8080/roller/adminblog' ,
        'term' => 'Music',
                }
     ] ,
     'rights' => 'Yadadada',
     'dc:subject' => 'Toyz',
    });

    printf "exported %d objects\n" , $exporter->count;

=head1 SEE ALSO

L<Catmandu::Exporter>

=cut

1;