# This file is part of the Owl Positioning System (OwlPS) project.  It is
# subject to the copyright notice and license terms in the COPYRIGHT.t2t file
# found in the top-level directory of this distribution and at
# https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t
# No part of the OwlPS Project, including this file, may be copied, modified,
# propagated, or distributed except according to the terms contained in the
# COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be distributed along with this
# file, either separately or by replacing this notice by the COPYRIGHT.t2t
# file's contents.


=head1 NAME

OwlPS::CSV - manipulate OwlPS CSV strings


=head1 SYNOPSIS

    use OwlPS::CSV;

    get_field("a;b;c;d", 1);            # returns "b"
    update_field("a;b;c", 0, "z");      # returns "z;b;c"
    split_timestamp("42.123456789");    # returns (42, 123456789)
    split_point_2d("4.3;8.1");          # returns (4.3, 8.1)
    split_point_2d("3;8;7");            # returns (3, 8, 7)
    round2(3.14159);                    # returns "3.14"
    round2(0);                          # returns "0.00"


=head1 DESCRIPTION

The OwlPS::CSV Perl module provides functions to deal with the CSV format used
in OwlPS. Upon error, all these functions will kill the program. This may change
in future versions.

The following functions are provided and exported by default:

=over

=cut


package OwlPS::CSV;
require Exporter;

our @ISA    = qw(Exporter);
our @EXPORT = qw(
  get_field
  update_field
  split_timestamp
  split_point_2d
  split_point_3d
  round2
);
our $VERSION = 1.00;

use strict;
use Carp;
use Scalar::Util qw(looks_like_number);


=item get_field($string, $field_idx)

This function returns the field number $field_idx of the CSV string $string.
The first field of the string has number 0.
=cut
sub get_field($$) {
    my ($string, $field_idx) = @_;

    # Split the line
    my @fields = split(';', $string);
    if (@fields <= $field_idx) {
        croak "Bad number of fields in CSV string \"$string\"";
    }

    return $fields[$field_idx];
}


=item update_field($string, $field_idx, $value)

This function updates the field number $field_idx the CSV string $string with
$value.
It returns the modified string ($string is left untouched).
The first field of the string has number 0.
=cut
sub update_field($$$) {
    my ($string, $field_idx, $value) = @_;

    # Split the line
    my @fields = split(';', $string);
    if (@fields <= $field_idx) {
        croak "Bad number of fields in CSV string \"$string\"";
    }

    # Update the corresponding field
    $fields[$field_idx] = $value;

    # Reconstitute the line
    my $newstring = shift @fields;
    foreach my $field (@fields) { $newstring .= ";$field" }

    return $newstring;
}


=item split_timestamp($timestamp)

This function splits a timestamp $timestamp which is a string
"seconds.nanoseconds".
It returns an array ($seconds, $nanoseconds).
=cut
sub split_timestamp($) {
    my $timestamp = $_[0];
    my ($sec, $nsec) = split('\.', $timestamp, 2);

    foreach ($sec, $nsec) {
        if (!(defined && looks_like_number($_))) {
            croak "Malformed timestamp string \"$timestamp\"";
        }
    }

    if (length($nsec) != 9) {
        croak "Malformed timestamp string \"$timestamp\": the nanosecond field"
          . " must have exactly 9 digits";
    }

    return ($sec, $nsec);
}


=item split_point_2d($point)

This funtion splits the 2-D point $point (string format: "X;Y").
It returns an array ($x, $y).
=cut
sub split_point_2d($) {
    my $point = $_[0];
    my ($x, $y) = split(';', $point, 2);

    foreach ($x, $y) {
        if (!(defined && looks_like_number($_))) {
            croak "Malformed 2-D point string \"$point\"";
        }
    }

    return ($x, $y);
}


=item split_point_3d($point)

This funtion splits the 3-D point $point (string format: "X;Y;Z").
It returns an array ($x, $y, $z).
=cut
sub split_point_3d($) {
    my $point = $_[0];
    my ($x, $y, $z) = split(';', $point, 3);

    foreach ($x, $y) {
        if (!(defined && looks_like_number($_))) {
            croak "Malformed 3-D point string \"$point\"";
        }
    }

    return ($x, $y, $z);
}


=item round2($f)

This function rounds a floating point number $f to two decimals.
It returns the rounded value of $f as a string.
=cut
sub round2($) {
    my $f = $_[0];

    if (!(defined($f) && looks_like_number($f))) {
        croak "Not a valid number \"$f\"";
    }

    return sprintf("%.2f", $f);
}


=back

=head1 COPYING

This module and its documentation are part of the Owl Positioning System (OwlPS)
project. They are subject to the copyright notice and license terms in the
COPYRIGHT.t2t file found in the top-level directory of the OwlPS distribution
and at https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t


=head1 SEE ALSO

owlps(7)

=cut


# vim: tabstop=4:shiftwidth=4:expandtab:textwidth=80
