// Copyright (c) 2015-2021 Martin Hensel
// Copyright (c) 2026 luo-studio
// Copyright (c) 2026 erweixin (upstream RaTeX)
// SPDX-License-Identifier: Apache-2.0 AND MIT
//
// Go translation and modification of mhchemparser 4.1.1. Initial Go
// translation structure adapted from github.com/Luo-Studio/go-tex (MIT).

package mhchem

import (
	
	
)

// texify renders the parser output (a flat []Value of strings and tagged
// maps) into a TeX fragment. Mirrors upstream texify::go.
func texify( []Value,  bool) (string, error) {
	var  strings.Builder
	 := false
	for ,  := range  {
		if ,  := .(string);  {
			.WriteString()
			continue
		}
		if typeStr() == "1st-level escape" {
			 = true
		}
		,  := texify2()
		if  != nil {
			return "", 
		}
		.WriteString()
	}
	 := .String()
	if  && ! &&  != "" {
		return "{" +  + "}", nil
	}
	return , nil
}

func typeStr( Value) string {
	,  := .(map[string]any)
	if ! {
		return ""
	}
	,  := ["type_"].(string)
	return 
}

func mapStr( Value,  string) string {
	,  := .(map[string]any)
	if ! {
		return ""
	}
	,  := [].(string)
	return 
}

func arrInner( Value,  string) (string, error) {
	,  := .(map[string]any)
	if ! {
		return "", nil
	}
	,  := []
	if ! {
		return "", nil
	}
	,  := .([]Value)
	if ! {
		// May come in as []any after json round-trip; coerce.
		if ,  := .([]any);  {
			 := make([]Value, len())
			for  := range  {
				[] = []
			}
			 = 
		} else {
			return "", nil
		}
	}
	return texify(, false)
}

func texify2( Value) (string, error) {
	 := typeStr()
	if  == "" {
		return "", parserError("MhchemBugT", "mhchem bug T. Please report.")
	}
	switch  {
	case "chemfive":
		return texifyChemfive()
	case "rm":
		return fmt.Sprintf("\\mathrm{%s}", mapStr(, "p1")), nil
	case "text":
		 := mapStr(, "p1")
		if strings.ContainsAny(, "^_") {
			 = strings.Replace(, " ", "~", 1)
			 = strings.Replace(, "-", "\\text{-}", 1)
			return fmt.Sprintf("\\mathrm{%s}", ), nil
		}
		return fmt.Sprintf("\\text{%s}", ), nil
	case "roman numeral":
		return fmt.Sprintf("\\mathrm{%s}", mapStr(, "p1")), nil
	case "state of aggregation":
		,  := arrInner(, "p1")
		if  != nil {
			return "", 
		}
		return "\\mskip2mu " + , nil
	case "state of aggregation subscript":
		,  := arrInner(, "p1")
		if  != nil {
			return "", 
		}
		return "\\mskip1mu " + , nil
	case "bond":
		 := mapStr(, "kind_")
		,  := getBond()
		if ! {
			return "", parserError("MhchemErrorBond", "mhchem Error. Unknown bond type ("++")")
		}
		return , nil
	case "frac":
		 := mapStr(, "p1")
		 := mapStr(, "p2")
		 := fmt.Sprintf("\\frac{%s}{%s}", , )
		return fmt.Sprintf("\\mathchoice{\\textstyle%s}{%s}{%s}{%s}", , , , ), nil
	case "pu-frac":
		,  := arrInner(, "p1")
		if  != nil {
			return "", 
		}
		,  := arrInner(, "p2")
		if  != nil {
			return "", 
		}
		 := fmt.Sprintf("\\frac{%s}{%s}", , )
		return fmt.Sprintf("\\mathchoice{\\textstyle%s}{%s}{%s}{%s}", , , , ), nil
	case "tex-math":
		return mapStr(, "p1") + " ", nil
	case "frac-ce":
		,  := arrInner(, "p1")
		if  != nil {
			return "", 
		}
		,  := arrInner(, "p2")
		if  != nil {
			return "", 
		}
		return fmt.Sprintf("\\frac{%s}{%s}", , ), nil
	case "overset":
		,  := arrInner(, "p1")
		if  != nil {
			return "", 
		}
		,  := arrInner(, "p2")
		if  != nil {
			return "", 
		}
		return fmt.Sprintf("\\overset{%s}{%s}", , ), nil
	case "underset":
		,  := arrInner(, "p1")
		if  != nil {
			return "", 
		}
		,  := arrInner(, "p2")
		if  != nil {
			return "", 
		}
		return fmt.Sprintf("\\underset{%s}{%s}", , ), nil
	case "underbrace":
		,  := arrInner(, "p1")
		if  != nil {
			return "", 
		}
		,  := arrInner(, "p2")
		if  != nil {
			return "", 
		}
		return fmt.Sprintf("\\underbrace{%s}_{%s}", , ), nil
	case "color":
		 := mapStr(, "color1")
		,  := arrInner(, "color2")
		if  != nil {
			return "", 
		}
		return fmt.Sprintf("{\\color{%s}{%s}}", , ), nil
	case "color0":
		 := mapStr(, "color")
		return fmt.Sprintf("\\color{%s}", ), nil
	case "arrow":
		,  := arrInner(, "rd")
		if  != nil {
			return "", 
		}
		,  := arrInner(, "rq")
		if  != nil {
			return "", 
		}
		 := mapStr(, "r")
		,  := getArrow()
		if  != nil {
			return "", 
		}
		if  != "" ||  != "" {
			switch  {
			case "<=>", "<=>>", "<<=>", "<-->":
				 = "\\long" + 
				if  != "" {
					 = "\\overset{" +  + "}{" +  + "}"
				}
				if  != "" {
					 := "\\lower6mu{"
					if  == "<-->" {
						 = "\\lower2mu{"
					}
					 = "\\underset{" +  +  + "}}{" +  + "}"
				}
				 = " {}\\mathrel{" +  + "}{} "
			default:
				if  != "" {
					 += "[{" +  + "}]"
				}
				 += "{" +  + "}"
				 = " {}\\mathrel{\\x" +  + "}{} "
			}
		} else {
			 = " {}\\mathrel{\\long" +  + "}{} "
		}
		return , nil
	case "operator":
		 := mapStr(, "kind_")
		,  := getOperator()
		if ! {
			return "", parserError("MhchemBugT", "mhchem bug T. Please report.")
		}
		return , nil
	case "1st-level escape":
		return mapStr(, "p1") + " ", nil
	case "space":
		return " ", nil
	case "tinySkip":
		return "\\mkern2mu", nil
	case "entitySkip":
		return "~", nil
	case "pu-space-1":
		return "~", nil
	case "pu-space-2":
		return "\\mkern3mu ", nil
	case "1000 separator":
		return "\\mkern2mu ", nil
	case "commaDecimal":
		return "{,}", nil
	case "comma enumeration L":
		return fmt.Sprintf("{%s}\\mkern6mu ", mapStr(, "p1")), nil
	case "comma enumeration M":
		return fmt.Sprintf("{%s}\\mkern3mu ", mapStr(, "p1")), nil
	case "comma enumeration S":
		return fmt.Sprintf("{%s}\\mkern1mu ", mapStr(, "p1")), nil
	case "hyphen":
		return "\\text{-}", nil
	case "addition compound":
		return "\\,{\\cdot}\\,", nil
	case "electron dot":
		return "\\mkern1mu \\bullet\\mkern1mu ", nil
	case "KV x":
		return "{\\times}", nil
	case "prime":
		return "\\prime ", nil
	case "cdot":
		return "\\cdot ", nil
	case "tight cdot":
		return "\\mkern1mu{\\cdot}\\mkern1mu ", nil
	case "times":
		return "\\times ", nil
	case "circa":
		return "{\\sim}", nil
	case "^":
		return "uparrow", nil
	case "v":
		return "downarrow", nil
	case "ellipsis":
		return "\\ldots ", nil
	case "/":
		return "/", nil
	case " / ":
		return "\\,/\\,", nil
	}
	return "", parserError("MhchemBugT", "mhchem bug T. Please report.")
}

func texifyChemfive( Value) (string, error) {
	,  := arrInner(, "a")
	if  != nil {
		return "", 
	}
	,  := arrInner(, "b")
	if  != nil {
		return "", 
	}
	,  := arrInner(, "p")
	if  != nil {
		return "", 
	}
	,  := arrInner(, "o")
	if  != nil {
		return "", 
	}
	,  := arrInner(, "q")
	if  != nil {
		return "", 
	}
	,  := arrInner(, "d")
	if  != nil {
		return "", 
	}
	var  strings.Builder
	if  != "" {
		 := 
		if strings.HasPrefix(, "+") || strings.HasPrefix(, "-") {
			 = "{" +  + "}"
		}
		.WriteString()
		.WriteString("\\,")
	}
	if  != "" ||  != "" {
		.WriteString("{\\vphantom{A}}")
		fmt.Fprintf(&, "^{\\hphantom{%s}}_{\\hphantom{%s}}", , )
		.WriteString("\\mkern-1.5mu")
		.WriteString("{\\vphantom{A}}")
		fmt.Fprintf(&, "^{\\smash[t]{\\vphantom{2}}\\llap{%s}}", )
		fmt.Fprintf(&, "_{\\vphantom{2}\\llap{\\smash[t]{%s}}}", )
	}
	if  != "" {
		 := 
		if strings.HasPrefix(, "+") || strings.HasPrefix(, "-") {
			 = "{" +  + "}"
		}
		.WriteString()
	}
	 := mapStr(, "dType")
	switch  {
	case "kv":
		if  != "" ||  != "" {
			.WriteString("{\\vphantom{A}}")
		}
		if  != "" {
			fmt.Fprintf(&, "^{%s}", )
		}
		if  != "" {
			fmt.Fprintf(&, "_{\\smash[t]{%s}}", )
		}
	case "oxidation":
		if  != "" {
			.WriteString("{\\vphantom{A}}")
			fmt.Fprintf(&, "^{%s}", )
		}
		if  != "" {
			.WriteString("{\\vphantom{A}}")
			fmt.Fprintf(&, "_{\\smash[t]{%s}}", )
		}
	default:
		if  != "" {
			.WriteString("{\\vphantom{A}}")
			fmt.Fprintf(&, "_{\\smash[t]{%s}}", )
		}
		if  != "" {
			.WriteString("{\\vphantom{A}}")
			fmt.Fprintf(&, "^{%s}", )
		}
	}
	return .String(), nil
}

func getArrow( string) (string, error) {
	switch  {
	case "->", "→", "⟶":
		return "rightarrow", nil
	case "<-":
		return "leftarrow", nil
	case "<->":
		return "leftrightarrow", nil
	case "<-->":
		return "leftrightarrows", nil
	case "<=>", "⇌":
		return "rightleftharpoons", nil
	case "<=>>":
		return "Rightleftharpoons", nil
	case "<<=>":
		return "Leftrightharpoons", nil
	}
	return "", parserError("MhchemBugT", "mhchem bug T. Please report.")
}

func getBond( string) (string, bool) {
	switch  {
	case "-", "1":
		return "{-}", true
	case "=", "2":
		return "{=}", true
	case "#", "3":
		return "{\\equiv}", true
	case "~":
		return "{\\tripledash}", true
	case "~-":
		return "{\\rlap{\\lower.1em{-}}\\raise.1em{\\tripledash}}", true
	case "~=", "~--":
		return "{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{\\tripledash}}-}", true
	case "-~-":
		return "{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{-}}\\tripledash}", true
	case "...":
		return "{{\\cdot}{\\cdot}{\\cdot}}", true
	case "....":
		return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}", true
	case "->":
		return "{\\rightarrow}", true
	case "<-":
		return "{\\leftarrow}", true
	case "<":
		return "{<}", true
	case ">":
		return "{>}", true
	}
	return "", false
}

func getOperator( string) (string, bool) {
	switch  {
	case "+":
		return " {}+{} ", true
	case "-":
		return " {}-{} ", true
	case "=":
		return " {}={} ", true
	case "<":
		return " {}<{} ", true
	case ">":
		return " {}>{} ", true
	case "<<":
		return " {}\\ll{} ", true
	case ">>":
		return " {}\\gg{} ", true
	case "\\pm":
		return " {}\\pm{} ", true
	case "\\approx", "$\\approx$":
		return " {}\\approx{} ", true
	case "v", "(v)":
		return " \\downarrow{} ", true
	case "^", "(^)":
		return " \\uparrow{} ", true
	}
	return "", false
}