package Gnuplot::Simple;
use Modern::Perl qw(2012);
use Kavorka;
use Types::Standard qw(ArrayRef Str);
use Text::CSV::Slurp;
use File::Slurp::Tiny 'write_file';
use Exporter::Tidy all => [qw(exec_commands write_data)];
use File::Temp qw(tempfile);
use Carp::Assert;

BEGIN {
    system("which gnuplot") == 0 || die 'Gnuplot was not found from $PATH using which.';
}

#ABSTRACT   Gnuplot::Simple - A simple way to control Gnuplot

=head1 NAME

Gnuplot::Simple - A simple way to control Gnuplot

=head1 SYNOPSIS

Gnuplot can be controlled from Perl using a pipe. This modules faciliates the process
to allow more convenient graph plotting from perl E.g.

  use GnuPlot::Simple qw(write_data);

  write_data("file.txt", [[ 1,2 ],[ 2,3 ]] );

For obvious reasons, gnuplot needs to be installed for this module to work. The presence
of gnuplot is checked using the "which" shell command to verify that it is reachable
via $PATH.

=head1 FUNCTION/ATTRIBUTES

=head2 write_data($filename, @datasets)

Write the $dataset to the file $filename to create a gnuplot data file. 
Each element in $datasets should be of the form [[<c1>...<cn>],...]

The column values must not contain newline or quote marks. They can be Unicode.

=head2 fun exec_commands ($c, $data)

Example usage:

    my $d = [ [ 1,2 ],[ 2,3 ] ];
    exec_commands(
        qq{
        set terminal png
        set output "$f"
        plot __DATA__ u 1:2 
        }, $d
    );

The function takes a string of gnuplot commands $c that is piped to gnuplot. You can give a data set as well in the array ref $data.
Then, any occurences of __DATA__ in $c are replaced by a temp file containing $data transformed to gnuplot format as
done by write_data.

=head1 LICENSE

This software is licensed under the same terms as Perl itself.

=cut

fun _is_valid ( Str $r) {
    $r =~ /^[^\n\r"]*\z/;
}

fun _check_constraint ( ArrayRef [ArrayRef] $data ) {
    die 'Non-empty data not accepted'
      unless @$data;
    for (@$data) {
        assert _is_valid( join "", @$_ ), '\n, \r or " chars not allowed in values.';
        assert @$_ > 0, "There must be more than one column";
    }
}

fun write_data ( Str $filename, $data ) {
    _check_constraint($data);
    write_file( $filename, _transform($data), binmode => ":utf8" );
}

fun _transform ( ArrayRef $data) {
    my $csv = Text::CSV->new( { binary => 1, sep_char => "\t", quote_space => 1 } );
    join( "\n", map { $csv->combine(@$_); $csv->string(); } @$data );
}

fun exec_commands ( Str $c, @data ) {
    open my $proc, "|gnuplot";
    my $tmp = File::Temp->new();
    my $tfn = $tmp->filename();
    write_data( $tfn, @data );

    #use placeholder to replace all occurences
    $c =~ s/__DATA__/'$tfn'/g;

    print $proc $c;

    binmode $proc, ":utf8";
    close $proc;
}

1;

