package parser

import "../token"
import "core:strconv"
import "core:strings"

parse :: proc(lines: []string, allocator := context.allocator) -> token.Token {
	root := token.Token{token.TokenType.Root, nil, nil, nil}
	current_token := &root

	in_code_block := false
	in_script_block := false
	script_block_content: [dynamic]string
	script_block_type := ""

	for untrimmed_line, line_index in lines {
		line := strings.trim_space(untrimmed_line)

		if in_code_block {
			if strings.has_prefix(line, "```") {
				in_code_block = false
				current_token = &root
			} else {
				push_token(current_token, token.TokenType.Text, untrimmed_line)
			}
			continue
		}

		if in_script_block {
			if strings.has_prefix(line, "$[/") {
				full_content := strings.join(script_block_content[:], "\n")
				script_token := push_token(&root, token.TokenType.ScriptBlock, full_content)
				push_token(script_token, token.TokenType.Text, script_block_type)
				in_script_block = false
				script_block_content = nil
				script_block_type = ""
				current_token = &root
			} else {
				append(&script_block_content, untrimmed_line)
			}
			continue
		}

		line = unescape_backslashes(line)

		if strings.has_prefix(line, "```") {
			lang := strings.trim_space(line[3:])
			current_token = push_token(&root, token.TokenType.CodeBlock, lang)
			in_code_block = true
			continue
		}

		if strings.has_prefix(line, "$[") && strings.has_suffix(line, "]") {
			inner := strings.trim_space(line[2:len(line) - 1])
			if inner == "script" || inner == "js" || inner == "javascript" {
				in_script_block = true
				script_block_type = inner
				script_block_content = nil
				current_token = &root
				continue
			}
		}

		if len(line) >= 3 {
			is_hr := true
			ch := line[0]
			if ch == '-' || ch == '_' || ch == '=' {
				for i := 1; i < len(line); i += 1 {
					if line[i] != ch {
						is_hr = false
						break
					}
				}
			} else {
				is_hr = false
			}

			if is_hr {
				push_token(&root, token.TokenType.HorizontalRule, nil)
				current_token = &root
				continue
			}
		}

		if len(line) == 0 {
			current_token = &root
			continue
		}

		header_level, text := parse_header(line)
		if header_level > 0 {
			current_token = push_token(&root, token.TokenType.Header, header_level)
			push_text(current_token, text)
			current_token = &root
			continue
		}

		if id, ftext, ok := parse_footnote_def(line); ok {
			current_token = push_token(&root, token.TokenType.FootnoteDef, id)
			push_text(current_token, ftext)
			current_token = &root
			continue
		}

		if strings.has_prefix(line, "> ") {
			if current_token.type != token.TokenType.Blockquote {
				current_token = push_token(&root, token.TokenType.Blockquote, nil)
				push_token(current_token, token.TokenType.Paragraph, nil)
			}
			last_p := current_token.children[len(current_token.children) - 1]
			push_text_line(last_p, line[2:])
			continue
		}

		if strings.has_prefix(line, ". ") || strings.has_prefix(line, "- ") {
			if current_token.type != token.TokenType.UnorderedList {
				current_token = push_token(&root, token.TokenType.UnorderedList, nil)
			}
			li := push_token(current_token, token.TokenType.ListItem, nil)
			push_text(li, line[2:])
			continue
		}

		if len(line) >= 3 {
			item := 1
			idx := 0
			for idx < len(line) && line[idx] >= '0' && line[idx] <= '9' {
				idx += 1
			}

			if idx > 0 && idx + 1 < len(line) && line[idx] == '.' && line[idx + 1] == ' ' {
				item, _ = strconv.parse_int(line[:idx])
				remaining_text := line[idx + 2:]

				if current_token.type != token.TokenType.OrderedList {
					current_token = push_token(&root, token.TokenType.OrderedList, item)
				}
				li := push_token(current_token, token.TokenType.ListItem, nil)
				push_text(li, remaining_text)
				continue
			}
		}

		if len(line) >= 3 && line[0] == '|' && line[len(line) - 1] == '|' {
			if current_token.type != token.TokenType.Table {
				current_token = push_token(&root, token.TokenType.Table, nil)
			}

			if is_table_separator(line) {
				row_count := len(current_token.children)
				if row_count == 1 {
					current_token.children[0].type = token.TokenType.TableHeaderRow
				} else if row_count > 1 {
					push_token(current_token, token.TokenType.TableSeparator, nil)
				}
			} else {
				row := push_token(current_token, token.TokenType.TableRow, nil)
				cells := parse_table_cells(line)
				for cell_text in cells {
					cell := push_token(row, token.TokenType.TableCell, nil)
					push_text(cell, cell_text)
				}
			}
			continue
		}

		if current_token.type == .Blockquote {
			last_p := current_token.children[len(current_token.children) - 1]
			push_text_line(last_p, line)
		} else if current_token.type == .UnorderedList || current_token.type == .OrderedList {
			last_li := current_token.children[len(current_token.children) - 1]
			push_text_line(last_li, line)
		} else {
			if current_token.type != .Paragraph {
				current_token = push_token(&root, token.TokenType.Paragraph, nil)
			}
			push_text_line(current_token, line)
		}
	}

	for tok in root.children {
		parse_inline_code(tok)
		parse_inline_footnotes(tok)
		parse_links(tok)
		parse_variables(tok)
		parse_embeds(tok)
		pass_through_children(tok)
		decode_text_escapes(tok)
	}

	vars: map[string]string
	for child in root.children {
		resolve_variables(child, &vars)
	}

	footnotes: [dynamic]^token.Token
	new_root_children: [dynamic]^token.Token
	for child in root.children {
		if child.type == .FootnoteDef {
			append(&footnotes, child)
		} else {
			append(&new_root_children, child)
		}
	}
	for child in footnotes {
		append(&new_root_children, child)
	}
	root.children = new_root_children

	return root
}
