#!perl

use Defaults::Modern
  -with_types => [ 'App::vaporcalc::Types' ];

use App::vaporcalc::CmdEngine;
use App::vaporcalc::FormatString;
use App::vaporcalc::Recipe;
use App::vaporcalc::RecipeResultSet;

use Getopt::Long;
my $Opts = +{
  help => sub {
    say $_ for (
      "vaporcalc - App::vaporcalc\n",
      "  --nocolor     Disable ANSI colors\n",
      "  --help",
      "  --version",
    );
    exit 0
  },

  version => sub {
    my $vers = $App::vaporcalc::Recipe::VERSION || '[git]';
    say "vaporcalc (App::vaporcalc $vers)";
    exit 0
  },

  nocolor => 0,
};

GetOptions( $Opts,
  'help',
  'version',
  'nocolor',
);

fun getopts { state $argv = hash(%$Opts)->inflate }


fun format_resultset (RecipeResultSet $rset) {
  state $tmpl = <<'EOT';
 --> %target_quantity ml  (%target_pg%/%target_vg% PG/VG)
  base nic: %base_nic_per_ml mg/ml -- %base_nic_type%
  target nic: %target_nic_per_ml mg/ml
  flavor percentage: %flavor_percentage -- %flavor_type
  notes: %notes
 =>
  vg: [%vg ml]  pg: [%pg ml]  nic base: [%nic ml]  flavor: [%flavor ml]
  total: %total ml
EOT

  my $recipe = $rset->recipe;
  my $result = $rset->result;

  format_str( $tmpl =>
    notes => sub {
      $recipe->notes->has_any ?
        "\n" . $recipe->notes->map(sub { ' - '.$_ })->join("\n")
        : 'none'
    },

    (map {; $_ => colorify('bold red', $recipe->$_) } qw/
      target_quantity target_nic_per_ml
      base_nic_per_ml base_nic_type
      target_pg target_vg
      flavor_percentage flavor_type
    /),

    (map {; $_ => colorify('bold yellow', $result->$_) } qw/
      vg pg nic flavor total
    /),
  )
}

use Term::ANSIColor ();
fun colorify (Str $color, Str $text) {
  getopts->nocolor ? $text : Term::ANSIColor::colored($text, $color)
}

fun format_error ($err) {
  # Colorify first line only:
  return '' unless $err;
  my @lines = split "\n", "$err";
  $lines[0] = colorify red => $lines[0];
  join "\n", @lines
}


use Term::UI;
use Term::ReadLine;
my $termpkg = 
  $ENV{PERL_RL} 
  || try { use_module('Term::ReadLine::Perl5') }
  || try { use_module('Term::ReadLine::Perl')  }
  || 'Term::ReadLine';
my $term  = $termpkg->new('vcalc'); 
my $outfh = $term->OUT || \*STDOUT;
$outfh->autoflush(1);


$outfh->say('Welcome to App::vaporcalc!');

my $cmdeng = App::vaporcalc::CmdEngine->new;
my $recipe = App::vaporcalc::Recipe->new(
  target_quantity   => 10,
  base_nic_per_ml   => 100,
  target_nic_per_ml => 16,
  target_pg         => 65,
  target_vg         => 35,
  flavor_percentage => 20,
);

my $orig_prompt = colorify green => 'vcalc> ';
my $prompt;
PROMPT: while (1) {
  $prompt //= $orig_prompt;

  my $input = $term->get_reply(
    prompt  => $prompt,
    default => 'help'
  );

  next PROMPT unless $input;

  $term->addhistory($input);

  $input = 'show recipe' if $input eq 'calc';
  last PROMPT            if $input =~ /^(?:exit|quit)/i;

  my $parsed = try {
    $cmdeng->parse_cmd($input)
  } catch {
    $outfh->say( format_error(">> Parser err: $_") );
    undef
  } or next PROMPT;

  my $cmd_result = try {
    $cmdeng->prepare_cmd(
      subject => $parsed->subject,
      verb    => $parsed->verb,
      params  => $parsed->params,
      recipe  => $recipe,
    )->execute
  } catch {
    $outfh->say( format_error(">> Cmd err: $_") );
    undef
  } or next PROMPT;

  if (is_RecipeObject $cmd_result) {
    $recipe = $cmd_result
  } elsif (is_RecipeResultSet $cmd_result) {
    # RecipeResultSet came back, format and display it:
    print format_resultset($cmd_result)
  } elsif (is_Str $cmd_result) {
    # String came back, print it:
    $outfh->say( colorify yellow => $cmd_result );
  } elsif ( is_ArrayObj($cmd_result) || is_ArrayRef($cmd_result) ) {
    # Feeding back an ARRAY controls the input loop:
    last PROMPT if $cmd_result->[0] eq 'exit' or $cmd_result->[0] eq 'quit';
    next PROMPT if $cmd_result->[0] eq 'next';
  }
}


say "Bye!"; exit 0
__END__

=pod

=head1 NAME

vaporcalc - Terminal-based e-liquid recipe calculator

=head1 SYNOPSIS

  vaporcalc [OPTIONS]...

=head1 OPTIONS

  --nocolor     Disable ANSI colors

  --version
  --help
  
=head1 DESCRIPTION

A terminal-based calculator for manging DIY e-liquid recipes.

See L<App::vaporcalc>, and especially L<App::vaporcalc/"WARNING">.

=head1 COMMANDS

Commands can be issued in a reasonably natural manner; a command has a
subject, possibly a verb (action the subject should perform), and possibly
some parameters. For example, these are equally valid commands:

  vcalc> show nic base
  vcalc> nic base show
  vcalc> set nic base 36
  vcalc> nic base set 36

The complete list of available subjects and their usage is available via the
command C<help>.

There are a few commands not represented via subjects: C<calc> is an alias for
C<show recipe>. C<quit> and C<exit> will quit immediately.

=head1 AUTHOR

Jon Portnoy <avenj@cobaltirc.org>

=cut
