package d2talalayout

import (
	
	
	
	
	
	

	
	
	
	
)

type d2ObjectVisit struct {
	object *d2graph.Object
	parent *d2graph.Object
	depth  int
}

// collectD2ObjectVisits validates the object tree and returns its non-root
// objects in the same depth-first pre-order used by D2. Keeping traversal
// iterative makes malformed or deeply nested caller-built graphs an ordinary
// input error instead of a process-threatening stack overflow.
func collectD2ObjectVisits( context.Context,  *d2graph.Object) ([]d2ObjectVisit, map[*d2graph.Object]struct{}, error) {
	if  == nil {
		return nil, nil, fmt.Errorf("tala requires a D2 graph with a root object")
	}

	 := []d2ObjectVisit{{object: }}
	 := make(map[*d2graph.Object]*d2graph.Object)
	 := make(map[*d2graph.Object]struct{})
	 := make([]d2ObjectVisit, 0, 64)
	 := 0

	for len() > 0 {
		if  := .Err();  != nil {
			return nil, nil, 
		}
		 := [len()-1]
		 = [:len()-1]
		if .object == nil {
			return nil, nil, fmt.Errorf("D2 object tree contains a nil object at depth %d", .depth)
		}

		if ,  := [.object];  {
			if d2ObjectIsAncestor(.object, .parent, ) {
				return nil, nil, fmt.Errorf("D2 object %q forms a ChildrenArray cycle", .object.ID)
			}
			return nil, nil, fmt.Errorf("D2 object %q is referenced more than once in ChildrenArray", .object.ID)
		}
		if .object.Parent != .parent {
			return nil, nil, fmt.Errorf(
				"D2 object %q has a Parent that does not match its ChildrenArray parent",
				.object.ID,
			)
		}
		if .depth > maxInputTreeDepth {
			return nil, nil, fmt.Errorf("D2 object nesting depth exceeds limit %d", maxInputTreeDepth)
		}
		[.object] = .parent

		if .parent != nil {
			if len() == maxInputNodes {
				return nil, nil, fmt.Errorf("D2 object count exceeds limit %d", maxInputNodes)
			}
			 = append(, )
			[.object] = struct{}{}
		}

		 := .object.ChildrenArray
		if len() > maxInputNodes- {
			return nil, nil, fmt.Errorf("D2 object count exceeds limit %d", maxInputNodes)
		}
		 += len()
		for ,  := range slices.Backward() {
			 = append(, d2ObjectVisit{
				object: ,
				parent: .object,
				depth:  .depth + 1,
			})
		}
	}

	return , , nil
}

func d2ObjectIsAncestor(,  *d2graph.Object,  map[*d2graph.Object]*d2graph.Object) bool {
	for  != nil {
		if  ==  {
			return true
		}
		 = []
	}
	return false
}

func validateD2Objects( context.Context,  []d2ObjectVisit,  bool) error {
	 := 0
	for ,  := range  {
		if  := .Err();  != nil {
			return 
		}
		 := .object
		if .Box == nil {
			return fmt.Errorf("D2 object %d (%q) has no box", , .ID)
		}
		if  && .TopLeft == nil {
			return fmt.Errorf("D2 object %q has no top-left position", .ID)
		}
		if  := validateResultDimension("width", .Width, false);  != nil {
			return fmt.Errorf("D2 object %q: %w", .ID, )
		}
		if  := validateResultDimension("height", .Height, false);  != nil {
			return fmt.Errorf("D2 object %q: %w", .ID, )
		}
		if .TopLeft != nil {
			if  := validateResultCoordinate("top-left x", .TopLeft.X);  != nil {
				return fmt.Errorf("D2 object %q: %w", .ID, )
			}
			if  := validateResultCoordinate("top-left y", .TopLeft.Y);  != nil {
				return fmt.Errorf("D2 object %q: %w", .ID, )
			}
			if  := validateResultCoordinate("bottom-right x", .TopLeft.X+.Width);  != nil {
				return fmt.Errorf("D2 object %q: %w", .ID, )
			}
			if  := validateResultCoordinate("bottom-right y", .TopLeft.Y+.Height);  != nil {
				return fmt.Errorf("D2 object %q: %w", .ID, )
			}
		}
		if  := validateD2TextDimensions("label", .LabelDimensions);  != nil {
			return fmt.Errorf("D2 object %q: %w", .ID, )
		}
		if  := validateD2Style("D2 object "+fmt.Sprintf("%q", .ID), .Style);  != nil {
			return 
		}
		if .LabelPosition != nil && !label.FromString(*.LabelPosition).IsShapePosition() {
			return fmt.Errorf("D2 object %q has invalid label position %q", .ID, *.LabelPosition)
		}
		if .IconPosition != nil && !label.FromString(*.IconPosition).IsShapePosition() {
			return fmt.Errorf("D2 object %q has invalid icon position %q", .ID, *.IconPosition)
		}
		if (.Top == nil) != (.Left == nil) {
			return fmt.Errorf("D2 object %q must set top and left together for a locked position", .ID)
		}
		if .Top != nil {
			if ,  := parseD2IntegerAttribute(.ID, "top", .Top, -maxResultCoordinate, maxResultCoordinate);  != nil {
				return 
			}
		}
		if .Left != nil {
			if ,  := parseD2IntegerAttribute(.ID, "left", .Left, -maxResultCoordinate, maxResultCoordinate);  != nil {
				return 
			}
		}
		if .WidthAttr != nil {
			if ,  := parseD2IntegerAttribute(.ID, "width", .WidthAttr, 1, maxResultCoordinate);  != nil {
				return 
			}
		}
		if .HeightAttr != nil {
			if ,  := parseD2IntegerAttribute(.ID, "height", .HeightAttr, 1, maxResultCoordinate);  != nil {
				return 
			}
		}

		 := .Text().FontSize
		if .Class != nil || .SQLTable != nil {
			 -= d2target.HeaderFontAdd
		}
		if  <= 0 ||  > maxResultCoordinate {
			return fmt.Errorf("D2 object %q has unsupported effective font size %d", .ID, )
		}

		if .SQLTable != nil {
			if .Shape.Value != d2target.ShapeSQLTable {
				return fmt.Errorf("D2 object %q has SQL columns but is not an SQL table", .ID)
			}
			if len(.SQLTable.Columns) > maxInputNodes- {
				return fmt.Errorf("D2 SQL table column count exceeds limit %d", maxInputNodes)
			}
			 += len(.SQLTable.Columns)
		}
	}
	return nil
}

func validateD2Style( string,  d2graph.Style) error {
	if .Opacity != nil {
		,  := strconv.ParseFloat(.Opacity.Value, 64)
		if  != nil || math.IsNaN() || math.IsInf(, 0) ||  < 0 ||  > 1 {
			return fmt.Errorf("%s has invalid opacity %q", , .Opacity.Value)
		}
	}
	for ,  := range []struct {
		  string
		 *d2graph.Scalar
		   int
	}{
		{: "stroke-width", : .StrokeWidth, : 0},
		{: "font-size", : .FontSize, : 1},
	} {
		if . == nil {
			continue
		}
		,  := strconv.Atoi(..Value)
		if  != nil ||  < . ||  > maxResultCoordinate {
			return fmt.Errorf("%s has invalid %s %q", , ., ..Value)
		}
	}
	for ,  := range []struct {
		  string
		 *d2graph.Scalar
	}{
		{: "animated", : .Animated},
		{: "bold", : .Bold},
		{: "double-border", : .DoubleBorder},
		{: "filled", : .Filled},
		{: "italic", : .Italic},
		{: "multiple", : .Multiple},
		{: "shadow", : .Shadow},
		{: "3d", : .ThreeDee},
		{: "underline", : .Underline},
	} {
		if . == nil {
			continue
		}
		if ,  := strconv.ParseBool(..Value);  != nil {
			return fmt.Errorf("%s has invalid %s boolean %q", , ., ..Value)
		}
	}
	return nil
}

func parseD2IntegerAttribute(,  string,  *d2graph.Scalar, ,  int) (int, error) {
	,  := strconv.Atoi(.Value)
	if  != nil {
		return 0, fmt.Errorf("D2 object %q has invalid integer %s attribute %q", , , .Value)
	}
	if  <  ||  >  {
		return 0, fmt.Errorf(
			"D2 object %q %s attribute must be between %d and %d",
			,
			,
			,
			,
		)
	}
	return , nil
}

func validateD2TextDimensions( string,  d2target.TextDimensions) error {
	if .Width < 0 || .Height < 0 {
		return fmt.Errorf("%s dimensions must be nonnegative", )
	}
	if .Width > maxResultCoordinate || .Height > maxResultCoordinate {
		return fmt.Errorf("%s dimensions exceed supported limit %d", , maxResultCoordinate)
	}
	return nil
}

func findD2Object( context.Context,  *d2graph.Object,  []string) (*d2graph.Object, bool, error) {
	if len() > maxInputTreeDepth {
		return nil, false, fmt.Errorf("D2 near key depth exceeds limit %d", maxInputTreeDepth)
	}
	 := 
	for ,  := range  {
		if  := .Err();  != nil {
			return nil, false, 
		}
		if  == nil {
			return nil, false, fmt.Errorf("D2 near key traverses a nil object before %q", )
		}
		,  := .Children[strings.ToLower()]
		if ! {
			return nil, false, nil
		}
		if  == nil {
			return nil, false, fmt.Errorf("D2 object %q has a nil child for near key part %q", .ID, )
		}
		 = 
	}
	return , true, nil
}

// validateRequestedD2Edges bounds subset validation before allocating maps or
// translating the graph. Every requested edge must be a distinct member of g.
func validateRequestedD2Edges( *d2graph.Graph,  []*d2graph.Edge) error {
	if len() > maxInputEdges {
		return fmt.Errorf("requested D2 edge count exceeds limit %d", maxInputEdges)
	}
	 := make(map[*d2graph.Edge]struct{}, len())
	for ,  := range  {
		if  == nil {
			return fmt.Errorf("requested D2 edge %d is nil", )
		}
		if ,  := [];  {
			return fmt.Errorf("requested D2 edge %d is repeated", )
		}
		[] = struct{}{}
	}
	if  == nil {
		return nil
	}
	if len(.Edges) > maxInputEdges {
		return fmt.Errorf("D2 edge count exceeds limit %d", maxInputEdges)
	}
	if len() > len(.Edges) {
		return fmt.Errorf("requested D2 edge count %d exceeds graph edge count %d", len(), len(.Edges))
	}
	 := make(map[*d2graph.Edge]struct{}, len(.Edges))
	for ,  := range .Edges {
		[] = struct{}{}
	}
	for ,  := range  {
		if ,  := []; ! {
			return fmt.Errorf("requested D2 edge %d is not in the graph", )
		}
	}
	return nil
}

// validateD2GraphStructure keeps D2's three public topology views coherent.
// The adapter traverses ChildrenArray, resolves near keys through Children,
// and applies results through Graph.Objects; accepting different sets would
// produce a successful layout whose patch is incomplete or targets the wrong
// objects.
func validateD2GraphStructure( *d2graph.Graph,  []d2ObjectVisit,  map[*d2graph.Object]struct{}) error {
	if .Root.Graph !=  {
		return fmt.Errorf("D2 root object belongs to a different graph")
	}

	 := make([]*d2graph.Object, 0, len()+1)
	 = append(, .Root)
	for ,  := range  {
		 = append(, .object)
	}
	for ,  := range  {
		if .Graph !=  {
			return fmt.Errorf("D2 object %q belongs to a different graph", .ID)
		}
		if len(.Children) != len(.ChildrenArray) {
			return fmt.Errorf("D2 object %q has inconsistent Children and ChildrenArray sets", .ID)
		}
		for ,  := range .ChildrenArray {
			 := strings.ToLower(.ID)
			,  := .Children[]
			if ! ||  !=  {
				return fmt.Errorf("D2 object %q has inconsistent Children and ChildrenArray sets", .ID)
			}
		}
	}

	if len(.Objects) != len() {
		return fmt.Errorf("D2 graph Objects and object tree contain different sets")
	}
	 := make(map[*d2graph.Object]struct{}, len(.Objects))
	for ,  := range .Objects {
		if  == nil {
			return fmt.Errorf("D2 graph Objects contains nil at index %d", )
		}
		if ,  := [];  {
			return fmt.Errorf("D2 graph Objects repeats object %q", .ID)
		}
		[] = struct{}{}
		if ,  := []; ! {
			return fmt.Errorf("D2 graph Objects contains object %q outside the object tree", .ID)
		}
	}
	return nil
}

func isSupportedLifelineEdge( *d2graph.Edge) bool {
	if .SrcArrow || .DstArrow || .SrcArrowhead != nil || .DstArrowhead != nil {
		return false
	}
	return d2sequence.IsLifelineEnd(.Dst) &&
		.Dst.ID == d2sequence.LifelineEndID(.Src.ID)
}

func validateD2Edges( context.Context,  []*d2graph.Edge,  map[*d2graph.Object]struct{}) error {
	if len() > maxInputEdges {
		return fmt.Errorf("D2 edge count exceeds limit %d", maxInputEdges)
	}

	 := make(map[*d2graph.Edge]struct{}, len())
	 := 0
	for ,  := range  {
		if  := .Err();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("D2 edge %d is nil", )
		}
		if ,  := [];  {
			return fmt.Errorf("D2 edge %d is repeated", )
		}
		[] = struct{}{}
		if .Src == nil || .Dst == nil {
			return fmt.Errorf("D2 edge %d has a nil endpoint", )
		}
		if  := validateD2TextDimensions("label", .LabelDimensions);  != nil {
			return fmt.Errorf("D2 edge %d: %w", , )
		}
		if  := validateD2Style(fmt.Sprintf("D2 edge %d", ), .Style);  != nil {
			return 
		}
		if .LabelPosition != nil && !label.FromString(*.LabelPosition).IsEdgePosition() {
			return fmt.Errorf("D2 edge %d has invalid label position %q", , *.LabelPosition)
		}
		if  := .LabelPercentage;  != nil && (math.IsNaN(*) || math.IsInf(*, 0) || * < 0 || * > 1) {
			return fmt.Errorf("D2 edge %d has invalid label percentage %v: must be finite and between 0 and 1", , *)
		}
		if .SrcArrowhead != nil {
			if  := validateD2Arrowhead(, "source", .SrcArrowhead);  != nil {
				return 
			}
			if  := validateD2TextDimensions("source arrowhead label", .SrcArrowhead.LabelDimensions);  != nil {
				return fmt.Errorf("D2 edge %d: %w", , )
			}
		}
		if .DstArrowhead != nil {
			if  := validateD2Arrowhead(, "destination", .DstArrowhead);  != nil {
				return 
			}
			if  := validateD2TextDimensions("destination arrowhead label", .DstArrowhead.LabelDimensions);  != nil {
				return fmt.Errorf("D2 edge %d: %w", , )
			}
		}
		if ,  := [.Src]; ! {
			return fmt.Errorf("D2 edge %d has a source outside the object tree", )
		}
		if ,  := [.Dst]; ! {
			if !isSupportedLifelineEdge() {
				return fmt.Errorf("D2 edge %d has a destination outside the object tree", )
			}
		}
		if  := validateD2TableColumnIndex(, "source", .Src, .SrcTableColumnIndex);  != nil {
			return 
		}
		if  := validateD2TableColumnIndex(, "destination", .Dst, .DstTableColumnIndex);  != nil {
			return 
		}

		if len(.Route) > maxInputRoutePoints- {
			return fmt.Errorf("D2 route point count exceeds limit %d", maxInputRoutePoints)
		}
		if len(.Route) == 1 {
			return fmt.Errorf("D2 edge %d route requires either zero or at least two points", )
		}
		 += len(.Route)
		for ,  := range .Route {
			if %256 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			if  == nil {
				return fmt.Errorf("D2 edge %d has a nil route point at index %d", , )
			}
			if  := validateResultCoordinate("route x", .X);  != nil {
				return fmt.Errorf("D2 edge %d route point %d: %w", , , )
			}
			if  := validateResultCoordinate("route y", .Y);  != nil {
				return fmt.Errorf("D2 edge %d route point %d: %w", , , )
			}
			if  > 0 && .X == .Route[-1].X && .Y == .Route[-1].Y {
				return fmt.Errorf("D2 edge %d has a degenerate route segment ending at index %d", , )
			}
		}
	}
	return nil
}

func validateD2Arrowhead( int,  string,  *d2graph.Attributes) error {
	if .Shape.Value != "" {
		if ,  := d2target.Arrowheads[.Shape.Value]; ! {
			return fmt.Errorf("D2 edge %d has unsupported %s arrowhead %q", , , .Shape.Value)
		}
	}
	return validateD2Style(fmt.Sprintf("D2 edge %d %s arrowhead", , ), .Style)
}

func validateD2TableColumnIndex( int,  string,  *d2graph.Object,  *int) error {
	if  == nil {
		return nil
	}
	if .SQLTable == nil {
		return fmt.Errorf("D2 edge %d has a %s table column index for a non-table object", , )
	}
	if * < 0 || * >= len(.SQLTable.Columns) {
		return fmt.Errorf(
			"D2 edge %d %s table column index %d is outside table with %d columns",
			,
			,
			*,
			len(.SQLTable.Columns),
		)
	}
	return nil
}