% parsing line by line
#(define-markup-command (wrap-newline layout props text) (string?)
   "Text Zeile für Zeile parsen"
   (interpret-markup layout props
     #{ \markup { \column {
     $(let ((verse-markup-string (
        string-append "\\line { "
          (ly:regex-replace (ly:make-regex "\r?\n") text " } \\line { ")
          " \\size-box-to-box ##f ##t \"\" \"Agj\" }" )))
       (ly:parser-include-string verse-markup-string))
     }}#}
   )
)

#(define-markup-command (size-box-to-box layout props use-x use-y abox bbox)
  (boolean? boolean? markup? markup?)
  (let* ((ma (interpret-markup layout props abox))
         (mb (interpret-markup layout props bbox))
         (ax (ly:stencil-extent ma X))
         (ay (ly:stencil-extent ma Y))
         (bx (ly:stencil-extent mb X))
         (by (ly:stencil-extent mb Y))
         (halfdiffabx (* (- (interval-length bx) (interval-length ax)) 0.5)))
    (ly:stencil-translate (ly:make-stencil (ly:stencil-expr ma)
                     (if use-x
                          (if (< halfdiffabx 0)
                              (cons
                                (- (interval-bound ax DOWN) halfdiffabx)
                                (+ (interval-bound ax UP) halfdiffabx))
                              bx)
                       ax)
                     (if use-y by ay))
      (cons (if (and use-x (< halfdiffabx 0)) halfdiffabx 0) 0) )))

#(define-markup-command (size-box-to-box-left-aligned layout props use-x use-y abox bbox)
  (boolean? boolean? markup? markup?)
  (let* ((ma (interpret-markup layout props abox))
         (mb (interpret-markup layout props bbox))
         (ax (ly:stencil-extent ma X))
         (ay (ly:stencil-extent ma Y))
         (bx (ly:stencil-extent mb X))
         (by (ly:stencil-extent mb Y)))
    (ly:make-stencil (ly:stencil-expr ma)
                     (if use-x bx ax)
                     (if use-y by ay))
    ))

#(define-markup-command (size-box-to-box-style-dependent layout props use-x use-y abox bbox)
   (boolean? boolean? markup? markup?)
   (interpret-markup layout props
   (case songTextChordAlignment
     ((center) (make-size-box-to-box-markup use-x use-y abox bbox))
     ((left) (make-size-box-to-box-left-aligned-markup use-x use-y abox bbox)))))

#(define-markup-command (chord-alignment-style-dependent layout props chord-with-text) (markup?)
   (interpret-markup layout props
   (case songTextChordAlignment
     ((center) (make-center-align-markup chord-with-text))
     ((left) (make-left-align-markup chord-with-text)))))

% Text über Text mittig darstellen
#(define-markup-command (textup layout props text uptext) (markup? markup?)
   "Markup über Text mittig darstellen."
   (let ((verselayout (chain-assoc-get 'verselayout props generalLayout)))
   (interpret-markup layout props
     #{\markup {
       \size-box-to-box-style-dependent ##t ##f
         \general-align #X #LEFT \override #`(direction . ,UP) \override #'(baseline-skip . 1.0) \dir-column \chord-alignment-style-dependent {
           \pad-to-box #'(0 . 0) #'(0 . 2.0) { #text }
           \size-box-to-box ##f ##t #uptext \score { \chords { g4:m a } \layout { \verselayout } }
         }
         #text
       }
     #}
   )))

#(define-markup-command (anchor-x-between layout props arga argb)
   (markup? markup?)
   (let* ((la (interval-length (ly:stencil-extent (interpret-markup layout props arga) X)))
          (m (interpret-markup layout props (markup #:general-align Y DOWN arga argb (make-size-box-to-box-markup #t #t (markup #:null) arga))))
          (l (interval-length (ly:stencil-extent m X))))
            (ly:stencil-aligned-to m X (- (/ (* la 2) l) 1))
     ))

#(define-markup-command (stanza-raw layout props arg)
   (string-or-music?)
   (let ((verselayout (chain-assoc-get 'verselayout props generalLayout)))
   (interpret-markup layout props
     (if (and (string? arg) (string-null? arg))
         " "
         #{\markup
           \score { \new Lyrics { \lyricmode { #(if (ly:music? arg) arg #{ \set stanza = #arg #}) "" } } \layout { \verselayout } }
         #}
     ))))

#(define-markup-command (stanza layout props arg)
   (string-or-music?)
   (interpret-markup layout props
     (make-size-box-to-box-markup #f #t (make-stanza-raw-markup arg) (make-stanza-raw-markup "x"))))

#(define (handle-custom-newlines custom-verse-breaks text)
  (if (null? custom-verse-breaks)
    text
    (let make-custom-linebreaks
      ((break-words custom-verse-breaks)
       (newtext (ly:regex-replace (ly:make-regex "\r?\n") text " ")))
      (if (null? break-words)
          newtext
          (make-custom-linebreaks
            (cdr break-words)
            (ly:regex-replace
              (ly:make-regex
                (string-append
                  "("
                  (string-concatenate
                    (map
                      (lambda (character)
                        (let ((escaped_char (ly:regex-quote (string character))))
                             (string-append "(?: *,[^,)]+\\)" escaped_char "|\\(?" escaped_char ")")))
                      (string->list (car break-words))))
                  "(?: *,[^,)]+\\))?)(.*)$"))
              newtext
              1 "\n" 2))))))

#(use-modules (lily display-lily))

% Kompletten Vers mit Akkorden
#(define-markup-command (chordverse layout props stanza verse) (string-or-music? string?)
   #:properties (
    (intraverse-vspace 0)
    (custom-verse-breaks '())
    (transposition (cons #f #f))
    (verselayout generalLayout)
    )
   "Vers mit Akkorden"
   (let ((transp (if (car transposition)
                    (string-append "\\transpose " (symbol->string (note-name->lily-string (car transposition))) " " (symbol->string (note-name->lily-string (cdr transposition))))
                    "")))
   (interpret-markup layout props
     (markup #:override `(baseline-skip . ,(+ intraverse-vspace songTextLineHeigth)) #:anchor-x-between #:stanza stanza
       (make-wrap-newline-markup
               (ly:regex-replace (ly:make-regex "\\(( *)([^,()]*)( *),([^)]*)\\)")
                 (ly:regex-replace (ly:make-regex "(([^ \n]*\\([^()]*,[^()]+\\)[^ \n(]*)+)") (handle-custom-newlines custom-verse-breaks verse) " \\concat { " 1 " } ")
                "\\textup \\line { \"" 1 "\" " 2 " \"" 3 "\" } \\score { " transp " \\chords { s4 " 4 " } \\layout { \\verselayout } }")
                )
     ))))

% Kompletter Vers aus dem Akkorde entfernt werden
#(define-markup-command (nochordverse layout props stanza verse) (string-or-music? string?)
   #:properties ((intraverse-vspace 0)(custom-verse-breaks '()))
   "Vers ohne Akkorde"
   (interpret-markup layout props
     (markup #:override `(baseline-skip . ,(+ intraverse-vspace 3.0)) #:anchor-x-between #:stanza stanza
           #:wrap-newline (ly:regex-replace (ly:make-regex "\\(([^,]*),([^)]*)\\)") (handle-custom-newlines custom-verse-breaks verse) 1)
     )
   )
)

#(define-markup-command (verseformat layout props verse) (markup?)
   #:properties ((verselayout generalLayout))
   "Textformatierung für Strophen"
   (interpret-markup layout props
     (let* (
         (layout-scale (ly:output-def-lookup layout 'output-scale 1.0))
         (verselayout-scale (ly:output-def-lookup verselayout 'output-scale layout-scale))
         (mag-scale (/ verselayout-scale layout-scale))
         (lyric-context-props (ly:context-def-lookup (ly:assoc-get 'Lyrics (ly:output-find-context-def verselayout 'Lyrics)) 'property-ops))
         (lyric-size (caddr (find (lambda (prop) (and (equal? 'push (car prop)) (equal? 'LyricText (cadr prop)) (equal? 'font-size (cadddr prop)))) lyric-context-props)))
       )
       (make-magnify-markup mag-scale (make-sans-markup (make-fontsize-markup lyric-size verse)))
     )
   )
)

#(define-markup-command (group-verses layout props versegroup) (markup-list?)
   #:properties ((verse-cols 1)
                 (verse-vspace 1)
                 (verse-hspace 1)
                 (verse-ordering-horizontal #f))
   "Gruppiere Strophen in einem Markup auf Wunsch spaltenweise"
   (define (add-markup-between-elements reverses markup-between elements)
     ((if reverses fold fold-right) (lambda (element filled-list)
              (cons element (if (null? filled-list) '() (cons markup-between filled-list))))
        '() elements))
   (let* ((column-item-count (ceiling (/ (length versegroup) verse-cols)))
          (column-data (make-list verse-cols)))
    (let columnize-list ((index 0) (items versegroup))
      (if (not (null? items))
        (let* ((column-index (if verse-ordering-horizontal
                                 (modulo index verse-cols)
                                 (floor (/ index column-item-count))))
               (column-markups (list-ref column-data column-index)))
          (list-set! column-data column-index (cons (car items) column-markups))
          (columnize-list (+ index 1) (cdr items)))))
    (interpret-markup layout props
      (make-fill-line-markup (list (make-verseformat-markup (make-line-markup
        (add-markup-between-elements #f
          (make-hspace-markup verse-hspace)
          (map (lambda (column-markups)
              (make-column-markup
                (add-markup-between-elements #t (make-vspace-markup verse-vspace) column-markups)))
            column-data)))))))))

#(define-markup-command (pad-left layout props amount arg)
  (number? markup?)
  (let* ((m (interpret-markup layout props arg))
         (x (ly:stencil-extent m X))
         (y (ly:stencil-extent m Y)))
    (ly:stencil-translate
      (ly:make-stencil (ly:stencil-expr m)
                       (cons (- (car x) amount) (cdr x))
                       y)
      `(,amount . 0))))

#(define-markup-command (pad-right layout props amount arg)
  (number? markup?)
  (let* ((m (interpret-markup layout props arg))
         (x (ly:stencil-extent m X))
         (y (ly:stencil-extent m Y)))
      (ly:make-stencil (ly:stencil-expr m)
                       (cons (car x) (+ (cdr x) amount))
                       y)))

#(define-markup-command (score-equal-height layout props reference-height lines)
  (number? markup-list?)
  #:category music
  #:properties ((baseline-skip))
    (stack-stencils Y DOWN baseline-skip
      (map
        (lambda (line) (ly:make-stencil (ly:stencil-expr line) (ly:stencil-extent line X) `(,(/ reference-height -2.0) . ,(/ reference-height 2.0))))
        (interpret-markup-list layout props lines))))

#(define-public (custom-lyric-text::print grob)
  "Allow interpretation of tildes as lyric tieing marks."
  ;; See also similar code in Lyric_performer.
  (let ((text (ly:grob-property grob 'text)))

    (grob-interpret-markup grob (if (string? text)
                                    (make-pad-right-markup -0.1 (make-tied-lyric-markup text))
                                    text))))

#(define-markup-command (chordlyrics layout props lyrics) (ly:music?)
   #:properties ((verse-chords #{#})
                 (verse-reference-voice #{#})
                 (verse-break-voice #{#})
                 (verse-line-height songTextLineHeigth)
                 (verse-text-chord-distance songTextChordDistance)
                 (intraverse-vspace 0)
                 (transposition (cons #f #f))
                 (verselayout generalLayout))
  "Vers mit Akkorden"
  (interpret-markup layout props
    #{
      \markup {
        \override #`(baseline-skip . ,intraverse-vspace)
        \score-equal-height #verse-line-height \score-lines {
          <<
            \new Devnull { #verse-break-voice }
            \new NullVoice = "dummyvoice" { #verse-reference-voice }
            \transposable #transposition #(music-clone verse-chords)
            \new Lyrics \lyricsto "dummyvoice" { #lyrics }
          >>
          \layout {
            \verselayout
            #(let
              ((custom-size (ly:output-def-lookup verselayout 'size #f)))
              (if custom-size (layout-set-staff-size custom-size)))
            ragged-right = ##t
            \context {
              \Lyrics
              \override VerticalAxisGroup.nonstaff-relatedstaff-spacing.basic-distance = #verse-text-chord-distance
              \override LyricText.parent-alignment-X = #LEFT
              \override LyricText.self-alignment-X = #LEFT
              \override LyricText.word-space = 0.8
              \override LyricSpace.minimum-distance = 0.8
              \override LyricText.stencil = #custom-lyric-text::print
            }
            \context {
              \ChordNames
              \override VerticalAxisGroup.staff-affinity = ##f
              \override ChordName.extra-spacing-width = #'(-0.1 . 0.1)
              \consists \Ensure_first_chord_after_line_break_printed_engraver
            }
            \context {
              \Score
              \override PaperColumn.keep-inside-line = ##f
             % \override SpacingSpanner.strict-note-spacing = ##t
              \override SpacingSpanner.uniform-stretching = ##t
              \override SpacingSpanner.spacing-increment = 0
              \remove Bar_number_engraver
              \remove Mark_engraver
              \remove Jump_engraver
              \remove Volta_engraver
              \remove Parenthesis_engraver
            }
            \context {
              \Staff
              \remove Staff_symbol_engraver
              \remove Clef_engraver
              \remove Time_signature_engraver
              \remove Bar_engraver
              \remove Separating_line_group_engraver
              \omit KeySignature
              \omit KeyCancellation
            }
            \context {
              \Voice
              \remove Stem_engraver
              \remove Rest_engraver
              \remove Multi_measure_rest_engraver
              \remove Phrasing_slur_engraver
              \remove Slur_engraver
              \remove Tie_engraver
              \remove Dynamic_engraver
              \remove Note_heads_engraver
            }
            \context {
              \NullVoice
              \consists Rest_engraver
              \omit Rest
             % \undo \omit NoteHead
             % \hide NoteHead
            }
          }
        }
      }
    #}
  )
)

#(define-markup-command (nochordlyrics layout props lyrics) (ly:music?)
    "Vers ohne Akkorde"
  (interpret-markup layout props
    (markup
      #:override `(verse-chords . ,#{#})
      #:override `(verse-line-height . ,(- songTextLineHeigth 2))
      #:chordlyrics lyrics))
)