package Net::Async::Graphite::Draw;

our $VERSION = '0.0_1';

=encoding utf8

=head1 NAME

Net::Async::Graphite::Draw - Interpret data obtained from graphite.

=head1 SYNOPSIS

    with 'Net::Async::Graphite::Draw';

=head1 DESCRIPTION

Don't use this module directly, use L<Net::Async::Graphite> and create
objects using its C<new> method in the normal way. Those objects will
include the functionality documented here.

This role brings the capacity to interpret and encode the data
obtained from graphite in various ways. Currently those various ways
are gnuplot, and probably using a blocking module at that (although
it's pretty fast).

=head1 BUGS

Gnuplut may be called synchronously.

plot() is ridiculously naïve. Format and scale in particular need to
be dealt with, and the timestamps and step returned by graphite.

=cut

use v5.14;
use strictures 2;
use Moo::Role;
use Carp;

use Gnuplot::Builder::Dataset;
use Gnuplot::Builder::Script;
use Future;
use List::Util   qw(min max);
use Scalar::Util qw(looks_like_number);
use namespace::clean;

=head1 ROLE

=head1 METHODS

=over

=item last_value ($target, [%extra])

Calls C<render()> with the same arguments but prepended by the format
C<raw>, and returns (a L<Future> which completes to) a list of the final
datum on each line in the response.

=cut

sub last_value {
  my $self = shift;
  $self->render(raw => @_)->then(sub {
    my $response = shift or return Future->fail('no data');
    Future->done(map {
      my $result = (split /\|/)[-1];
      # Blessed object which scalar's into this but has attributes for other data?
      (split /,/, $result)[-1];
    } split /\r?\n/, $response);
  });
}

=item plot ($target, [%extra])

Return a (L<Future> which completes to) a scalar containing the result
of running a plot inside a child gnuplot process.

Mostly this exists for fun so that I can get data displayed on the
terminal.

=cut

my %plots = (
  ascii => {
    terminal     => 'dumb',
    # Use a proper module instead of tput.
    default_size => sub { [ map int, `tput cols`, `tput lines` ] },
  },
  png   => {
    terminal     => 'png',
    default_size => [ 640, 480 ],
  }
);

sub plot {
  my $self = shift;
  my (undef, %extra) = @_;
  my $format = delete $extra{format} || 'ascii'; # To not confuse render()
  return Future->fail("Invalid plot format: $format")
    unless exists $plots{$format};
  $self->_plot_gather_datasets(@_)->then(sub {
    my @req_size     = ( $extra{width} || 0, $extra{height} || 0 );
    my @default_size = @{ $self->_default_plot_size($format) };
    my @real_size    = map {
      $req_size[$_] < 0
        ? $default_size[$_] + $req_size[$_]
        : $req_size[$_] || $default_size[$_]
      } 0..1;
    my @sets = @_;
    my $min = min (map { $_->{min} } @sets) || 0;
    my $max = max (map { $_->{max} } @sets) || 1; # Nonsensical but better than die (maybe)
    my $plot = Gnuplot::Builder::Script->new(
      terminal => "$plots{$format}{terminal} size $real_size[0],$real_size[1] enhanced",
      yrange   => "[$min:$max]",
    )->plot_with(dataset   => [ map { $_->{dataset} } @_ ],
                 no_stderr => 1); # Would be nice to put it somewhere
                                  # but it's all or nothing.
    Future->done($plot);
  });
}

=item _plot_gather_datasets

I can't remember.

=cut

sub _plot_gather_datasets {
  my $self = shift;
  $self->render_asperl(@_)->then(sub {
    Future->done(map {
      my ($min, $max) = (0,0);
      my $dataset = Gnuplot::Builder::Dataset->new_data(join "\n",
        map {
          my $datum = looks_like_number $_ ? $_ : 0;
          $min = min($min, $datum);
          $max = max($max, $datum);
          $datum;
        } @{ $_->{data} });
      $dataset->set(title => "\"$_->{target}\"");
      +{ %$_, min => $min, max => $max, dataset => $dataset };
    } @_);
  });
}

=item _default_plot_size

I can't remember.

=cut

sub _default_plot_size {
  my $self = shift;
  my ($format) = @_;
  my $default = $plots{$format}{default_size};
  ref $default eq 'ARRAY' ? $default : $default->($self);
}

1;

=back

=head1 SEE ALSO

Gnuplot L<http://gnuplot.info/>

L<Net::Async::Graphite>

L<Gnuplot::Builder>

L<Future>

L<Moo>

=head1 AUTHOR

Matthew King <matthew.king@cloudbeds.com>

=cut
