# Copyright 2007-2010 David Snopek <dsnopek@gmail.com>
# This program 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 2 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
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

package POE::Component::MessageQueue::Storage;
use Moose::Role;
use POE::Component::MessageQueue::Logger;

requires qw(
	get            get_all 
	get_oldest     claim_and_retrieve
	claim          store
	disown_all     disown_destination
	empty          remove     

# Given a method name, makes its first argument OPTIONALLY be an aref.  If it
# is not an aref, it normalizes it into one.  If mangle_callback is true, it
# also unpacks it in the callback.
sub _areffify
	my ($method, $mangle_callback) = @_;

	around($method => sub {
		my @args = @_;
		my $original = shift(@args);
		my $arg = $args[1];
		unless (ref $arg eq 'ARRAY')
			$args[1] = [$arg];
			if ($mangle_callback)
				my $cb = $args[-1];
				$args[-1] = sub {
					my @response = @_;
					my $arr = $response[0];
					$response[0] = (@$arr > 0) ? $arr->[0] : undef;
					@_ = @response;
					goto $cb;
		@_ = @args;
		goto $original;

_areffify($_, 1) foreach qw(get);
_areffify($_, 0) foreach qw(claim remove);

has 'names' => (
	is      => 'rw',
	isa     => 'ArrayRef',
	writer  => 'set_names',
	default => sub { [] },

has 'namestr' => (
	is      => 'rw',
	isa     => 'Str',
	default => q{},

has 'children' => (
	is => 'rw',
	isa => 'HashRef',
	default => sub { {} },

has 'logger' => (
	is      => 'rw',
	writer  => 'set_logger',
	default => sub { POE::Component::MessageQueue::Logger->new() },

sub add_names
	my ($self, @names) = @_;
	my @prev_names = @{$self->names};
	push(@prev_names, @names);

after 'set_names' => sub {
	my ($self, $names) = @_;
	while (my ($name, $store) = each %{$self->children})
		$store->set_names([@$names, $name]);
	$self->namestr(join(': ', @$names));

sub log
	my ($self, $type, $msg, @rest) = @_;
	my $namestr = $self->namestr;
	return $self->logger->log($type, "STORE: $namestr: $msg", @rest);




=head1 NAME

POE::Component::MessageQueue::Storage -- Parent of provided storage engines


The role implemented by all storage engines.  It provides a few bits of global
functionality, but mostly exists to define the interface for storage engines.


=over 2

=item optional arefs

Some functions take an "optional aref" as an argument.  What this means is
that you can pass either a plain-old-scalar argument (such as a message id) or
an arrayref of such objects.  If you pass the former, your callback (if any)
will receive a single value.  If the latter, it will receive an arrayref.
Note that the normalization is done by this role - storage engines need only
implement the version that takes an aref, and send arefs to the callbacks.

=item callbacks

Every storage method has a callback as its last argument.  Callbacks are Plain
Old Subs. If the method doesn't have some kind of return value, the callback is 
optional and has no arguments.  It's simply called so you you know the method
is done.   If the method does have some kind of return value, the 
callback is not optional and the argument will be said value.  Return values
of storage functions are not significant and should never be used.  Unless
otherwise specified, assume the functions below have plain success callbacks.



=over 2

=item set_logger I<SCALAR>

Takes an object of type L<POE::Component::MessageQueue::Logger> that should be 
used for logging.  This isn't a storage method and does not have any callback
associated with it.

=item store I<Message>

Takes one or more objects of type L<POE::Component::MessageQueue::Message> 
that should be stored.

=item get I<optional-aref>

Passes the message(s) specified by the passed id(s) to the callback.

=item get_all

=item get_oldest


=item remove I<optional-aref>

Removes the message(s) specified by the passed id(s).

=item empty

Deletes all messages from the storage engine.

=item claim I<optional-aref>, I<client-id>

Naively claims the specified messages for the specified client, even if they
are already claimed.  This is intended to be called by stores that wrap other
stores to maintain synchronicity between multiple message copies - non-store
clients usually want claim_and_retrieve.

=item claim_and_retrieve I<destination>, I<client-id>

Claims the "next" message intended for I<destination> for I<client-id> and
passes it to the supplied callback.  Storage engines are free to define what 
"next" means, but the intended meaning is "oldest unclaimed message for this 

=item disown_all I<client-id>

Disowns all messages owned by the client.

=item disown_destination I<destination>, I<client-id>

Disowns the message owned by the specified client on the specified
destination.  (This should only be one message).

=item storage_shutdown

Starts shutting down the storage engine.  The storage engine will
attempt to do any cleanup (persisting of messages, etc) before calling the


=head1 SEE ALSO