import std/[logging, options, os, strutils] import zero_functional import ./[ast, notation] type FormatContext = ref object chart: ChordChart currentKey: Key sourceKey: Key currentSection: ChordChartNode numberChart: bool transposeSteps: int const DEFAULT_STYLESHEET* = """ """ const LARGE_PRINT_STYLESHEET* = """ """ func format(ctx: FormatContext, chord: ChordChartChord, useNumber = false): string = ##if not useNumber and chord.original.isSome: return chord.original.get result = "" if useNumber: result &= $chord.root else: result &= $ctx.currentKey.spellPitch(chord.root) result &= "" if chord.flavor.isSome: result &= "" & chord.flavor.get & "" if chord.bass.isSome: result &= "/" if useNumber: result &= $chord.bass.get else: result &= $ctx.currentKey.spellPitch(chord.bass.get) result &= "" func makeSongOrder(songOrder, indent: string): string = result = indent & "
\p" & indent & "

Song Order

\p" & indent & " \p" & indent & "
\p" proc toHtml(ctx: var FormatContext, node: ChordChartNode, indent: string): string = result = "" case node.kind of ccnkSection: ctx.currentSection = node result &= indent & "
\p" & indent & " " & "

" & node.sectionName if ctx.currentSection.remainingSectionLine.isSome: result &= "" & ctx.currentSection.remainingSectionLine.get & "" result &= "

\p" var contents = newSeq[string]() for contentNode in node.sectionContents: contents.add(ctx.toHtml(contentNode, indent & " ")) result &= contents.join("\p") result &= indent & "
" ctx.currentSection = EMPTY_CHORD_CHART_NODE of ccnkLine: result &= indent & "
\p" for linePart in node.line: result &= ctx.toHtml(linePart, indent & " ") result &= indent & "
" of ccnkWord: result &= "" if node.chord.isSome: result &= "" & ctx.format(node.chord.get, ctx.numberChart) & "" result &= "" if node.word.isSome: result &= node.word.get else: result &= " " result &= "" result &= "" of ccnkNote: result &= indent & "
" & node.note & "
" of ccnkColBreak: result &= "
" of ccnkPageBreak: result &= "
" of ccnkTransposeKey: ctx.currentKey = ctx.currentKey.transpose(node.transposeSteps) result &= indent & "

Key Change: " & $ctx.currentKey & "

" of ccnkRedefineKey: let oldKey = ctx.currentKey ctx.sourceKey = node.newKey ctx.currentKey = ctx.sourceKey.transpose(ctx.transposeSteps) if oldKey != ctx.currentKey: let headingVal = indent & "

Key Change: " & $ctx.currentKey & "

" if ctx.currentSection.kind == ccnkNone: result &= headingVal else: result &= "
" & headingVal & "
" of ccnkNone: discard proc toHtml*( cc: ChordChart, transpose = 0, numberChart = false, stylesheets = @[DEFAULT_STYLESHEET], scripts: seq[string] = @[]): string = var ctx = FormatContext( chart: cc, currentKey: cc.metadata.key.transpose(transpose), sourceKey: cc.metadata.key, currentSection: EMPTY_CHORD_CHART_NODE, numberChart: numberChart, transposeSteps: transpose) debug "Formatting:\p\tsource key: $#\p\ttransposing: $#\p\ttarget key: $#" % [$ctx.sourceKey, $transpose, $ctx.currentKey] result = """ """ & cc.metadata.title & " — " & $ctx.currentKey & "\p" for ss in stylesheets: if ss.startsWith("" else: warn "cannot read stylesheet file '" & ss & "'" for sc in scripts: if sc.startsWith("" else: warn "cannot read script file '" & sc & "'" result &= " \p" var bodyClasses = newSeq[string]() if numberChart: bodyClasses.add("number-chart") if cc.metadata.contains("columns") and cc.metadata["columns"] == "1": bodyClasses.add("one-column") else: bodyClasses.add("two-column") result &= " " var indent = " " # Title result &= indent & "

" & cc.metadata.title & "

\p" var metadataPieces = @["Key: " & $ctx.currentKey] if cc.metadata.contains("time signature"): metadataPieces.add(cc.metadata["time signature"]) if cc.metadata.contains("bpm"): metadataPieces.add(cc.metadata["bpm"] & "bpm") if cc.metadata.contains("tempo"): metadataPieces.add(cc.metadata["tempo"]) result &= indent & "

" & metadataPieces.join(" | ") & "

\p" if cc.metadata.contains("artist"): result &= indent & "
" & cc.metadata["artist"] & "
\p" result &= "
" result &= join(cc.nodes --> map(ctx.toHtml(it, indent & " ")), "\p") if cc.metadata.contains("song order"): result &= makeSongOrder(cc.metadata["song order"], indent) result &= "
" result &= " \p"