#!/usr/bin/env perl

my $copyright = <<'COPYRIGHT';
# Copyright 2021 by Christian Jaeger <ch@christianjaeger.ch>
# Published under the same terms as perl itself
COPYRIGHT

use strict;
use warnings;
use warnings FATAL => 'uninitialized';
use experimental 'signatures';

my ($email_full) = $copyright =~ / by ([^\n]*)/s;

my ($mydir, $myname);

BEGIN {
    $0 =~ /(.*?)([^\/]+)\z/s or die "?";
    ($mydir, $myname) = ($1, $2);
}
use lib "$mydir/../lib";

use FP::Text::CSV qw(csv_file_to_rows);
use FP::JSON qw(to_json);
use Getopt::Long;
use FP::Hash qw(ziphash);
use FP::Div qw(identity);
use Chj::xIOUtil qw(stdout_utf8 stdin_utf8);

sub usage {
    print STDERR map {"$_\n"} @_ if @_;
    print "$myname file.csv file.mint

  Convert CSV to JSON or Mint record literal syntax. It expects the
  input file to have a title row, and uses the fields in that as the
  field names (with Mint compatible mangling).

  file.csv or file.mint can also be '-' for stdin or stdout,
  respectively

  Options:
    --json  output JSON (default)
    --mint  output Mint record literal syntax
    --auto-numbers
            treat strings that look like numbers as numbers
    --auto-integers
            strings that look like numbers and only have zeroes after
            the dot are treated as integers; currently implies
            --auto-numbers
    --repl
            open a repl instead of carrying out the identity
            conversion
    --conversion 'code'
            code is Perl that must evaluate to a function that
            receives the inputs and translate to the outputs.
            Example code:
            'sub(\$records) { groupByNumber(\$records, \"Quadrat\")}'
            or
            'sub(\$records) { groupByNumber(\$records, \"Quadrat\")->map(sub (\$group){ \$group->sort(on(hashkey(\"Frequency\"), \\&real_cmp))})}'


  ($email_full)
";
    exit(@_ ? 1 : 0);
}

my $verbose = 0;
our ($opt_json, $opt_mint, $opt_auto_numbers, $opt_auto_integers, $opt_repl,
    $opt_conversion);
GetOptions(
    "verbose"       => \$verbose,
    "help"          => sub {usage},
    "json"          => \$opt_json,
    "mint"          => \$opt_mint,
    "auto-numbers"  => \$opt_auto_numbers,
    "auto-integers" => \$opt_auto_integers,
    "repl"          => \$opt_repl,
    "conversion=s"  => \$opt_conversion,
) or exit 1;
usage unless @ARGV == 2;

our $output_format
    = $opt_json
    ? ($opt_mint ? usage "both --json and --mint given" : "JSON")
    : ($opt_mint ? "Mint"                               : "JSON");

# Not sure this is ideal but people will be confused if giving
# --auto-integers and it doesn't do anything.
$opt_auto_numbers = 1 if $opt_auto_integers;

my $settings = {
    output_format => $output_format,
    auto_numbers  => $opt_auto_numbers,
    auto_integers => $opt_auto_integers,
};

my ($file_csv, $file_mint) = @ARGV;

#my $rows= csv_file_to_rows $file_csv;

sub convertfile ($fn) {
    my $csvinput = $file_csv eq "-" ? stdin_utf8 : $file_csv;
    my $rows = csv_file_to_rows($csvinput, { sep_char => ",", eol => "\n" });

    my $titles = $rows->first;

    my $body      = $rows->rest;
    my $body_hms  = $body->map(sub ($row) { ziphash $titles, $row });
    my $body_hms2 = $fn->($body_hms);

    my $mint = "[\n" . $body_hms2->map(
        sub ($v) {
            to_json $v, $settings
        }
    )->strings_join(",\n")
        . "\n]\n";

    my $out;
    if ($file_mint eq "-") {
        $out = stdout_utf8;
    } else {
        open $out, ">:encoding(UTF-8)", $file_mint
            or die "can't open file for writing: '$file_mint'";
    }
    print $out $mint or die "print: $!";
    close $out       or die "close($file_mint): $!";
}

sub groupByNumber ($records, $key) {
    $records->sort(on(hashkey($key), \&real_cmp))
        ->group(on(hashkey($key), \&number_eq))
}

if ($opt_repl) {
    require FP::Repl;
    FP::Repl::repl();
} elsif ($opt_conversion) {
    my $function = eval $opt_conversion;
    die $@ if $@;
    ref($function) eq "CODE"
        or die
        "expecting --conversion's argument to evaluate to a function, but got: $function";
    convertfile($function);
} else {
    convertfile(\&identity);
}

#use Chj::ruse;
#use Chj::Backtrace;

