// Copyright 2017-2022 The MathJax Consortium
// SPDX-License-Identifier: Apache-2.0
// This file is a Go translation and modification of MathJax 3.2.2.

package svg

import (
	

	
	
)

// defaultAccentRemap is FontData.defaultAccentMap from MathJax 3.2.2.
// CommonMo applies it only to a single-character accent token outside an
// mrow.  Several entries deliberately expand to more than one character.
var defaultAccentRemap = map[rune]string{
	0x0300: "\u02CB", 0x0301: "\u02CA", 0x0302: "\u02C6", 0x0303: "\u02DC",
	0x0304: "\u02C9", 0x0306: "\u02D8", 0x0307: "\u02D9", 0x0308: "\u00A8",
	0x030A: "\u02DA", 0x030C: "\u02C7", 0x2192: "\u20D7",
	0x2032: "'", 0x2033: "''", 0x2034: "'''", 0x2035: "`",
	0x2036: "``", 0x2037: "```", 0x2057: "''''",
	0x20D0: "\u21BC", 0x20D1: "\u21C0", 0x20D6: "\u2190", 0x20E1: "\u2194",
	0x20F0: "*", 0x20DB: "...", 0x20DC: "....",
	0x20EC: "\u21C1", 0x20ED: "\u21BD", 0x20EE: "\u2190", 0x20EF: "\u2192",
}

func accentCoreMO( *wrapper) *wrapper {
	for  != nil && .node.Kind != "mo" && .node.Flags.Embellished {
		 := .node.Flags.CoreIndex
		if  < 0 ||  >= len(.children) {
			return nil
		}
		 = .children[]
	}
	if  != nil && .node.Kind == "mo" {
		return 
	}
	return nil
}

func ( *wrapper) () bool {
	if  == nil || .node.Kind != "mo" {
		return false
	}
	// MmlMo.isAccent is separate from the internal mathaccent positioning
	// marker. Follow coreParent through embellished nodes, stopping at math.
	 := 
	for .parent != nil && .parent.node.Kind != "math" &&
		.parent.node.Flags.Embellished && accentCoreMO(.parent) ==  {
		 = .parent
	}
	 := .parent
	if  == nil {
		return false
	}
	 := ""
	switch .node.Kind {
	case "mover":
		if .overChild() != nil {
			 = "accent"
		}
	case "munder":
		if .underChild() != nil {
			 = "accentunder"
		}
	case "munderover":
		if accentCoreMO(.overChild()) ==  {
			 = "accent"
		} else if accentCoreMO(.underChild()) ==  {
			 = "accentunder"
		}
	}
	if  == "" {
		return false
	}
	// The pinned getter leaves isAccent false when the stack has an explicit
	// accent attribute; otherwise it uses this operator's resolved accent.
	if ,  := .node.Attributes.GetExplicit();  {
		return false
	}
	return boolAttributeDefault(.node, "accent", false)
}

func remapAccentText( *wrapper,  string) string {
	// CommonTextNode uses an explicit stretch.c directly. A selected schar
	// also becomes stretch.c; neither may be remapped back to an accent.
	if  != nil && .hasStretch {
		 := .stretch.HasAlias && .stretch.Alias != 0
		if .sizeSet && .size >= 0 && .size < len(.stretch.SizeChars) {
			 =  || .stretch.SizeChars[.size] != 0
		}
		if  {
			return 
		}
	}
	 := []rune()
	if len() != 1 ||  == nil || !.isAccentMO() {
		return 
	}
	if ,  := defaultAccentRemap[[0]];  {
		return 
	}
	return 
}

func ( *wrapper) ( font.Direction) bool {
	// CommonMfrac keeps its numerator's embellished identity for MathML
	// semantics, but its wrapper explicitly refuses both stretch directions.
	if .node.Kind == "mfrac" {
		return false
	}
	if .node.Kind != "mo" {
		// CommonWrapper.canStretch resets its own wrapper-level stretch state,
		// then delegates through the core child of an embellished wrapper.  The
		// wrapper state is observable when a table later asks a mover/mrow to
		// stretch after its core mo was already sized by an earlier layout pass.
		.stretch = font.Delimiter{}
		.hasStretch = false
		if !.node.Flags.Embellished {
			return false
		}
		 := .node.Flags.CoreIndex
		if  < 0 ||  >= len(.children) {
			return false
		}
		 := .children[]
		if  == nil ||  ==  || !.() {
			return false
		}
		.stretch = .stretch
		.hasStretch = true
		return true
	}
	if .hasStretch {
		return .stretch.Direction == 
	}
	if !boolAttributeDefault(.node, "stretchy", false) {
		return false
	}
	 := []rune(nodeText(.node))
	if len() != 1 {
		return false
	}
	,  := font.LookupDelimiter([0])
	if ! || .Direction !=  {
		return false
	}
	.stretch = 
	.hasStretch = true
	.stretchGlyph = [0]
	return true
}

func ( *wrapper) ( []float64) float64 {
	if len() == 0 {
		return 0
	}
	if len() == 1 {
		return [0]
	}
	,  := [0], [1]
	if boolAttributeDefault(.node, "symmetric", false) {
		 := .renderer.params.Axis
		return 2 * math.Max(-, +)
	}
	return  + 
}

func ( *wrapper) ( string,  float64) float64 {
	if .node.Attributes.IsSet() {
		return layout.Length2Em(attribute(.node, , ), 1, 1, .renderer.pxPerEm)
	}
	return 
}

func ( *wrapper) ( []float64,  bool) {
	if !.hasStretch {
		return
	}
	 := .stretchDimension()
	 := .delimiterSize("minsize", 0)
	 := .delimiterSize("maxsize", math.Inf(1))
	 = math.Max(, math.Min(, ))
	 := .renderer.params.DelimiterFactor / 1000
	 := .renderer.params.DelimiterShortfall
	 := 
	,  := .node.Property("mathaccent")
	if  == 0 && ! {
		if  {
			 = math.Min(/, +)
		} else {
			 = math.Max(*, -)
		}
	}
	for  := range .stretch.Sizes {
		if .stretch.Sizes[] <  {
			continue
		}
		if  &&  != 0 {
			--
		}
		if ,  := .stretch.SizeVariant();  {
			.variant = 
		}
		.size, .sizeSet = , true
		if  < len(.stretch.SizeChars) && .stretch.SizeChars[] != 0 {
			.stretchGlyph = .stretch.SizeChars[]
		} else if .stretch.HasAlias {
			.stretchGlyph = .stretch.Alias
		}
		return
	}
	if len(.stretch.Stretch) != 0 {
		.size, .sizeSet = -1, true
		.invalidateBBox()
		.setStretchBBox(, .extendedHeight())
		return
	}
	if len(.stretch.Sizes) != 0 {
		 := len(.stretch.Sizes) - 1
		if ,  := .stretch.SizeVariant();  {
			.variant = 
		}
		.size, .sizeSet = , true
		// CommonMo retains delim.c when falling back to the largest fixed size.
		if .stretch.HasAlias {
			.stretchGlyph = .stretch.Alias
		}
	}
}

func ( *wrapper) ( float64) float64 {
	if .stretch.HasFullExt {
		,  := .stretch.FullExt[0], .stretch.FullExt[1]
		 := math.Ceil(math.Max(0, -) / )
		return  + *
	}
	return 
}

func ( *wrapper) ( []float64,  float64) {
	if .stretch.HasMin && .stretch.Min >  {
		 = .stretch.Min
	}
	, ,  := .stretch.HDW[0], .stretch.HDW[1], .stretch.HDW[2]
	if .stretch.Direction == font.DirectionVertical {
		,  = .stretchBaseline(, )
	} else {
		 = 
	}
	.bbox.H, .bbox.D, .bbox.W = , , 
	.bboxComputed = true
}

func ( *wrapper) ( []float64,  float64) (float64, float64) {
	 := len() == 2 && [0]+[1] == 
	,  := , 0.0
	if boolAttributeDefault(.node, "symmetric", false) {
		 := .renderer.params.Axis
		if  {
			 = 2 * math.Max([0]-, [1]+)
		}
		 = /2 - 
	} else if  {
		 = [1]
	} else {
		,  := .75, .25
		if .stretch.HasHDW {
			,  = .stretch.HDW[0], .stretch.HDW[1]
		}
		 =  *  / ( + )
	}
	return  - , 
}

func ( *wrapper) () {
	// CommonWrapper invalidates only cached boxes and their ancestors. Text
	// children retain their measured advance when an operator changes size.
	if !.bboxComputed {
		return
	}
	.bboxComputed = false
	if .parent != nil {
		.parent.()
	}
}

func ( *wrapper) ( *layout.BBox) {
	if .hasStretch && !.sizeSet {
		.getStretchedVariant([]float64{0}, false)
	}
	if !(.hasStretch && .sizeSet && .size < 0) {
		.computeChildrenBBox()
		.copySkewIC()
	}
	if boolAttributeDefault(.node, "symmetric", false) && (!.hasStretch || .stretch.Direction != font.DirectionHorizontal) {
		 := .centerOffset()
		.H += 
		.D -= 
	}
	if ,  := .node.Property("mathaccent");  && (!.hasStretch || .size >= 0) {
		.W = 0
	}
}

func ( *wrapper) ( *layout.BBox) float64 {
	return (.H+.D)/2 + .renderer.params.Axis - .H
}

func ( *wrapper) ( *Element) {
	 := .standardSVG()
	if .hasStretch && !.sizeSet {
		.getStretchedVariant(nil, false)
	}
	if .hasStretch && .sizeSet && .size < 0 {
		.stretchToSVG()
		return
	}
	,  := 0.0, 0.0
	if boolAttributeDefault(.node, "symmetric", false) || boolAttributeDefault(.node, "largeop", false) {
		 := layout.EmptyBBox()
		.computeChildrenBBox()
		 = .centerOffset()
	}
	if ,  := .node.Property("mathaccent");  {
		 := layout.EmptyBBox()
		.computeChildrenBBox()
		// CommonMo.getAccentOffset uses protoBBox, including italic correction.
		.copySkewIC()
		 = -.W / 2
	}
	if  != 0 ||  != 0 {
		.SetAttr("transform", "translate("+fixed()+" "+fixed()+")")
	}
	.addChildren()
}

func ( *wrapper) ( *Element) {
	if .stretch.Direction == font.DirectionVertical {
		.stretchVertical()
	} else {
		.stretchHorizontal()
	}
}

// CommonMrow asks the outer row child whether it stretches. Embellished
// wrappers propagate their core's stretch state and must be excluded from the
// non-stretchy height sample just like a direct mo. Only the final resize is
// delivered to the core mo.
func ( *wrapper) () {
	var  []*wrapper
	for ,  := range .children {
		if .canStretch(font.DirectionVertical) {
			 = append(, )
		}
	}
	if len() == 0 || len(.children) <= 1 {
		return
	}
	,  := 0.0, 0.0
	 := len() > 1 && len() == len(.children)
	for ,  := range .children {
		if ! && .hasStretch {
			continue
		}
		 := .styledOuterBBoxWithSave(!.hasStretch)
		 = math.Max(, .H*.RScale)
		 = math.Max(, .D*.RScale)
	}
	for ,  := range  {
		if  := tableCoreMO();  != nil {
			.getStretchedVariant([]float64{, }, false)
		}
	}
}