#!/usr/bin/env perl

# Return random jazz progressions

# Unfortunately, a number of chords (e.g. "6sus4") are not handled by this code yet...

use strict;
use warnings;

use Data::Dumper::Compact qw(ddc);
use Data::Dataset::ChordProgressions qw(as_hash transpose);
use List::MoreUtils qw(first_index);
use lib map { "$ENV{HOME}/sandbox/$_/lib" } qw(MIDI-Util);
use MIDI::Util qw(setup_score midi_format);
use Music::Chord::Note;

my $n     = shift || 4;
my $note  = shift || 'C'; # Transpose chords from C to this
my $scale = shift || 'major'; # or minor
my $reps  = shift || 1;

my $octave = 4;

my %data = as_hash();

# List of jazz > scale > type chord progressions
my @pool = map { @{ $data{jazz}{$scale}{$_} } } keys %{ $data{jazz}{$scale} };

my $score = setup_score();

my $cn = Music::Chord::Note->new;

for my $i (1 .. $n) {
    my $progression = $pool[int rand @pool];
    my $named = transpose($note, $scale, $progression->[0]);
    print "$i. $named, $progression->[1]\n";
    my @chords = split /-/, $named;
    for my $j (1 .. $reps) {
        for my $chord (@chords) {
            # Find any "Chord/bass-note" chords
            my @slash = split /\//, $chord;
            # Get the notes of the chord
            my @notes = $cn->chord_with_octave($slash[0], $octave);
            # If there is a bass note...
            if ($slash[1]) {
                my $bass = $slash[1];
                my $o = $octave;
                # Does the bass note exist in the chord?
                my $idx = first_index { /^$bass/ } @notes;
                # If so, set the octave and splice-out the note
                if ($idx >= 0) {
                    ($o = $notes[$idx]) =~ s/^[A-G][#b]?(\d+)$/$1/;
                    $o--;
                    splice @notes, $idx, 1;
                }
                # Prepend the bass note and octave to the chord notes
                unshift @notes, $bass . $o;
            }
            @notes = midi_format(@notes);
#            print "\t", ddc(\@notes);
            $score->n('wn', @notes);
        }
    }
}

$score->write_score("$0.mid");
