// SPDX-FileCopyrightText: 2024 Nicolas Peugnet <nicolas@club1.fr>
// SPDX-License-Identifier: GPL-3.0-or-later

package markdown

import (
	"github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/renderer"
	"github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/util"
)

// NoEscapeCodeBlockRenderer is a renderer.NodeRenderer implementation that
// renders code blocks without escaping HTML special characters.
//
// This is needed in lintian-ssg because the tag description are not really
// Markdown conformant. They are over-escaped inside code blocks, which
// means that another pass of escaping will not produce the expected result.
//
// Specifically, Lintian tags explanation have had their underscores (_)
// replaced by &lowbar; in lintian#d590cbf22, as well as some other special
// chars, apparently to fix the HTML output. Escaping them once more, as
// the default CodeBlock renderer would do it, would result in &lowbar;
// being displayed in the code block instead of the expected underscore (_).
//
// This renderer could be removed once the tag descriptions are fixed to use
// conformant Markdown.
type NoEscapeCodeBlockRenderer struct {
	html.Config
}

// NewNoEscapeCodeBlockRenderer returns a new [NoEscapeCodeBlockRenderer].
func NewNoEscapeCodeBlockRenderer() *NoEscapeCodeBlockRenderer {
	return &NoEscapeCodeBlockRenderer{html.NewConfig()}
}

// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *NoEscapeCodeBlockRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
	reg.Register(ast.KindCodeBlock, r.renderCodeBlock)
}

func (r *NoEscapeCodeBlockRenderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
	if entering {
		_, _ = w.WriteString("<pre><code>")
		r.writeLines(w, source, n)
	} else {
		_, _ = w.WriteString("</code></pre>\n")
	}
	return ast.WalkContinue, nil
}

func (r *NoEscapeCodeBlockRenderer) writeLines(w util.BufWriter, source []byte, n ast.Node) {
	l := n.Lines().Len()
	for i := 0; i < l; i++ {
		line := n.Lines().At(i)
		// write codeblock's text as is in the HTML output
		r.Writer.Write(w, line.Value(source))
	}
}
