package Music::ScaleNote;
our $AUTHORITY = 'cpan:GENE';

# ABSTRACT: Position of notes in a scale

our $VERSION = '0.01';

use Carp;
use Moo;
use strictures 2;
use namespace::clean;

use List::Util qw( first );
use Music::Note;
use Music::Scales;


has scale_note => (
    is      => 'ro',
    default => sub { 'C' },
);


has scale_name => (
    is => 'ro',
);


sub get_offset {
    my ( $self, %args ) = @_;

    croak 'note_name, note_format or offset not provided'
        unless $args{note_name} || $args{note_format} || $args{offset};

    my $rev;  # Going in reverse?

    my $note = Music::Note->new( $args{note_name}, $args{note_format} );
#warn(__PACKAGE__,' L',__LINE__,". MARK: $args{note_name} - ",$note->format('ISO'),"\n");

    my @scale = get_scale_notes( $self->scale_note, $self->scale_name );
#warn(__PACKAGE__,' L',__LINE__,". MARK: @scale",,"\n");
    if ( $args{offset} < 0 ) {
        $rev++;
        $args{offset} = abs $args{offset};
        @scale  = reverse @scale;
    }

    my $posn = first { $scale[$_] eq $note->format('isobase') } 0 .. $#scale;
#warn(__PACKAGE__,' L',__LINE__,". MARK: $args{note_name}=$posn\n");

    $args{offset} += $posn;

    my $octave = $note->octave;
    my $factor = int( $args{offset} / @scale );

    if ( $rev ) {
        $octave -= $factor;
    }
    else {
        $octave += $factor;
    }

    $note = Music::Note->new( $scale[ $args{offset} % @scale ] . $octave, 'ISO' );
#warn(__PACKAGE__,' L',__LINE__,". MARK: $args{offset} => ",$note->format('ISO') .' - '.$note->format($args{note_format}),"\n");

    return $note;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Music::ScaleNote - Position of notes in a scale

=head1 VERSION

version 0.01

=head1 SYNOPSIS

  use Music::ScaleNote;
  my $msn = Music::ScaleNote->new(
    scale_note => 'C',
    scale_name => 'pminor',
  );
  my $note = $msn->get_offset(
    note_name   => 'C4',
    note_format => 'ISO',
    offset      => 1,
  );
  print $note->format('ISO'), "\n"; # D#4

=head1 DESCRIPTION

A C<Music::ScaleNote> object manipulates the position of notes in a given scale.

=head1 ATTRIBUTES

=head2 scale_note

This is the name of the note that starts the given B<scale_name>.

Default: C

=head2 scale_name

L<Music::Scales/SCALES>

Default: major

=head1 METHODS

=head2 new()

  $msn = Music::ScaleNote->new(%arguments);

Create a new C<Music::ScaleNote> object.

=head2 get_offset()

  $note = $msn->get_offset(%arguments);

Return a new L<Music::Note> object based on the given B<note_name>,
B<note_format> and L<offset>.

=head1 SEE ALSO

L<Moo>

L<List::Util>

L<Music::Note>

L<Music::Scales>

=head1 AUTHOR

Gene Boggs <gene@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2018 by Gene Boggs.

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
