// Copyright 2018-2022 The MathJax Consortium
// SPDX-License-Identifier: Apache-2.0
// This file is a Go translation and modification of MathJax 3.2.2.
//
// Source: ts/output/common/Wrappers/menclose.ts and
// ts/output/svg/Wrappers/menclose.ts.

package svg

import (
	
	
	
	

	
	
)

const (
	encloseArrowX  = 4.0
	encloseArrowDX = 1.0
	encloseArrowY  = 2.0
	encloseRule    = .067
	enclosePadding = .2
)

type encloseTRBL [4]float64

type encloseLayout struct {
	w         *wrapper
	padding   float64
	thickness float64
	arrowX    float64
	arrowY    float64
	arrowDX   float64
	notations []string
	active    map[string]bool
	trbl      encloseTRBL
	msqrt     *wrapper
}

var encloseFloatPrefix = regexp.MustCompile(`^[+-]?(?:Infinity|(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)`)

func newEncloseLayout( *wrapper) *encloseLayout {
	 := &encloseLayout{
		w: , padding: enclosePadding, thickness: encloseRule,
		arrowX: encloseArrowX, arrowY: encloseArrowY, arrowDX: encloseArrowDX,
		active: make(map[string]bool),
	}
	.getParameters()
	.getNotations()
	.removeRedundantNotations()
	if .active["radical"] {
		.msqrt = .createMsqrt()
	}
	.trbl = .getBBoxExtenders()
	return 
}

func ( *encloseLayout) () {
	if ,  := .w.node.Attributes.Get("data-padding");  {
		.padding = layout.Length2Em(, enclosePadding, .w.bbox.Scale, .w.renderer.pxPerEm)
	}
	if ,  := .w.node.Attributes.Get("data-thickness");  {
		.thickness = layout.Length2Em(, encloseRule, .w.bbox.Scale, .w.renderer.pxPerEm)
	}
	if ,  := .w.node.Attributes.Get("data-arrowhead");  {
		 := strings.Fields(encloseStringValue())
		.arrowX = encloseArrowPart(, 0, encloseArrowX)
		.arrowY = encloseArrowPart(, 1, encloseArrowY)
		.arrowDX = encloseArrowPart(, 2, encloseArrowDX)
	}
}

func encloseStringValue( any) string {
	if  == nil {
		return ""
	}
	if ,  := .(string);  {
		return 
	}
	return strconv.FormatFloat(encloseNumberValue(), 'g', -1, 64)
}

func encloseNumberValue( any) float64 {
	switch value := .(type) {
	case float64:
		return 
	case float32:
		return float64()
	case int:
		return float64()
	case int64:
		return float64()
	case string:
		return encloseParseFloat()
	default:
		return math.NaN()
	}
}

func encloseArrowPart( []string,  int,  float64) float64 {
	if  >= len() || [] == "" {
		return 
	}
	return encloseParseFloat([])
}

func encloseParseFloat( string) float64 {
	 := encloseFloatPrefix.FindString(strings.TrimSpace())
	if  == "" {
		return math.NaN()
	}
	,  := strconv.ParseFloat(, 64)
	if  != nil {
		return math.NaN()
	}
	return 
}

func ( *encloseLayout) () {
	 := stringAttribute(.w.node, "notation", "longdiv")
	for ,  := range strings.Fields() {
		if !encloseNotationSupported() || .active[] {
			continue
		}
		.active[] = true
		.notations = append(.notations, )
	}
}

func ( *encloseLayout) () {
	for ,  := range .notations {
		if !.active[] {
			continue
		}
		for ,  := range strings.Fields(encloseNotationRemove()) {
			delete(.active, )
		}
	}
}

func ( *encloseLayout) () []string {
	 := make([]string, 0, len(.notations))
	for ,  := range .notations {
		if .active[] {
			 = append(, )
		}
	}
	return 
}

func ( *encloseLayout) () encloseTRBL {
	var  encloseTRBL
	for ,  := range .activeNotations() {
		maximizeEnclose(&, .notationBBox())
	}
	return 
}

func ( *encloseLayout) () encloseTRBL {
	var  encloseTRBL
	for ,  := range .activeNotations() {
		maximizeEnclose(&, .notationBorder())
	}
	 := .trbl
	for  := range  {
		[] -= []
	}
	return 
}

func maximizeEnclose( *encloseTRBL,  encloseTRBL) {
	for ,  := range  {
		if (*)[] <  {
			(*)[] = 
		}
	}
}

func ( *encloseLayout) ( string) float64 {
	 := (.trbl[2] - .trbl[0]) / 2
	if  == "X" {
		 = (.trbl[1] - .trbl[3]) / 2
	}
	if math.Abs() <= .001 {
		return 0
	}
	return 
}

func encloseArgMod(,  float64) (float64, float64) {
	return math.Atan2(, ), math.Sqrt(* + *)
}

func ( *encloseLayout) () (, , ,  float64) {
	 := .thickness * (.arrowX + math.Max(1, .arrowDX))
	 := .childBBox()
	 := .H + .D
	 := math.Sqrt(* + .W*.W)
	 = math.Max(.padding, *.W/)
	 = math.Max(.padding, */)
	,  = encloseArgMod(.W+2*, +2*)
	return
}

func ( *encloseLayout) () (float64, float64) {
	 := .childBBox()
	return encloseArgMod(
		.trbl[3]+.W+.trbl[1],
		.trbl[0]+.H+.D+.trbl[2],
	)
}

func ( *encloseLayout) () *layout.BBox {
	if len(.w.children) == 0 {
		return layout.ZeroBBox()
	}
	return .w.children[0].getBBox()
}

func ( *encloseLayout) () *wrapper {
	if len(.w.node.Children) == 0 {
		return nil
	}
	 := mml.NewNode("msqrt", nil, nil, .w.node.Children[0].Clone())
	.TeXClass = mml.TeXClassOrd
	.Attributes.SetInherited("displaystyle", .w.displayStyle)
	.Attributes.SetInherited("scriptlevel", .w.scriptLevel)
	if ,  := .w.node.Attributes.Get("mathsize");  && .w.node.Attributes.IsSet("mathsize") {
		.Attributes.SetInherited("mathsize", )
	}
	return .w.renderer.wrap(, .w, .w.scriptLevel, .w.displayStyle)
}

func ( *encloseLayout) () encloseTRBL {
	if .msqrt == nil || len(.msqrt.children) == 0 {
		return encloseTRBL{}
	}
	 := .msqrt.getBBox()
	 := .msqrt.children[0].getBBox()
	return encloseTRBL{.H - .H, 0, .D - .D, .W - .W}
}

// computeEncloseBBox ports CommonMencloseMixin.computeBBox().  It is named for
// the central wrapper dispatcher and deliberately keeps all notation state
// local so wrappers remain immutable and safe for concurrent renders.
func ( *wrapper) ( *layout.BBox) {
	 := newEncloseLayout()
	// CommonMenclose combines into the supplied box: new and temporary
	// boxes start at zero, while invalidation retains cached dimensions.
	// Emptying it would lose the baseline for negative child height/depth.
	.Combine(.childBBox(), .trbl[3], 0)
	.H += .trbl[0]
	.D += .trbl[2]
	.W += .trbl[1]
	// CommonMenclose resolves percentages here, while this box is still
	// uncached. Deferring until SVG emission can invalidate and enlarge the
	// enclosure a second time when its child resolves to a different width.
	.bbox.PWidth = ""
	for ,  := range .children {
		if  := .outerBBox(); .PWidth != "" {
			.setChildPWidths(.W, true)
		}
	}
}

// encloseToSVG ports SVGmenclose.toSVG().
func ( *wrapper) ( *Element) {
	if !.bboxComputed {
		.computeEncloseBBox(.bbox)
		.bboxComputed = true
	}
	 := newEncloseLayout()
	 := .standardSVG()
	 := NewElement("g")
	if  := .getBBoxExtenders()[3];  > 0 {
		.SetAttr("transform", "translate("+fixed()+", 0)")
	}
	.Append()
	if .active["radical"] {
		.renderRadical()
	} else if len(.children) != 0 {
		.children[0].toSVG()
	}
	for ,  := range .activeNotations() {
		if  != "radical" {
			.renderNotation(, )
		}
	}
}

func ( *encloseLayout) ( *Element) {
	if .msqrt == nil {
		return
	}
	.msqrt.rootToSVG()
	.w.place(-.sqrtTRBL()[3], 0, )
}