#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

NOTES = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']

def scale(root, pattern):
    idx = NOTES.index(root)
    return [NOTES[(idx+s)%12] for s in pattern]

SCALES = {
    'Major':         [0,2,4,5,7,9,11],
    'Natural Minor': [0,2,3,5,7,8,10],
    'Pentatonic Maj':[0,2,4,7,9],
    'Pentatonic Min':[0,3,5,7,10],
    'Blues':         [0,3,5,6,7,10],
    'Dorian':        [0,2,3,5,7,9,10],
    'Phrygian':      [0,1,3,5,7,8,10],
}

INTERVALS = [
    ('Unison','P1',0),('Minor 2nd','m2',1),('Major 2nd','M2',2),
    ('Minor 3rd','m3',3),('Major 3rd','M3',4),('Perfect 4th','P4',5),
    ('Tritone','TT',6),('Perfect 5th','P5',7),('Minor 6th','m6',8),
    ('Major 6th','M6',9),('Minor 7th','m7',10),('Major 7th','M7',11),
    ('Octave','P8',12),
]

CHORDS = {
    'Major':'1 - 3 - 5','Minor':'1 - ♭3 - 5','Dominant 7':'1 - 3 - 5 - ♭7',
    'Major 7':'1 - 3 - 5 - 7','Minor 7':'1 - ♭3 - 5 - ♭7',
    'Diminished':'1 - ♭3 - ♭5','Augmented':'1 - 3 - #5',
    'Sus2':'1 - 2 - 5','Sus4':'1 - 4 - 5','Add9':'1 - 3 - 5 - 9',
    'Dim7':'1 - ♭3 - ♭5 - ♭♭7','Half-Dim7':'1 - ♭3 - ♭5 - ♭7',
}

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='Music Theory Reference')
        self.set_default_size(640, 480)
        nb = Gtk.Notebook(); self.add(nb)
        # Scales tab
        p1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        p1.set_margin_top(10); p1.set_margin_bottom(10)
        p1.set_margin_start(10); p1.set_margin_end(10)
        hb = Gtk.Box(spacing=8)
        hb.add(Gtk.Label(label='Root:'))
        self.root_sel = Gtk.ComboBoxText()
        for n in NOTES: self.root_sel.append_text(n)
        self.root_sel.set_active(0)
        self.root_sel.connect('changed', self.update_scales)
        hb.pack_start(self.root_sel, False, False, 0)
        p1.pack_start(hb, False, False, 0)
        sw = Gtk.ScrolledWindow()
        store = Gtk.ListStore(str, str)
        tv = Gtk.TreeView(model=store)
        for i, title in enumerate(['Scale', 'Notes']):
            tv.append_column(Gtk.TreeViewColumn(title, Gtk.CellRendererText(), text=i))
        self.scale_store = store; self.update_scales()
        sw.add(tv); p1.pack_start(sw, True, True, 0)
        nb.append_page(p1, Gtk.Label(label='Scales'))
        # Intervals tab
        p2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        p2.set_margin_top(10); p2.set_margin_bottom(10)
        p2.set_margin_start(10); p2.set_margin_end(10)
        sw2 = Gtk.ScrolledWindow()
        st2 = Gtk.ListStore(str, str, int)
        tv2 = Gtk.TreeView(model=st2)
        for i, title in enumerate(['Name','Symbol','Semitones']):
            tv2.append_column(Gtk.TreeViewColumn(title, Gtk.CellRendererText(), text=i))
        for name, sym, semi in INTERVALS: st2.append([name, sym, semi])
        sw2.add(tv2); p2.pack_start(sw2, True, True, 0)
        nb.append_page(p2, Gtk.Label(label='Intervals'))
        # Chords tab
        p3 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        p3.set_margin_top(10); p3.set_margin_bottom(10)
        p3.set_margin_start(10); p3.set_margin_end(10)
        sw3 = Gtk.ScrolledWindow()
        st3 = Gtk.ListStore(str, str)
        tv3 = Gtk.TreeView(model=st3)
        for i, title in enumerate(['Chord Type', 'Formula']):
            tv3.append_column(Gtk.TreeViewColumn(title, Gtk.CellRendererText(), text=i))
        for k, v in CHORDS.items(): st3.append([k, v])
        sw3.add(tv3); p3.pack_start(sw3, True, True, 0)
        nb.append_page(p3, Gtk.Label(label='Chord Formulas'))
    def update_scales(self, *_):
        self.scale_store.clear()
        root = NOTES[self.root_sel.get_active()]
        for name, pattern in SCALES.items():
            notes = scale(root, pattern)
            self.scale_store.append([name, '  '.join(notes)])

win = App()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
