package Net::Plywood::Simple;
use 5.006;
use strict;
use warnings;

use HTTP::Tiny;
use Try::Tiny;
use JSON;

our $VERSION = 0.06;

############
## Public ##
############

sub new
{
	my $class  = shift;
	my $params = ref($_[0]) ? $_[0] : {@_};
	if (!$params->{scheme} || !$params->{host} || !$params->{port}) {
		die "arguments 'scheme', 'host' and 'port' are required";
	}
	$params->{_url}  = "$params->{scheme}://$params->{host}:$params->{port}";
	$params->{_json} = JSON->new->utf8;
	return bless($params, $class);
}

sub disjoin
{
	my ($self, $tree_key, $disjoin) = @_;
	if (!$disjoin->{key} || !$disjoin->{value}) {
		die "arguments 'key', 'value' are required to disjoin a tree";
	}
	return $self->mutate(
		$tree_key,
		{
			filter => {
				field => $disjoin->{key},
				op    => '!=',
				value => $disjoin->{value},
			}
		},
	);
}

sub get
{
	my ($self, $tree_key, @path) = @_;
	my $path = join('/', @path);
	my $response;
	try {
		$response = HTTP::Tiny->new->get($self->_url . "/tree/$tree_key/$path");
	} catch {
		die "could not get tree, error was: $_";
	};
	if (!$response->{success}) {
		die "$response->{reason} ($response->{status}): $response->{content}";
	}
	return $response;
}

sub mutate
{
	my ($self, $tree_key, $data) = @_;
	my $response;
	my $json = $self->_to_json($data);
	try {
		$response = HTTP::Tiny->new->post(
			$self->_url . "/mutate/$tree_key",
			{content => $json},
		);
	} catch {
		die "could not mutate tree, error was: $_";
	};
	if (!$response->{success}) {
		die "$response->{reason} ($response->{status}): $response->{content}";
	}
	return $response;
}

sub put
{
	my ($self, $tree_key, $data) = @_;
	if ($tree_key =~ m/\//) {
		die 'can not have forward slash ( / ) characters in key names';
	}
	my $response;
	my $json = $self->_to_json($data);
	try {
		$response = HTTP::Tiny->new->put(
			$self->_url . "/tree/$tree_key",
			{content => $json},
		);
	} catch {
		die "could not store tree, error was: $_";
	};
	if (!$response->{success}) {
		die "$response->{reason} ($response->{status}): $response->{content}";
	}
	return $response;
}

#############
## Private ##
#############

sub _to_json
{
	my ($self, $data) = @_;
	my $json;
	try {
		$json = $self->_json->encode($data);
	} catch {
		die "could not encode data as JSON, error was: $_";
	};
	return $json;
}

sub _from_json
{
	my ($self, $json) = @_;
	my $data;
	try {
		$data = $self->_json->decode($json);
	} catch {
		die "could not decode data which we thought was JSON, error was: $_";
	};
	return $data;
}

sub _json {return shift->{_json}}
sub _url  {return shift->{_url}}

1;

__END__

=head1 NAME

Net::Plywood::Simple - A simple interface to "Plywood", a hierarchical data storage and retrieval server

=head1 DESCRIPTION

This module is a simple wrapper around "Plywood", a hierarchical data storage
and retrieval server written in Erlang. The database can be cloned from:
	https://github.com/ricecake/plywood
The basic premise is to store, retrieve, and search for nodes in large JSON
tree structures that can be merged together. Mutations can also be used to
programmatically remove groups or sets of nodes from a tree.

=head1 SYNOPSIS

Storing data:

	use Net::Plywood::Simple;
	my $plywood = Net::Plywood::Simple->new(
		scheme => 'http',
		host   => 'localhost',
		port   => 8080,
	);
	my $tree_key  = 'PICKAKEY';
	my $data = {...}; # build your tree
	$plywood->put($tree_key, $data);

Retrieving data:

	use Net::Plywood::Simple;
	my $plywood = Net::Plywood::Simple->new(
		scheme => 'http',
		host   => 'localhost',
		port   => 8080,
	);
	my $data = $plywood->get('PICKAKEY');
	## OR
	my $data = $plywood->get('PICKAKEY', qw/node path in data/);

Disjoining data:

You can by key value pair present in any number of nodes programmatically
"disjoin" those nodes from their parent tree:

	use Net::Plywood::Simple;
	my $plywood = Net::Plywood::Simple->new(
		scheme => 'http',
		host   => 'localhost',
		port   => 8080,
	);
	my $data = $plywood->disjoin('PICKAKEY', {key => $key, value => $value);

Any node with that key value pair given in the arguments will be removed from the tree,
this is the canonical method in plywood to remove merges to trees, so you must be
intentional in adding key => value pairs to your nodes so that they can be specifically
selected out.

Mutate data:

Disjoin relies on mutate, and other than disjoining there are several potential actions
that mutate can get you.

	use Net::Plywood::Simple;
	my $plywood = Net::Plywood::Simple->new(
		scheme => 'http',
		host   => 'localhost',
		port   => 8080,
	);
	my $data = $plywood->mutate(
		'PICKAKEY',
		{
			filter =>
			{
				field => $key,
				op    => $operator,
				value => $value,
			}
		}
	);

Given a key, an operator, and a value above you can filter nodes from the tree.

The following operators are available:

	>  - greater than
	<  - less than
	>= - greater than or equal to
	=< - less than or equal to
	=  - equal to
	!= - not equal to

and also available are:

	like
	ilike
	unlike
	unilike

Note that these operators are based on the internal Erlang software Plywood, and
have nothing to do with Perl.

=head1 AUTHOR

Shane Utt - sutt@cpan.org

=cut

