The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package WWW::UsePerl::Journal::Thread;

use strict;
use warnings;

use vars qw($VERSION);
$VERSION = '0.16';

#----------------------------------------------------------------------------

=head1 NAME

WWW::UsePerl::Journal::Thread - Handles the retrieval of UsePerl journal comment threads

=head1 SYNOPSIS

  use WWW::UsePerl::Journal;
  use WWW::UsePerl::Journal::Thread;

  my $journal = WWW::UsePerl::Journal->new('barbie');
  my @entries = $journal->entryids();

  my $thread = WWW::UsePerl::Journal::Thread->new(
        j       => $journal, 
        thread  => $entries[0]
  );

  my @comments = $thread->commentids();
  for my $id (@comments) {
    printf "\n----\n%s [%d %s %d] %s",
	  $thread->comment($id)->subject(),
	  $thread->comment($id)->score(),
	  $thread->comment($id)->user(),
	  $thread->comment($id)->uid(),
	  $thread->comment($id)->date(),
	  $thread->comment($id)->content();
  }

  my $threadid = $thread->thread();

=head1 DESCRIPTION

A collection of routines to handle the retrieval of threads from a
UsePerl (L<http://use.perl.org/>) journal entry.

Using WWW::UsePerl::Journal, journal entry ids can be obtain. Each entry id
can be used to obtain a comment thread. Each comment property is accessed
via a comment object from within the thread.

Note that as on late 2010 use.perl was decommissioned. A read-only version of
the site now exists on the perl.org servers, and a full database backup is
also available if you wish to host your own use.perl archive. 

A future edition of this distribution will allow a DBI interface to a local
database to retrieve journal entries.

=cut

# -------------------------------------
# Library Modules

use HTTP::Request::Common;
use LWP::UserAgent;
use Time::Piece;
use WWW::UsePerl::Journal::Comment;

# -------------------------------------
# Variables

use constant USEPERL => 'http://use.perl.org/use.perl.org';

my %months = (
	'January'   => 1,
	'February'  => 2,
	'March'     => 3,
	'April'     => 4,
	'May'       => 5,
	'June'      => 6,
	'July'      => 7,
	'August'    => 8,
	'September' => 9,
	'October'   => 10,
	'November'  => 11,
	'December'  => 12,
);

# -------------------------------------
# Public Interface

=head1 PUBLIC INTERFACE

=head2 The Constructor

=over 4

=item new

  use WWW::UsePerl::Journal;
  my $journal = WWW::UsePerl::Journal->new('barbie');

  use WWW::UsePerl::Journal::Thread;
  my $j = WWW::UsePerl::Journal::Thread->new(
            j       => $journal, 
            eid     => $entryid,
  );

Creates an thread instance for the specified journal entry. An entry ID 
returned from $journal->entryids() must use the entry => $entryid form to 
obtain the correct thread.

=back

=cut

sub new {
    my ($class,%opts) = @_;

    for(qw/j eid/) {
    	return	unless(exists $opts{$_});
    }

    die "No parent object"
	    unless $opts{j}->isa('WWW::UsePerl::Journal');

    my %atts = map {$_ => $opts{$_}} qw(j eid);
    my $self = bless \%atts, $class;

    return $self;
}

=head2 Methods

=over 4

=item thread()

Returns the current thread id.

=cut

sub thread {
    my $self = shift;
	$self->_commenthash	unless($self->{thread});
	return $self->{thread};
}

=item comment($commentid)

Returns a comment object of the given comment ID

=cut

sub comment {
    my $self = shift;
    my $cid  = shift;
	my %entries = $self->_commenthash;
    return $entries{$cid};
}

=item commentids()

Returns an ascending array of the comment IDs.

Can take an optional hash containing; {descending=>1} to return a descending
list of comment IDs, {ascending=>1} to return an ascending list or
{threaded=>1} to return a thread ordered list. 'ascending' being the default.

=back

=cut

sub commentids {
    my ($self,%hash) = @_;

	my ($key,$sorter) = ('_commentids_asc',\&_ascender);
   	   ($key,$sorter) = ('_commentids_dsc',\&_descender)	if(%hash && $hash{descending});
	   ($key,$sorter) = ('_commentids_thd',sub{-1})			if(%hash && $hash{threaded});

    $self->{$key} ||= do {
        my %entries = $self->_commenthash;
        my @ids = sort $sorter keys %entries;
        \@ids;
    };

    return @{$self->{$key}};
}

# -------------------------------------
# The Private Methods

# name: commenthash
# desc: Returns a hash of WWW::UsePerl::Journal::Comment objects

sub _commenthash {
    my $self = shift;

    return %{ $self->{_commenthash} }	if($self->{_commenthash});

	# URL depends upon which id we've been given, as thread and entry
	# are different, but both can still return the thread list, just in
	# different formats

    my $user = $self->{j}->user;
	my $url = USEPERL . "/_$user/journal/$self->{eid}.html";

	my $content;
    eval { $content = $self->{j}->{ua}->request(GET $url)->content; };
	return $self->{j}->error("could not create comment list") if($@ || !$content);

    if($self->{j}->debug) {
        $self->{j}->log('mess' => "#_commenthash: url=[$url]\n");
        $self->{j}->log('mess' => "#_commenthash: content=[$content]\n");
    }

	# main comment thread
	my %comments;
    my @cids = $content =~ m! <div\s+id="comment_top_(\d+)"  !sixg;
    if($self->{j}->debug) {
        $self->{j}->log('mess' => "#cids: @cids\n");
    }

    ($self->{thread}) = $content =~ m!sid=(\d+)!;

    for my $cid (@cids) {

        if($self->{j}->debug) {
            $self->{j}->log('mess' => "\n#_commenthash: cid=[$cid]\n");
        }

		next if($comments{$cid});

        my ($extract) = $content =~ m! (<ul\s+id="[^"]+"[^>]*>\s*<li\s+id="tree_$cid"\s+class="[^"]+">.*?<div\s+id="replyto_$cid"></div>) !six;
        if($self->{j}->debug) {
            $self->{j}->log('mess' => "\n#extract: [$extract]\n");
        }

		$comments{$cid} = WWW::UsePerl::Journal::Comment->new(
                j       => $self->{j},
                cid     => $cid,
                eid     => $self->{eid},
                extract => $extract
		);
	}

	%{ $self->{_commenthash} } = %comments;
	return %{ $self->{_commenthash} };
}

# sort methods

sub _ascender  { $a <=> $b }
sub _descender { $b <=> $a }

1;

__END__

=head1 SUPPORT

There are no known bugs at the time of this release. However, if you spot a
bug or are experiencing difficulties that are not explained within the POD
documentation, please submit a bug to the RT system (see link below). However,
it would help greatly if you are able to pinpoint problems or even supply a
patch.

Fixes are dependent upon their severity and my availability. Should a fix not
be forthcoming, please feel free to (politely) remind me by sending an email
to barbie@cpan.org .

RT: L<http://rt.cpan.org/Public/Dist/Display.html?Name=WWW-UsePerl-Journal-Thread>

=head1 SEE ALSO

F<http://use.perl.org/use.perl.org>

L<WWW::UsePerl::Journal>,
L<WWW::UsePerl::Journal::Server>

=head1 AUTHOR

Barbie, E<lt>barbie@cpan.orgE<gt>
for Miss Barbell Productions L<http://www.missbarbell.co.uk>.

=head1 CREDITS

Russell Matbouli, for creating WWW::UsePerl::Journal in the first place
and giving me the idea to extend it further.

=head1 COPYRIGHT AND LICENSE

  Copyright (C) 2003-2015 Barbie for Miss Barbell Productions

This distribution is free software; you can redistribute it and/or
modify it under the Artistic Licence v2.

=cut