package Graphics::Grid::UnitList;

# ABSTRACT: List of UnitLikes

use Graphics::Grid::Class;

our $VERSION = '0.001'; # VERSION

use List::AllUtils;
use Scalar::Util qw(looks_like_number);
use Type::Params ();
use Types::Standard qw(Str ArrayRef Any Num Maybe);

use Graphics::Grid::Util qw(points_to_cm);
use Graphics::Grid::Types qw(:all);

extends 'Forest::Tree';

has _list => ( is => 'ro', default => sub { [] } );


has elems =>
  ( is => 'ro', lazy => 1, builder => '_build_elems', init_arg => undef );

method _build_elems () {
    return List::AllUtils::sum( map { $_->elems } @{ $self->_list } );
}

has is_null_unit => (
    is       => 'ro',
    lazy     => 1,
    builder  => '_build_is_null_unit',
    init_arg => undef
);

method _build_is_null_unit () {
    return ( List::AllUtils::all { $_->is_null_unit } @{ $self->_list } );
}

with qw(
  Graphics::Grid::UnitLike
);

around BUILDARGS( $orig, $class : UnitLike @rest ) {

    # unfold if an element is already a UnitList.
    my @list = map { $_->$_isa($class) ? @{ $_->_list } : $_ } @rest;
    $class->$orig( _list => \@list );
}



method slice ($indices) {
    my $class = ref($self);
    return $class->new( map { $self->at($_) } @$indices );
}

method at ($idx) {
    $idx %= $self->elems;

    for my $u ( @{ $self->_list } ) {
        my $l = $u->elems;
        if ( $idx < $l ) {
            return $u->at($idx);
        }
        $idx -= $l;
    }
}

method string () {
    return join( ", ", map { $_->string } @{ $self->_list } );
}

method _make_operation ( $op, $other, $swap = undef ) {
    my $class = ref($self);
    return $class->new(
        map { $self->at($_)->_make_operation( $op, $other->at($_) ) }
          ( 0 .. $self->elems - 1 ) );
}

method transform_to_cm($grid, $idx, $gp, $length_cm) {
    return $self->at($_)->transform_to_cm($grid, $idx, $gp, $length_cm);
}

__PACKAGE__->meta->make_immutable;

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Graphics::Grid::UnitList - List of UnitLikes

=head1 VERSION

version 0.001

=head1 SYNOPSIS

    use Graphics::Grid::UnitList;
    my $ul1 = Graphics::Grid::UnitList->new($u1, $u2, ...);

=head1 DESCRIPTION

You would mostly never directly use this class. Usually you could
get a UnitList object by the C<append> method.

    # at least one of $u1 and $u2 is a UnitArithmetic object
    my $ul2 = $u1->append($u2);

=head1 METHODS

=head2 elems

Number of effective values in the object.

=head2 length

This is an alias of C<elems()>.

=head2 at($idx)

=head2 slice($indices) 

Slice by indices.

=head2 string()

Returns a string representing the object.

=head2 sum()

Sum the effective unit vector in a unit object.

=head2 append($other)

Append two UnitLike objects. If both are Graphics::Grid::Unit objects,
this method would return a Graphics::Grid::Unit object. Otherwise,
it would return a Graphics::Grid::UnitList object.

=head2 insert($other, $after=$self->elems-1)

Insert another UnitLike object after index C<$after>.
Insert before first element if C<after> is a negative value.

=head1 SEE ALSO

L<Graphics::Grid>

L<Graphics::Grid::UnitLike>

=head1 AUTHOR

Stephan Loyd <sloyd@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2018-2023 by Stephan Loyd.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
