#!/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 lib map { "$ENV{HOME}/sandbox/$_/lib" } qw(MIDI-Util);
use Data::Dataset::ChordProgressions;
use List::MoreUtils qw(first_index);
use MIDI::Util qw(setup_score midi_format);
use Music::Chord::Note;
use Music::Scales qw(get_scale_notes);

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

my $octave = 4;

my %data = Data::Dataset::ChordProgressions::as_hash();

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

my %note_map;
@note_map{ get_scale_notes('C', $scale) } = get_scale_notes($note, $scale);

my $score = setup_score();

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

for my $i (1 .. $n) {
    my $progression = $pool[int rand @pool];
    # Transpose the chords
    (my $named = $progression->[0]) =~ s/([A-G][#b]?)/$note_map{$1}/g;
    print "$i. $named, $progression->[1]\n";
    my @chords = split /-/, $named;
    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 $i = first_index { /^$bass/ } @notes;
            # If so, set the octave and splice-out the note
            if ($i >= 0) {
                ($o = $notes[$i]) =~ s/^[A-G][#b]?(\d+)$/$1/;
                $o--;
                splice @notes, $i, 1;
            }
            # Add 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");
