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

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

CHORD_INTERVALS = {
    'Major':    [0,4,7],
    'Minor':    [0,3,7],
    '7th':      [0,4,7,10],
    'maj7':     [0,4,7,11],
    'min7':     [0,3,7,10],
    'dim':      [0,3,6],
    'aug':      [0,4,8],
    'sus2':     [0,2,7],
    'sus4':     [0,5,7],
}

# guitar chord shapes: [string6, string5, string4, string3, string2, string1] (fret, -1=muted, 0=open)
GUITAR_CHORDS = {
    'C_Major':   [-1,3,2,0,1,0],
    'D_Major':   [-1,-1,0,2,3,2],
    'E_Major':   [0,2,2,1,0,0],
    'G_Major':   [3,2,0,0,0,3],
    'A_Major':   [-1,0,2,2,2,0],
    'F_Major':   [1,1,2,3,3,1],
    'C_Minor':   [-1,3,1,0,1,-1],
    'A_Minor':   [-1,0,2,2,1,0],
    'E_Minor':   [0,2,2,0,0,0],
    'D_Minor':   [-1,-1,0,2,3,1],
}

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='Chord Finder')
        self.set_default_size(560, 480)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        vbox.set_margin_top(12); vbox.set_margin_bottom(12)
        vbox.set_margin_start(12); vbox.set_margin_end(12)
        self.add(vbox)
        hb = Gtk.Box(spacing=10)
        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)
        hb.pack_start(self.root_sel, False, False, 0)
        hb.add(Gtk.Label(label='Type:'))
        self.type_sel = Gtk.ComboBoxText()
        for t in CHORD_INTERVALS: self.type_sel.append_text(t)
        self.type_sel.set_active(0)
        self.type_sel.connect('changed', self.update)
        hb.pack_start(self.type_sel, False, False, 0)
        vbox.pack_start(hb, False, False, 0)
        self.chord_name_lbl = Gtk.Label()
        self.chord_name_lbl.override_font(Pango.FontDescription('Bold 24'))
        vbox.pack_start(self.chord_name_lbl, False, False, 0)
        self.notes_lbl = Gtk.Label()
        vbox.pack_start(self.notes_lbl, False, False, 0)
        self.da = Gtk.DrawingArea(); self.da.set_size_request(400, 260)
        self.da.set_halign(Gtk.Align.CENTER)
        self.da.connect('draw', self.draw_fretboard)
        vbox.pack_start(self.da, True, True, 0)
        self.chord_shape = None
        self.update()
    def update(self, *_):
        root_idx = self.root_sel.get_active()
        root = NOTES[root_idx]
        chord_type = self.type_sel.get_active_text()
        intervals = CHORD_INTERVALS[chord_type]
        chord_notes = [NOTES[(root_idx + i) % 12] for i in intervals]
        self.chord_name_lbl.set_text(f'{root} {chord_type}')
        self.notes_lbl.set_text('Notes: ' + '  –  '.join(chord_notes))
        key = f'{root}_{chord_type}'
        self.chord_shape = GUITAR_CHORDS.get(key, None)
        self.da.queue_draw()
    def draw_fretboard(self, w, cr):
        alloc = w.get_allocation()
        W, H = alloc.width, alloc.height
        cr.set_source_rgb(0.96, 0.93, 0.85); cr.paint()
        strings = 6; frets = 5
        margin_l, margin_r, margin_t, margin_b = 50, 30, 40, 20
        fw = W - margin_l - margin_r
        fh = H - margin_t - margin_b
        string_gap = fw / (strings - 1)
        fret_gap = fh / frets
        # nut
        cr.set_source_rgb(0.2,0.1,0.05); cr.set_line_width(4)
        cr.move_to(margin_l, margin_t); cr.line_to(W-margin_r, margin_t); cr.stroke()
        # fret lines
        cr.set_line_width(1.5); cr.set_source_rgb(0.4,0.3,0.2)
        for f in range(1, frets+1):
            y = margin_t + f * fret_gap
            cr.move_to(margin_l, y); cr.line_to(W-margin_r, y); cr.stroke()
        # strings
        cr.set_line_width(1.5)
        for s in range(strings):
            x = margin_l + s * string_gap
            thick = 0.1 + s * 0.1
            cr.set_line_width(thick * 2 + 0.5)
            cr.set_source_rgb(0.5, 0.5, 0.5)
            cr.move_to(x, margin_t); cr.line_to(x, H-margin_b); cr.stroke()
        # string labels
        cr.set_source_rgb(0.2,0.2,0.2); cr.set_font_size(11)
        for s, name in enumerate(['E','A','D','G','B','e']):
            x = margin_l + s * string_gap
            cr.move_to(x-5, margin_t - 10); cr.show_text(name)
        if self.chord_shape:
            for s, fret in enumerate(self.chord_shape):
                x = margin_l + s * string_gap
                if fret == -1:
                    cr.set_source_rgb(0.8,0.1,0.1); cr.set_font_size(14)
                    cr.move_to(x-5, margin_t-2); cr.show_text('×')
                elif fret == 0:
                    cr.set_source_rgb(0.1,0.6,0.1); cr.set_line_width(1.5)
                    cr.arc(x, margin_t-8, 6, 0, math.pi*2); cr.stroke()
                else:
                    y = margin_t + (fret - 0.5) * fret_gap
                    cr.set_source_rgb(0.1,0.1,0.7)
                    cr.arc(x, y, 9, 0, math.pi*2); cr.fill()
                    cr.set_source_rgb(1,1,1); cr.set_font_size(11)
                    cr.move_to(x-4, y+4); cr.show_text(str(fret))
        else:
            cr.set_source_rgb(0.5,0.5,0.5); cr.set_font_size(13)
            cr.move_to(W//2-80, H//2)
            cr.show_text('No diagram available for this chord')

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