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"
result &= join(songOrder.split(",") -->
map(indent & " - " & it.strip & "
\p"), "")
result &= 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 &= ""
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 &= "
"
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"