package main

import "core:fmt"
import "core:strings"
import "core:unicode"

escape_html :: proc(s: string, allocator := context.temp_allocator) -> string {
	escaped := decode_escapes(s, allocator)

	b := strings.builder_make(allocator)
	for ch in escaped {
		switch ch {
		case '&':
			strings.write_string(&b, "&amp;")
		case '<':
			strings.write_string(&b, "&lt;")
		case '>':
			strings.write_string(&b, "&gt;")
		case '"':
			strings.write_string(&b, "&quot;")
		case:
			strings.write_rune(&b, ch)
		}
	}
	return strings.to_string(b)
}

get_text_content :: proc(token: ^Token, b: ^strings.Builder) {
	if token.type == .Text {
		if v, ok := token.value.(string); ok {
			strings.write_string(b, v)
		}
	}
	for child in token.children {
		get_text_content(child, b)
	}
}

check_only_var_defs :: proc(token: ^Token) -> bool {
	for child in token.children {
		if child.type == .VariableDef || child.type == .BreakLine {
			continue
		}
		if child.type == .Text {
			if child.value != nil {
				if v, ok := child.value.(string); ok && v != "" {
					return false
				}
			}
			has_non_var_def := false
			for gc in child.children {
				if gc.type != .VariableDef {
					has_non_var_def = true
					break
				}
			}
			if has_non_var_def {
				return false
			}
			continue
		}
		return false
	}
	return true
}

slugifyifyifyify :: proc(text: string) -> string {
	b := strings.builder_make(context.temp_allocator)

	for ch in text {
		lower_ch := unicode.to_lower(ch)

		if unicode.is_letter(lower_ch) || unicode.is_digit(lower_ch) {
			strings.write_rune(&b, lower_ch)
		} else if lower_ch == ' ' || lower_ch == '_' || lower_ch == '-' {
			strings.write_rune(&b, '-')
		}
	}

	res := strings.to_string(b)

	for {
		new_res, replaced := strings.replace_all(res, "--", "-", context.temp_allocator)
		if !replaced {
			break
		}
		res = new_res
	}

	res = strings.trim(res, "-")

	return res
}

print_html :: proc(token: ^Token) {
	switch token.type {
	case TokenType.Root:
		has_footnotes := false
		for child in token.children {
			if child.type == .FootnoteDef && !has_footnotes {
				fmt.print("<hr class=\"footnotes-sep\">\n")
				has_footnotes = true
			}
			print_html(child)
		}

	case TokenType.Header:
		level := 1
		if v, ok := token.value.(int); ok {
			level = v
		}

		b := strings.builder_make(context.temp_allocator)
		for child in token.children {
			get_text_content(child, &b)
		}
		slug := slugifyifyifyify(strings.to_string(b))

		fmt.printf("<h%d id=\"%s\">", level, escape_html(slug))
		for child in token.children {
			print_html(child)
		}
		fmt.printf("</h%d>\n", level)

	case TokenType.Paragraph:
		only_var_defs := check_only_var_defs(token)
		if !only_var_defs {
			fmt.print("<p>")
			for child in token.children {
				print_html(child)
			}
			fmt.print("</p>\n")
		} else {
			for child in token.children {
				if child.type != .BreakLine {
					print_html(child)
				}
			}
		}

	case TokenType.Blockquote:
		fmt.print("<blockquote>\n")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</blockquote>\n")

	case TokenType.UnorderedList:
		fmt.print("<ul>\n")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</ul>\n")

	case TokenType.OrderedList:
		fmt.print("<ol>\n")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</ol>\n")

	case TokenType.ListItem:
		fmt.print("<li>")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</li>\n")

	case TokenType.Bold:
		fmt.print("<strong>")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</strong>")

	case TokenType.Italics:
		fmt.print("<em>")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</em>")

	case TokenType.Strikethrough:
		fmt.print("<strike>")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</strike>")

	case TokenType.Table:
		fmt.print("<table>\n")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</table>\n")

	case TokenType.TableHeaderRow:
		fmt.print("<tr>")
		for child in token.children {
			align := ""
			if len(child.children) > 0 {
				first_child := child.children[0]
				if first_child.type == .Left {
					align = "left"
				} else if first_child.type == .Right {
					align = "right"
				} else if first_child.type == .Middle {
					align = "center"
				}
			}
			if align != "" {
				fmt.printf("<th align=\"%s\">", align)
				for grandchild in child.children[0].children {
					print_html(grandchild)
				}
			} else {
				fmt.print("<th>")
				for grandchild in child.children {
					print_html(grandchild)
				}
			}
			fmt.print("</th>")
		}
		fmt.print("</tr>\n")

	case TokenType.TableRow:
		fmt.print("<tr>")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</tr>\n")

	case TokenType.TableCell:
		align := ""
		if len(token.children) > 0 {
			first_child := token.children[0]
			if first_child.type == .Left {
				align = "left"
			} else if first_child.type == .Right {
				align = "right"
			} else if first_child.type == .Middle {
				align = "center"
			}
		}
		if align != "" {
			fmt.printf("<td align=\"%s\">", align)
			for child in token.children[0].children {
				print_html(child)
			}
		} else {
			fmt.print("<td>")
			for child in token.children {
				print_html(child)
			}
		}
		fmt.print("</td>")

	case TokenType.TableSeparator:
		fmt.print("<tr class=\"table-sep\"><td colspan=\"100\"></td></tr>\n")

	case TokenType.Left:
		fmt.print("<div align=\"left\">")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</div>")

	case TokenType.Right:
		fmt.print("<div align=\"right\">")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</div>")

	case TokenType.Middle:
		fmt.print("<div align=\"center\">")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</div>")

	case TokenType.FootnoteRef:
		if id, ok := token.value.(string); ok {
			fmt.printf(
				"<a href=\"#fn-%s\" id=\"ref-%s\"><sup>%s</sup></a>",
				escape_html(id),
				escape_html(id),
				escape_html(id),
			)
		}

	case TokenType.FootnoteDef:
		if id, ok := token.value.(string); ok {
			fmt.printf(
				"<div class=\"footnote\" id=\"fn-%s\"><a href=\"#ref-%s\">^</a> ",
				escape_html(id),
				escape_html(id),
			)
			for child in token.children {
				print_html(child)
			}
			fmt.print("</div>\n")
		}

	case TokenType.CodeBlock:
		lang := ""
		if v, ok := token.value.(string); ok && v != "" {
			lang = v
		}
		if lang != "" {
			fmt.printf("<pre><code class=\"lang-%s\">", lang)
		} else {
			fmt.print("<pre><code>")
		}
		for i := 0; i < len(token.children); i += 1 {
			print_html(token.children[i])
			if i + 1 < len(token.children) {
				fmt.print("\n")
			}
		}
		fmt.print("\n</code></pre>\n")

	case TokenType.InlineCode:
		if code, ok := token.value.(string); ok {
			fmt.printf("<code>%s</code>", escape_html(code))
		}

	case TokenType.HorizontalRule:
		fmt.print("<hr>\n")

	case TokenType.Underline:
		fmt.print("<u>")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</u>")

	case TokenType.Superscript:
		fmt.print("<sup>")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</sup>")

	case TokenType.Subscript:
		fmt.print("<sub>")
		for child in token.children {
			print_html(child)
		}
		fmt.print("</sub>")

	case TokenType.HeaderLink:
		if target, ok := token.value.(string); ok {
			slug := slugifyifyifyify(target)
			fmt.printf("<a href=\"#%s\">", escape_html(slug))
			for child in token.children {
				print_html(child)
			}
			fmt.print("</a>")
		}

	case TokenType.Link:
		if url, ok := token.value.(string); ok {
			fmt.printf("<a href=\"%s\">", escape_html(url))
			for child in token.children {
				print_html(child)
			}
			fmt.print("</a>")
		}

	case TokenType.Image:
		if url, ok := token.value.(string); ok {
			b := strings.builder_make(context.temp_allocator)
			for child in token.children {
				get_text_content(child, &b)
			}
			alt_text := strings.to_string(b)
			fmt.printf("<img src=\"%s\" alt=\"%s\">", escape_html(url), escape_html(alt_text))
		}

	case TokenType.Audio:
		if url, ok := token.value.(string); ok {
			mime := detect_mime_type(url)
			b := strings.builder_make(context.temp_allocator)
			for child in token.children {
				get_text_content(child, &b)
			}
			alt_text := strings.to_string(b)
			if alt_text == "" {
				alt_text = url
			}
			fmt.printf("<object data=\"%s\" type=\"%s\">\n", escape_html(url), escape_html(mime))
			fmt.printf("<param name=\"src\" value=\"%s\">\n", escape_html(url))
			fmt.printf("<param name=\"autoplay\" value=\"false\">\n")
			fmt.printf("<param name=\"controller\" value=\"true\">\n")
			fmt.printf("<a href=\"%s\">%s</a>\n", escape_html(url), escape_html(alt_text))
			fmt.print("</object>\n")
		}

	case TokenType.Video:
		if url, ok := token.value.(string); ok {
			mime := detect_mime_type(url)
			b := strings.builder_make(context.temp_allocator)
			for child in token.children {
				get_text_content(child, &b)
			}
			alt_text := strings.to_string(b)
			if alt_text == "" {
				alt_text = url
			}
			fmt.printf("<object data=\"%s\" type=\"%s\">\n", escape_html(url), escape_html(mime))
			fmt.printf("<param name=\"src\" value=\"%s\">\n", escape_html(url))
			fmt.printf("<param name=\"autoplay\" value=\"false\">\n")
			fmt.printf("<param name=\"controller\" value=\"true\">\n")
			fmt.printf("<a href=\"%s\">%s</a>\n", escape_html(url), escape_html(alt_text))
			fmt.print("</object>\n")
		}

	case TokenType.Iframe:
		if url, ok := token.value.(string); ok {
			fmt.printf("<iframe src=\"%s\"></iframe>", escape_html(url))
		}

	case TokenType.Script:
		if url, ok := token.value.(string); ok {
			fmt.printf("<script src=\"%s\"></script>", escape_html(url))
		}

	case TokenType.ScriptBlock:
		if content, ok := token.value.(string); ok {
			fmt.printf("<script>%s</script>", content)
		}

	case TokenType.Variable:
		if name, ok := token.value.(string); ok {
			fmt.printf("<span data-tlm-var=\"%s\">", escape_html(name))
			for child in token.children {
				print_html(child)
			}
			fmt.print("</span>")
		}

	case TokenType.VariableDef:
		if name, ok := token.value.(string); ok {
			b := strings.builder_make(context.temp_allocator)
			for child in token.children {
				get_text_content(child, &b)
			}
			val := strings.to_string(b)
			fmt.printf(
				"<template data-tlm-def=\"%s\" data-tlm-value=\"%s\"></template>",
				escape_html(name),
				escape_html(val),
			)
		}

	case TokenType.BreakLine:
		fmt.print("<br>\n")

	case TokenType.Text:
		if v, ok := token.value.(string); ok {
			fmt.print(escape_html(v))
		}
		for child in token.children {
			print_html(child)
		}
	}
}
