use v6;
use Test;
use ABC::Header;
use ABC::Tune;
use ABC::Grammar;
use ABC::Actions;
use ABC::Note;
use ABC::Stem;
use ABC::Rest;
use ABC::Tuplet;
use ABC::BrokenRhythm;
use ABC::Chord;

{
    my $match = ABC::Grammar.parse('F#', :rule<chord>, :actions(ABC::Actions.new));
    ok $match, 'chord recognized';
    isa-ok $match.ast, ABC::Chord, '$match.ast is an ABC::Chord';
    is $match.ast.main-note, "F", "Pitch F";
    is $match.ast.main-accidental, "#", "...sharp";
}

{
    my $match = ABC::Grammar.parse('Bbmin/G#', :rule<chord>, :actions(ABC::Actions.new));
    ok $match, 'chord recognized';
    isa-ok $match.ast, ABC::Chord, '$match.ast is an ABC::Chord';
    is $match.ast.main-note, "B", "Pitch B";
    is $match.ast.main-accidental, "b", "...flat";
    is $match.ast.main-type, "min", "...min";
    is $match.ast.bass-note, "G", "over G";
    is $match.ast.bass-accidental, "#", "...#";
}

{
    my $match = ABC::Grammar.parse('"F#"', :rule<chord_or_text>, :actions(ABC::Actions.new));
    ok $match, 'chord_or_text recognized';
    isa-ok $match.ast[0], ABC::Chord, '$match.ast[0] is an ABC::Chord';
    is $match.ast[0].main-note, "F", "Pitch F";
    is $match.ast[0].main-accidental, "#", "...sharp";
}

{
    my $match = ABC::Grammar.parse('{gf}', :rule<grace_notes>, :actions(ABC::Actions.new));
    ok $match, 'grace_notes recognized';
    isa-ok $match.ast, ABC::GraceNotes, '$match.ast is an ABC::GraceNotes';
    nok $match.ast.acciaccatura, "It's not an acciaccatura";
    is $match.ast.notes[0].pitch, "g", "Pitch g found";
    is $match.ast.notes[1].pitch, "f", "Pitch g found";
}

{
    my $match = ABC::Grammar.parse('"F#"', :rule<element>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, Pair, '$match.ast is a Pair';
    is $match.ast.key, "chord_or_text", '$match.ast.key is "chord_or_text"';
    is $match.ast.value[0].main-note, "F", "Pitch F";
    is $match.ast.value[0].main-accidental, "#", "...sharp";
}

{
    my $match = ABC::Grammar.parse('"^Bb whistle"', :rule<element>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, Pair, '$match.ast is a Pair';
    is $match.ast.key, "chord_or_text", '$match.ast.key is "chord_or_text"';
    isa-ok $match.ast.value[0], Str, "And it's text";
    is $match.ast.value[0], "^Bb whistle", '$match.ast.value[0] is ^Bb whistle';
}

{
    my $match = ABC::Grammar.parse("e3", :rule<mnote>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, ABC::Note, '$match.ast is an ABC::Note';
    is $match.ast.pitch, "e", "Pitch e";
    is $match.ast.ticks, 3, "Duration 3 ticks";
}

{
    my $match = ABC::Grammar.parse("e", :rule<mnote>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, ABC::Note, '$match.ast is an ABC::Note';
    is $match.ast.pitch, "e", "Pitch e";
    is $match.ast.ticks, 1, "Duration 1 ticks";
}

{
    my $match = ABC::Grammar.parse("^e,/", :rule<mnote>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, ABC::Note, '$match.ast is an ABC::Note';
    is $match.ast.pitch, "^e,", "Pitch ^e,";
    is $match.ast.ticks, 1/2, "Duration 1/2 ticks";
}

{
    my $match = ABC::Grammar.parse("[a2bc]3", :rule<stem>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, ABC::Stem, '$match.ast is an ABC::Stem';
    is $match.ast.notes[0], "a2", "Pitch 1 a";
    is $match.ast.notes[1], "b", "Pitch 2 b";
    is $match.ast.notes[2], "c", "Pitch 3 c";
    is $match.ast.ticks, 6, "Duration 6 ticks";
    nok $match.ast.is-tie, "Not tied";
}

{
    my $match = ABC::Grammar.parse("[a2bc]/-", :rule<stem>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, ABC::Stem, '$match.ast is an ABC::Stem';
    is $match.ast.notes[0], "a2", "Pitch 1 a";
    is $match.ast.notes[1], "b", "Pitch 2 b";
    is $match.ast.notes[2], "c", "Pitch 3 c";
    is $match.ast.ticks, 1, "Duration 1 tick";
    ok $match.ast.is-tie, "Tied";
}

{
    my $match = ABC::Grammar.parse("z/", :rule<rest>, :actions(ABC::Actions.new));
    ok $match, 'rest recognized';
    isa-ok $match.ast, ABC::Rest, '$match.ast is an ABC::Rest';
    is $match.ast.type, "z", "Rest is z";
    is $match.ast.ticks, 1/2, "Duration 1/2 ticks";
}

{
    my $match = ABC::Grammar.parse("F3/2", :rule<mnote>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, ABC::Note, '$match.ast is an ABC::Note';
    is $match.ast.pitch, "F", "Pitch F";
    is $match.ast.ticks, 3/2, "Duration 3/2 ticks";
}

{
    my $match = ABC::Grammar.parse("F2/3", :rule<mnote>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, ABC::Note, '$match.ast is an ABC::Note';
    is $match.ast.pitch, "F", "Pitch F";
    is $match.ast.ticks, 2/3, "Duration 2/3 ticks";
}

{
    my $match = ABC::Grammar.parse("(3abc", :rule<tuplet>, :actions(ABC::Actions.new));
    ok $match, 'tuplet recognized';
    isa-ok $match.ast, ABC::Tuplet, '$match.ast is an ABC::Tuplet';
    is $match.ast.tuple, "3", "It's a triplet";
    is $match.ast.ticks, 2, "Duration 2 ticks";
    is +$match.ast.notes, 3, "Three internal note";
    ok $match.ast.notes[0] ~~ ABC::Stem | ABC::Note, "First internal note is of the correct type";
    is $match.ast.notes, "a b c", "Notes are correct";
}

{
    my $match = ABC::Grammar.parse("a>~b", :rule<broken_rhythm>, :actions(ABC::Actions.new));
    ok $match, 'broken rhythm recognized';
    isa-ok $match.ast, ABC::BrokenRhythm, '$match.ast is an ABC::BrokenRhythm';
    is $match.ast.ticks, 2, "total duration is two ticks";
    isa-ok $match.ast.effective-stem1, ABC::Note, "effective-stem1 is a note";
    is $match.ast.effective-stem1.pitch, "a", "first pitch is a";
    is $match.ast.effective-stem1.ticks, 1.5, "first duration is 1 + 1/2";
    isa-ok $match.ast.effective-stem2, ABC::Note, "effective-stem2 is a note";
    is $match.ast.effective-stem2.pitch, "b", "first pitch is a";
    is $match.ast.effective-stem2.ticks, .5, "second duration is 1/2";
}

{
    my $match = ABC::Grammar.parse("a<<<b", :rule<broken_rhythm>, :actions(ABC::Actions.new));
    ok $match, 'broken rhythm recognized';
    isa-ok $match.ast, ABC::BrokenRhythm, '$match.ast is an ABC::BrokenRhythm';
    is $match.ast.ticks, 2, "total duration is two ticks";
    isa-ok $match.ast.effective-stem1, ABC::Note, "effective-stem1 is a note";
    is $match.ast.effective-stem1.pitch, "a", "first pitch is a";
    is $match.ast.effective-stem1.ticks, 1/8, "first duration is 1/8";
    isa-ok $match.ast.effective-stem2, ABC::Note, "effective-stem2 is a note";
    is $match.ast.effective-stem2.pitch, "b", "first pitch is a";
    is $match.ast.effective-stem2.ticks, 15/8, "second duration is 1 + 7/8";
}

{
    my $match = ABC::Grammar.parse("[K:F]", :rule<element>, :actions(ABC::Actions.new));
    ok $match, 'inline field recognized';
    # isa-ok $match.ast, ABC::BrokenRhythm, '$match.ast is an ABC::BrokenRhythm';
    is $match<inline_field><alpha>, "K", "field type is K";
    is $match<inline_field><value>, "F", "field value is K";
}

{
    my $match = ABC::Grammar.parse("+fff+", :rule<long_gracing>, :actions(ABC::Actions.new));
    ok $match, 'long gracing recognized';
    isa-ok $match.ast, Str, '$match.ast is a Str';
    is $match.ast, "fff", "gracing is fff";
}

{
    my $match = ABC::Grammar.parse("+fff+", :rule<gracing>, :actions(ABC::Actions.new));
    ok $match, 'long gracing recognized';
    isa-ok $match.ast, Str, '$match.ast is a Str';
    is $match.ast, "fff", "gracing is fff";
}

{
    my $match = ABC::Grammar.parse("~", :rule<gracing>, :actions(ABC::Actions.new));
    ok $match, 'gracing recognized';
    isa-ok $match.ast, Str, '$match.ast is a Str';
    is $match.ast, "~", "gracing is ~";
}

{
    my $match = ABC::Grammar.parse("+fff+", :rule<element>, :actions(ABC::Actions.new));
    ok $match, 'long gracing recognized';
    is $match.ast.key, "gracing", '$match.ast.key is gracing';
    isa-ok $match.ast.value, Str, '$match.ast.value is a Str';
    is $match.ast.value, "fff", "gracing is fff";
}

{
    my $music = q«X:64
T:Cuckold Come Out o' the Amrey
S:Northumbrian Minstrelsy
M:4/4
L:1/8
K:D
»;
    my $match = ABC::Grammar.parse($music, :rule<header>, :actions(ABC::Actions.new));
    ok $match, 'tune recognized';
    isa-ok $match.ast, ABC::Header, '$match.ast is an ABC::Header';
    is $match.ast.get("T").elems, 1, "One T field found";
    is $match.ast.get("T")[0].value, "Cuckold Come Out o' the Amrey", "And it's correct";
    ok $match.ast.is-valid, "ABC::Header is valid";
}

{
    my $match = ABC::Grammar.parse("e3", :rule<element>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    isa-ok $match.ast, Pair, '$match.ast is a Pair';
    is $match.ast.key, "stem", "Stem found";
    isa-ok $match.ast.value, ABC::Note, "Value is note";
}

{
    my $match = ABC::Grammar.parse("G2g gdc|", :rule<bar>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    is $match.ast.elems, 7, '$match.ast has seven elements';
    is $match.ast[3].key, "stem", "Fourth is stem";
    is $match.ast[*-1].key, "barline", "Last is barline";
}

{
    my $match = ABC::Grammar.parse("G2g gdc", :rule<bar>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
    is $match.ast.elems, 6, '$match.ast has six elements';
    is $match.ast[3].key, "stem", "Fourth is stem";
    is $match.ast[*-1].key, "stem", "Last is stem";
}

{
    my $music = q«BAB G2G|G2g gdc|1BAB G2G|2=F2f fcA|
BAB G2G|G2g gdB|c2a B2g|A2=f fcA:|
»;

    my $match = ABC::Grammar.parse($music, :rule<music>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
#     say $match.ast.perl;
    is $match.ast.elems, 59, '$match.ast has 59 elements';
    # say $match.ast.elems;
    # say $match.ast[28].WHAT;
    # say $match.ast[28].perl;
    is $match.ast[22].key, "nth_repeat", "21st is nth_repeat";
    isa-ok $match.ast[22].value, Set, "21st value is a Set";
    ok $match.ast[22].value ~~ (set 2), "21st is '2'";
    is $match.ast[30].key, "endline", "29th is endline";
    is $match.ast[*-1].key, "endline", "Last is endline";
}

{
    my $music = q«BAB G2G|G2g gdc|BAB G2G|=F2f fcA|
BAB G2G|G2g gdB|c2a B2g|A2=f fcA:|
»;

    my $match = ABC::Grammar.parse($music, :rule<music>, :actions(ABC::Actions.new));
    ok $match, 'element recognized';
#     say $match.ast.perl;
    is $match.ast.elems, 57, '$match.ast has 57 elements';
    # say $match.ast.elems;
    # say $match.ast[28].WHAT;
    # say $match.ast[28].perl;
    is $match.ast[28].key, "endline", "29th is endline";
    is $match.ast[*-1].key, "endline", "Last is endline";
}

{
    my $music = q«X:044
T:Elsie Marley
B:Robin Williamson, "Fiddle Tunes" (New York 1976)
N:"printed by Robert Petrie in 1796 and is
N:"described by him as a 'bumpkin'."
Z:Nigel Gatherer
M:6/8
L:1/8
K:G
BAB G2G|G2g gdc|BAB G2G|=F2f fcA|
BAB G2G|G2g gdB|c2a B2g|A2=f fcA:|
»;

    my $match = ABC::Grammar.parse($music, :rule<tune>, :actions(ABC::Actions.new));
    ok $match, 'tune recognized';
    isa-ok $match.ast, ABC::Tune, 'and ABC::Tune created';
    ok $match.ast.header.is-valid, "ABC::Tune's header is valid";
    is $match.ast.music.elems, 57, '$match.ast.music has 57 elements';
}

{
    my $match = ABC::Grammar.parse(slurp("samples.abc"), :rule<tune_file>, :actions(ABC::Actions.new));
    ok $match, 'samples.abc is a valid tune file';
    # say $match.ast.perl;
    is @( $match<tune> ).elems, 3, "Three tunes were found";
    # is @( $match.ast )[0].elems, 3, "Three tunes were found";
    isa-ok @( $match.ast )[0][0], ABC::Tune, "First is an ABC::Tune";
}

{
    my $music = q«X:1
T:Canon in D
C:Pachelbel
M:2/2
L:1/8
K:D
"D" DFAd "A" CEAc|"Bm" B,DFB "F#m" A,CFA|"G" B,DGB "D" A,DFA|"G" B,DGB "A" CEAc|
"D" f4 "A" e4|"Bm" d4 "F#m" c4|"G" B4 "D" A4|"G" B4 "A" c4|»;
    my $match = ABC::Grammar.parse($music, :rule<tune_file>, :actions(ABC::Actions.new));
    isa-ok $match, Match, 'Got a match';
    ok $match, 'tune_file recognized';
    
    is $match<tune>.elems, 1, 'found one tune';
    is $match<tune>[0]<music><line_of_music>.elems, 2, "with two lines of music";
}


done-testing;
