// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package comment

import (
	
	
	
)

// An mdPrinter holds the state needed for printing a Doc as Markdown.
type mdPrinter struct {
	*Printer
	headingPrefix string
	raw           bytes.Buffer
}

// Markdown returns a Markdown formatting of the Doc.
// See the [Printer] documentation for ways to customize the Markdown output.
func ( *Printer) ( *Doc) []byte {
	 := &mdPrinter{
		Printer:       ,
		headingPrefix: strings.Repeat("#", .headingLevel()) + " ",
	}

	var  bytes.Buffer
	for ,  := range .Content {
		if  > 0 {
			.WriteByte('\n')
		}
		.block(&, )
	}
	return .Bytes()
}

// block prints the block x to out.
func ( *mdPrinter) ( *bytes.Buffer,  Block) {
	switch x := .(type) {
	default:
		fmt.Fprintf(, "?%T", )

	case *Paragraph:
		.text(, .Text)
		.WriteString("\n")

	case *Heading:
		.WriteString(.headingPrefix)
		.text(, .Text)
		if  := .headingID();  != "" {
			.WriteString(" {#")
			.WriteString()
			.WriteString("}")
		}
		.WriteString("\n")

	case *Code:
		 := .Text
		for  != "" {
			var  string
			, , _ = strings.Cut(, "\n")
			if  != "" {
				.WriteString("\t")
				.WriteString()
			}
			.WriteString("\n")
		}

	case *List:
		 := .BlankBetween()
		for ,  := range .Items {
			if  > 0 &&  {
				.WriteString("\n")
			}
			if  := .Number;  != "" {
				.WriteString(" ")
				.WriteString()
				.WriteString(". ")
			} else {
				.WriteString("  - ") // SP SP - SP
			}
			for ,  := range .Content {
				const  = "    "
				if  > 0 {
					.WriteString("\n" + )
				}
				.text(, .(*Paragraph).Text)
				.WriteString("\n")
			}
		}
	}
}

// text prints the text sequence x to out.
func ( *mdPrinter) ( *bytes.Buffer,  []Text) {
	.raw.Reset()
	.rawText(&.raw, )
	 := bytes.TrimSpace(.raw.Bytes())
	if len() == 0 {
		return
	}
	switch [0] {
	case '+', '-', '*', '#':
		// Escape what would be the start of an unordered list or heading.
		.WriteByte('\\')
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		 := 1
		for  < len() && '0' <= [] && [] <= '9' {
			++
		}
		if  < len() && ([] == '.' || [] == ')') {
			// Escape what would be the start of an ordered list.
			.Write([:])
			.WriteByte('\\')
			 = [:]
		}
	}
	.Write()
}

// rawText prints the text sequence x to out,
// without worrying about escaping characters
// that have special meaning at the start of a Markdown line.
func ( *mdPrinter) ( *bytes.Buffer,  []Text) {
	for ,  := range  {
		switch t := .(type) {
		case Plain:
			.escape(, string())
		case Italic:
			.WriteString("*")
			.escape(, string())
			.WriteString("*")
		case *Link:
			.WriteString("[")
			.(, .Text)
			.WriteString("](")
			.WriteString(.URL)
			.WriteString(")")
		case *DocLink:
			 := .docLinkURL()
			if  != "" {
				.WriteString("[")
			}
			.(, .Text)
			if  != "" {
				.WriteString("](")
				 = strings.ReplaceAll(, "(", "%28")
				 = strings.ReplaceAll(, ")", "%29")
				.WriteString()
				.WriteString(")")
			}
		}
	}
}

// escape prints s to out as plain text,
// escaping special characters to avoid being misinterpreted
// as Markdown markup sequences.
func ( *mdPrinter) ( *bytes.Buffer,  string) {
	 := 0
	for  := 0;  < len(); ++ {
		switch [] {
		case '\n':
			// Turn all \n into spaces, for a few reasons:
			//   - Avoid introducing paragraph breaks accidentally.
			//   - Avoid the need to reindent after the newline.
			//   - Avoid problems with Markdown renderers treating
			//     every mid-paragraph newline as a <br>.
			.WriteString([:])
			.WriteByte(' ')
			 =  + 1
			continue
		case '`', '_', '*', '[', '<', '\\':
			// Not all of these need to be escaped all the time,
			// but is valid and easy to do so.
			// We assume the Markdown is being passed to a
			// Markdown renderer, not edited by a person,
			// so it's fine to have escapes that are not strictly
			// necessary in some cases.
			.WriteString([:])
			.WriteByte('\\')
			.WriteByte([])
			 =  + 1
		}
	}
	.WriteString([:])
}