package Graphics::Grid::UnitLike;

# ABSTRACT: Role for unit-like classes in Graphics::Grid

use Graphics::Grid::Role;

our $VERSION = '0.001'; # VERSION

use List::AllUtils qw(reduce);
use Type::Params;
use Types::Standard qw(ArrayRef Maybe Num);

use Graphics::Grid::Types qw(UnitLike UnitName);

with qw(MooseX::Clone);

use overload
  '+'      => 'plus',
  '-'      => 'minus',
  '*'      => 'multiply',
  fallback => 1;


requires 'elems';

method length() { $self->elems; }

requires 'is_null_unit';


requires 'at';



requires 'slice';

requires 'string';

method sum() {
    return reduce { $a + $b } map { $self->at($_) } ( 0 .. $self->elems - 1 );
}

method append( UnitLike $other) {
    require Graphics::Grid::UnitList;
    return Graphics::Grid::UnitList->new( $self, $other );
}

method insert( UnitLike $other, $after = $self->elems - 1 ) {
    if ( $after < 0 ) {
        return $other->append($self);
    }
    elsif ( $after >= $self->elems - 1 ) {
        return $self->append($other);
    }
    else {
        my $u1 = $self->slice( [ 0 .. $after ] );
        my $u2 = $self->slice( [ $after + 1 .. $self->elems - 1 ] );
        return $u1->append($other)->append($u2);
    }
}

sub _make_operation { ... }

method plus( Maybe [UnitLike] $other, $swap = undef ) {
    return $self->clone unless ( defined $other );
    return $self->_make_operation( '+', $other, $swap );
}

method minus( Maybe [UnitLike] $other, $swap = undef ) {
    return $self->clone unless ( defined $other );
    return $self->_make_operation( '-', $other, $swap );
}

method multiply( ( ArrayRef [Num] | Num ) $other, $swap = undef ) {
    return $self->_make_operation( '*', $other, $swap );
}


requires 'transform_to_cm';


classmethod is_absolute_unit ($unit_name) {
    # cannot put the unit in function signature, as we need coercion.
    state $check = Type::Params::compile(UnitName);
    my ($unit_name_coerced) = $check->($unit_name);

    state $absolute_units = { map { $_ => 1 } qw(cm inches mm points picas) };
    return exists( $absolute_units->{$unit_name_coerced} );
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Graphics::Grid::UnitLike - Role for unit-like classes in Graphics::Grid

=head1 VERSION

version 0.001

=head1 DESCRIPTION

This role represents something that can be used as unit-value.

=head1 CLASS METHODS

=head2 is_absolute_unit($unit_name)

This is a class method. It tells if the given unit name is absolute or not.

    my $is_absolute = Graphics::Grid::UnitLike->is_absolute_unit('cm');

=head1 METHODS

=head2 elems

Number of effective values in the object.

=head2 length

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

=head2 at

=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.

=head2 transform_to_cm($grid, $idx, $gp, $length_cm)

=head1 SEE ALSO

L<Graphics::Grid>

L<Graphics::Grid::Unit>

L<Graphics::Grid::UnitArithmetic>

L<Graphics::Grid::UnitList>

=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
