"""Visualization tools such as piano keys and guitar fretboards."""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/03_viz.ipynb.

# %% auto 0
__all__ = ['Instrument', 'Piano', 'Guitar']

# %% ../nbs/03_viz.ipynb 3
from fasthtml import *
from fastcore.all import *
from fasthtml.jupyter import render_ft
from IPython.display import HTML

from . import Note, Chord, Scale

render_ft()

# %% ../nbs/03_viz.ipynb 6
class Instrument:
    def __ft__(self, midi_notes: list[int] = []):
        """Base class for instrument visualizations. Override this method in subclasses."""
        raise NotImplementedError("Subclasses must implement __ft__")
    
    def visualize_note(self, note: Note):
        """Visualize a single note on the instrument."""
        return self.__ft__([note.midi])
    
    def visualize_notes(self, notes: list[Note]):
        """Visualize multiple notes on the instrument."""
        return self.__ft__([n.midi for n in notes])
    
    def visualize_chord(self, chord: Chord):
        """Visualize a chord on the instrument."""
        return self.__ft__([n.midi for n in chord.notes])
    
    def visualize_scale(self, scale: Scale, root: str = "C", octs = 2):
        """Visualize a scale on the instrument across multiple octaves."""
        return self.__ft__([n.midi for oct in range(1, octs+1) for n in scale.get_notes(root, oct=oct)])
    
    def __call__(self, highlight = None):
        """Main entry point for visualization. Handles different input types."""
        if not highlight:
            return self.__ft__([])
        elif isinstance(highlight, Note):
            return self.visualize_note(highlight)
        elif isinstance(highlight, Chord):
            return self.visualize_chord(highlight)
        elif isinstance(highlight, Scale):
            return self.visualize_scale(highlight)
        elif isinstance(highlight, list):
            return self.__ft__([n.midi for n in highlight])
        else:
            raise ValueError(f"Unsupported type: {type(highlight)}")

# %% ../nbs/03_viz.ipynb 9
class Piano(Instrument):
    def __ft__(self, midi_notes: list[int] = []):
        midi_range = range(min(midi_notes or [60]), max(midi_notes or [60]) + 1)
        octaves = range(min(midi_range) // 12, max(midi_range) // 12 + 1)
        white_keys = [(note, Note(note, oct).midi) for oct in octaves for note in ['C', 'D', 'E', 'F', 'G', 'A', 'B']]
        black_keys = []
        for oct in octaves:
            oct_offset = (oct - min(octaves)) * 7
            black_keys.extend([
                ('C#', Note('C#', oct).midi, oct_offset),  # Between C and D
                ('D#', Note('D#', oct).midi, oct_offset + 1),  # Between D and E
                ('F#', Note('F#', oct).midi, oct_offset + 3),  # Between F and G
                ('G#', Note('G#', oct).midi, oct_offset + 4),  # Between G and A
                ('A#', Note('A#', oct).midi, oct_offset + 5),  # Between A and B
            ])
        
        css = Style("""
.piano { background: #222; padding: 20px 0; position: relative; }
.white-keys { display: flex; }
.white-key, .black-key { text-align: center; font-family: Arial; position: relative; }
.white-key { width: 40px; height: 125px; background: #fff; border: 1px solid #000; 
    color: #111; font-size: 18px; line-height: 200px; z-index: 1; }
.black-key { width: 20px; height: 80px; background: #000; color: #fff; border: 1px solid #333;
    position: absolute; z-index: 2; line-height: 100px; font-size: 14px; top: 20px; }
.highlight { background: #ff0 !important; color: #000 !important; }
""")
        white_divs = [Div(note, cls=f"white-key{' highlight' if midi in midi_notes else ''}") for note, midi in white_keys]
        black_divs = [Div(note, cls=f"black-key{' highlight' if midi in midi_notes else ''}", style=f"left:{(idx + 1) * 40 - 11}px") 
                     for note, midi, idx in black_keys]
        return HTML(css + Div(Div(*white_divs, cls="white-keys"), *black_divs, cls="piano", style=f"width:{len(white_keys)*40}px"))

# %% ../nbs/03_viz.ipynb 23
class Guitar(Instrument):
    def __ft__(self, midi_notes: list[int] = []):
        num_frets = 22
        string_names = ['E', 'B', 'G', 'D', 'A', 'E']
        standard_tuning = [64, 59, 55, 50, 45, 40]

        # Fret numbers row
        fret_nums = [Div(str(fret), cls="guitar-fret-num") for fret in range(num_frets+1)]
        fret_nums = Div(Div("", cls="guitar-string-name"), *fret_nums, cls="guitar-fret-nums")

        # Fretboard grid
        rows = []
        for name, open_midi in zip(string_names, standard_tuning):
            cells = []
            for fret in range(num_frets+1):
                midi = open_midi + fret
                if midi in midi_notes:
                    cells.append(Div(str(Note.from_midi(midi)), cls="guitar-note"))
                else:
                    cells.append(Div("", cls="guitar-cell"))
            rows.append(Div(Div(name, cls="guitar-string-name"), *cells, cls="guitar-row"))

        # CSS
        css = Style("""
.guitar-fretboard { background: #c49e60; border-radius: 8px; padding: 8px; display: inline-block; }
.guitar-fret-nums { display: flex; font-size: 12px; color: #333; margin-bottom: 2px; }
.guitar-fret-num { width: 32px; text-align: center; font-weight: bold; border-right: 2.5px solid #888; }
.guitar-dot { height: 6px; text-align: center; color: #444; font-size: 10px; }
.guitar-row { display: flex; align-items: center; position: relative; }
.guitar-string-name { width: 24px; text-align: right; margin-right: 0px; font-weight: bold; color: #444; }
.guitar-cell, .guitar-note { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-right: 2.5px solid #888; position: relative; }
.guitar-cell { border-bottom: 1.2px solid #bbb; background: none; }
.guitar-note { background: rgba(255, 255, 0, 0.7); color: #222; 
""")
        board = Div(
            fret_nums,
            *rows,
            cls="guitar-fretboard"
        )
        return HTML(css + board)
    
    def visualize_chord(self, chord: Chord):
        return self.__ft__([Note(str(n), oct).midi for oct in range(1, 8) for n in list(chord)])
    
    def visualize_scale(self, scale: Scale, root: str = "C"):
        return self.__ft__([n.midi for oct in range(1, 8) for n in scale.get_notes(root, oct=oct)])
