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

#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.

=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 (Str $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 $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_ascii ( Str $r) {
    $r =~ /^[^\n\r"]*\z/;
}

fun _check_constraint ( ArrayRef [ArrayRef] $data ) {
    die 'Non-empty data not accepted'
      unless @$data;
    for (@$data) {
        assert _is_ascii( join "", @$_ ), "Non-ascii chars not allowed";
        assert @$_ > 0, "There must be more than one column";
    }
}

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

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, "| /usr/bin/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;

