package Cursed::Template;

use strict;
use warnings;
use include;

# Create and return a reusable template.
sub forge {
  my ($class, $list, %entries) = @_;
  my $self = bless { Fields => [] }, $class;

  $self->{Reserved} = 0;
  # Determine the width of everything
  foreach my $str (@$list) {
    # How many variables do we have?
    my $vars = $str =~ tr/</</;
    my ($len, @vars);
    if ($vars == 0) {
      $len = length($str);
    } else {
      while ($str =~ m/<([\w_]+)>/g) {
        my $name = $1;
        my $entry = $entries{$name} // croak "undefined format $name";
        push @vars, {
          Name   => $name,
          Filter => $entry->{Filter}
        };

        next if $len and $len eq "-";
        my $format = $entry->{Format};
        if (ref $format eq "ARRAY") {
          $len += longest(@$format);
        } elsif ($format eq "-") {
          $len = "-";
        } else {
          $len += length($format);
        }
      }
      unless ($len eq "-") {
        # Nuke temps then measure length of static parts
        my $static = $str;
        $static =~ s/<[\w_]+>//g;
        $len += length($static);
      }
    }

    $self->{Reserved} += $len + 2 unless $len eq "-";
    push @{ $self->{Fields} }, {
      Vars     => \@vars,
      Length   => $len,
      Template => $str
    };
  }

  return $self;
}

# Apply data to a template.
sub filter {
  my ($self, $obj, $width) = @_;
  my @ls;
  foreach my $field (@{ $self->{Fields} }) {
    my $len = $field->{Length};
    my $value = $field->{Template};

    # Sub in the variables
    foreach my $var (@{ $field->{Vars} }) {
      my $name = $var->{Name};
      my $sub = $obj->{$name} // "___";
      $sub = $var->{Filter}->($obj) if $var->{Filter};
      $value =~ s/<$name>/$sub/;
    }

    # Truncuate or pad.
    if ($len eq "-") {
      $len = $width - $self->{Reserved};
    }
    $value = substr($value, 0, $len) if length($value) > $len;
    # If we're all the way on the right, go ahead and pad-align to the right.
    if ($self->{Fields}[-1] == $field) {
      $value = " " x ($len - length($value)) . $value;
    } else {
      $value .= " " x ($len - length($value));
    }
    push @ls, $value;
  }
  return join "  ", @ls;
}

42;
