// Copyright (c) 2009-2022 The MathJax Consortium
// SPDX-License-Identifier: Apache-2.0
//
// This file is a Go translation and modification of MathJax 3.2.2.
// Sources: ts/input/tex/{NodeFactory,NodeUtil}.ts and
// ts/core/MmlTree/{MmlNode,OperatorDictionary}.ts;
// MmlNodes/{TeXAtom,maction,math,menclose,merror,mfenced,mglyph,mi,
// mmultiscripts,mn,mo,mpadded,mphantom,mroot,mrow,ms,mspace,msqrt,mstyle,
// msubsup,mtable,mtd,mtext,munderover}.ts.

package tex

import (
	
)

func node( string,  ...*mml.Node) *mml.Node {
	if hasInferredMrow() {
		// Negative-arity constructors create their own inferred content row,
		// then append the supplied children through that unbounded row. Even
		// one supplied inferred row is flattened, never reused as the owner.
		 = []*mml.Node{forcedRow(, true)}
	}
	if ,  := texMMLFactory.Definition();  && .Flags.Arity == mmlUnboundedArity {
		// AbstractMmlNode.appendChild appends an inferred child's contents
		// directly into an unbounded parent. Preserve their identities/order
		// and the caller's slice; the factory links their new parent below.
		 := make([]*mml.Node, 0, len())
		for ,  := range  {
			if  != nil && .Flags.Inferred {
				 = append(, .Children...)
			} else {
				 = append(, )
			}
		}
		 = 
	}
	// AbstractMmlNode.appendChild converts an inferred mrow to an explicit
	// mrow when it is inserted into a fixed-arity node (mfrac, mroot,
	// scripts, ...).  This is observable in SVG: inferred rows borrow their
	// parent's element, while explicit rows own the transform that places a
	// multi-token numerator or denominator.
	if ,  := texMMLFactory.Definition();  && .Flags.Arity >= 0 && .Flags.Arity != mmlUnboundedArity {
		for ,  := range  {
			if  == nil || !.Flags.Inferred {
				continue
			}
			 := texMMLFactory.Create("mrow", .Children...)
			.Attributes = .Attributes.Clone()
			.Properties = .Properties.Clone()
			.TeXClass = .TeXClass
			.PrevClass = .PrevClass
			.PrevLevel = .PrevLevel
			refreshDynamicFlags()
			[] = 
		}
	}
	 := texMMLFactory.Create(, ...)
	switch  {
	case "TeXAtom":
		// TeXAtom initializes its actual class to ORD and records a separate
		// serialization property. Changing that property does not set the field.
		.TeXClass = mml.TeXClassOrd
		.SetProperty("texClass", mml.TeXClassOrd)
	case "mi", "mn", "mtext", "ms", "mglyph", "msqrt", "mroot", "merror", "mphantom", "menclose", "mtable":
		.TeXClass = mml.TeXClassOrd
	case "mfenced":
		.TeXClass = mml.TeXClassInner
	}
	if  == "mtable" {
		.SetProperty("useHeight", true)
	} else if  == "annotation" {
		.SetProperty("isChars", true)
	}
	refreshDynamicFlags()
	return 
}

// MathJax's MML classes insert an inferred mrow for the variable-arity
// content models below. Keeping it in the Go tree is observable through
// MmlNode.toString(), inherited attributes, and wrapper construction.
func hasInferredMrow( string) bool {
	switch  {
	case "math", "TeXAtom", "mstyle", "merror", "mpadded", "mphantom", "menclose", "mtd", "msqrt":
		return true
	}
	return false
}

func token(,  string) *mml.Node {
	 := node(, mml.NewText())
	.SetProperty(vectorFactoryToken, true)
	.Flags.Token = true
	switch  {
	case "mi", "mn", "mtext", "ms":
		.TeXClass = mml.TeXClassOrd
	case "mo":
		.Flags.Embellished = true
		.TeXClass = operatorClass()
	}
	return 
}

func operator( string,  mml.TeXClass,  map[string]any) *mml.Node {
	 := token("mo", )
	.TeXClass = 
	// Parser-created operators pass a TeX class explicitly through
	// NodeUtil.setProperties.  Preserve that distinction so MmlMo's dynamic
	// dictionary lookup does not overwrite it during inherited-attribute setup.
	.SetProperty("texClass", )
	for ,  := range  {
		.Attributes.Set(, )
	}
	return 
}

func row( []*mml.Node,  bool) *mml.Node {
	if len() == 1 {
		return [0]
	}
	 := node("mrow", ...)
	.Flags.Inferred = 
	.Flags.NotParent = 
	return 
}

func forcedRow( []*mml.Node,  bool) *mml.Node {
	 := node("mrow", ...)
	.Flags.Inferred = 
	.Flags.NotParent = 
	return 
}

func texAtom( *mml.Node,  mml.TeXClass) *mml.Node {
	 := node("TeXAtom", )
	.TeXClass = 
	.SetProperty("texClass", )
	return 
}

func setAttributes( *mml.Node,  map[string]any) *mml.Node {
	for ,  := range  {
		.Attributes.Set(, )
	}
	return 
}

func textContent( *mml.Node) string {
	if  == nil {
		return ""
	}
	if .Kind == "text" {
		return .Text
	}
	var  string
	for ,  := range .Children {
		 += ()
	}
	return 
}

// refreshDynamicFlags evaluates the MML class getters that depend on children
// or attributes.  The shared tree stores their current values explicitly so
// SVG wrappers do not need a parallel class hierarchy.
func refreshDynamicFlags( *mml.Node) {
	if  == nil {
		return
	}
	switch .Kind {
	case "mrow":
		.Flags.Spacelike = true
		.Flags.CoreIndex = 0
		 := -1
		 := true
		for ,  := range .Children {
			if  == nil {
				continue
			}
			if !.Flags.Spacelike {
				.Flags.Spacelike = false
			}
			if .Flags.Embellished {
				if  >= 0 {
					 = false
				}
				 = 
			} else if !.Flags.Spacelike {
				 = false
			}
		}
		.Flags.Embellished =  &&  >= 0
		if .Flags.Embellished {
			.Flags.CoreIndex = 
		}

	case "math", "mstyle", "mpadded", "mphantom":
		if len(.Children) != 0 && .Children[0] != nil {
			.Flags.Spacelike = .Children[0].Flags.Spacelike
			.Flags.Embellished = .Children[0].Flags.Embellished
			.Flags.CoreIndex = 0
		}
		if .Kind == "mstyle" {
			.Flags.NotParent = hasSingleInferredChild()
		}

	case "TeXAtom":
		if len(.Children) != 0 && .Children[0] != nil {
			.Flags.Embellished = .Children[0].Flags.Embellished
			.Flags.CoreIndex = 0
		}
		.Flags.NotParent = hasSingleInferredChild()

	case "mfrac", "msub", "msup", "msubsup", "munder", "mover", "munderover", "mmultiscripts", "mtd":
		if len(.Children) != 0 && .Children[0] != nil {
			.Flags.Embellished = .Children[0].Flags.Embellished
			.Flags.CoreIndex = 0
		}

	case "maction":
		if len(.Children) != 0 && .Children[0] != nil {
			.Flags.Embellished = .Children[0].Flags.Embellished
			.Flags.Spacelike = .Children[0].Flags.Spacelike
			.Flags.CoreIndex = 0
		}

	case "mo":
		,  := .Attributes.Get("linebreak")
		.Flags.HasNewline =  == "newline"

	case "mspace":
		,  := .Attributes.Get("linebreak")
		,  := .Attributes.GetExplicit("width")
		,  := .Attributes.GetExplicit("height")
		,  := .Attributes.GetExplicit("depth")
		.Flags.HasNewline = ! && ! && ! &&  == "newline"
	}
}

func hasSingleInferredChild( *mml.Node) bool {
	return len(.Children) != 0 && .Children[0] != nil && .Children[0].Flags.Inferred && len(.Children[0].Children) == 1
}

func operatorClass( string) mml.TeXClass {
	switch  {
	case "(", "[", "{", "⟨", "⌈", "⌊", "|", "‖":
		return mml.TeXClassOpen
	case ")", "]", "}", "⟩", "⌉", "⌋":
		return mml.TeXClassClose
	case ",", ";":
		return mml.TeXClassPunct
	case "=", "<", ">", "≤", "≥", "≠", "≈", "∼", "∈", "∉", "⊂", "⊃", "⊆", "⊇", "←", "→", "↔", "⇐", "⇒", "⇔", "⟶":
		return mml.TeXClassRel
	case "+", "−", "±", "∓", "×", "÷", "⋅", "∗", "∪", "∩", "⊕", "⊗", "∧", "∨":
		return mml.TeXClassBin
	case "∑", "∏", "∫", "∬", "∭", "∐", "⋃", "⋂":
		return mml.TeXClassOp
	}
	return mml.TeXClassOrd
}