package Queue::Q::ReliableFIFO::Item;
use strict;

# for reasons of debugging, JSON is easier, while Serial::* is 
# faster (about 7%) and delivers smaller serialized blobs.
use JSON::XS; # use the real stuff, no fall back on pure Perl JSON please
my $serializer   = JSON::XS->new->pretty(0);
my $deserializer = JSON::XS->new->pretty(0);

#use Sereal::Encoder;
#use Sereal::Decoder;
#my $serializer   = Sereal::Encoder->new();
#my $deserializer = Sereal::Decoder->new();

my @item_info = (
    't',        # time the item was created
    'rc',       # requeue counter (how often the item is requeued)
    'fc',       # fail counter (how often the item reached max-requeue)
    'error',    # last error message
);

sub new {
    my $class = shift;
    my $self = bless { @_ }, $class;

    die "data or _serialized expected for new\n"
        if (!exists $self->{data} && !exists $self->{_serialized});

    if (!exists $self->{_serialized}) {
        $self->{_serialized} ||=
            $serializer->encode({
                t => time(),
                b => $self->{data}
             });
    }
    return $self;
}
sub time_created  { $_[0]->_get('t_created') || $_[0]->_get('t'); }
sub time_queued   { $_[0]->_get('t'); }
sub data          { shift->_get('data'); }
sub requeue_count { shift->_get('rc') || 0; }
sub fail_count    { shift->_get('fc') || 0; }
sub last_error    { shift->_get('error'); }
sub _get { 
    my ($self, $elem) = @_;
    $self->_deserialize if ! exists $self->{data};
    return exists $self->{$elem} ? $self->{$elem} : undef;
}
sub _serialized { $_[0]->{_serialized}; }
sub inc_nr_requeues {
    my $self = shift;
    my $plain = $deserializer->decode($self->{_serialized});
    $self->{rc} = ++$plain->{rc};
    $self->{_serialized} =  $serializer->encode($plain);
    return $self->{rc};
}
sub _deserialize {
    my $self = shift;
    my $plain = $deserializer->decode($self->{_serialized});
    $self->{data} = $plain->{b};
    for my $k (@item_info) {
        if (exists $plain->{$k}) {
            $self->{$k} = $plain->{$k};
        }
        else {
            delete $self->{$k} if exists $self->{$k};
        }
    }
}
1;
__END__

=head1 NAME

Queue::Q::ReliableFIFO::Item - An item object of an queue item

=head1 SYNOPSIS

  use Queue::Q::ReliableFIFO::Item;
  my $item = Queue::Q::ReliableFIFO::Item->new(data => { id => 23 });
  my $time_created = $item->time_created;   # epoch time


  # items can be created from serialized data (to be used by 
  # Queue::Q::ReliableFIFO::Redis).
  my $item = Queue::Q::ReliableFIFO::Item->new(_serialized => $serialized);
  my $data = $item->data();
  my $last_error = $item->last_error();

=head1 METHODS

=head2 $item = new(data => $data);

Constructor for an item object (key "data" for plain data that needs to 
be serialized, key _serialized for data that needs to be deserialzed).

=head2 $data = $item->data();

Returns the data where the item was created from.

=head2 $time_created = $item->time_created();

Returns de time (epoch) the item was originally created (put in the queue).

=head2 $time_queued = $item->time_queued();

Returns de time (epoch) the item was put in the queue for the last time.

=head2 $n = $item->requeue_count();

Returns the number the item was requeued. Requeuing can happen when
processing the item fails.

=head2 $n = $item->fail_count();

Returns the number the item was requeued. An item fails after the it has
been retried up to requeue_limit times (See Redis.pm).

=head2 $n = $item->last_error();

Returns the last error message of processing this item.

=head2 inc_nr_requeues();

Increases the requeue counter of this item.

=cut
