package routing

import (
	
	
)

type OVGNode struct {
	*geo.Point                                // 8 bytes
	Edges      []*OVGEdge                     // 24 bytes
	IsNearPort map[*layoutgraph.Node]struct{} // 8 bytes
	// portOwnersByNode keeps port metadata per graph node. More than one graph node can
	// legitimately have a port at the same coordinate (for example, two shapes
	// whose borders touch). The OVG canonicalizes nodes by coordinate, so a
	// single owner and direction cannot describe that point correctly.
	portOwnersByNode map[*layoutgraph.Node]ovgPortMetadata
	Container        *layoutgraph. // 8 bytes
				Node
	Index        int  // 8 bytes
	IsNodeCenter bool // 1 byte
	IsTunnel     bool // 1 byte
}

type ovgPortMetadata struct {
	directions   portDirectionSet
	isCenterPort bool
}

// A rounded OVG coordinate can represent more than one side of the same node.
// A 1x1 square, for example, has top and left snap points at the same point.
// Keep every valid role instead of letting the last side overwrite the first.
type portDirectionSet uint16

func newPortDirectionSet( geo.Orientation) portDirectionSet {
	if  < geo.TopLeft ||  > geo.NONE {
		return 0
	}
	return 1 << uint()
}

func ( portDirectionSet) ( geo.Orientation) bool {
	return &newPortDirectionSet() != 0
}

// any reports whether predicate accepts at least one represented direction.
// Empty metadata is unrestricted and therefore behaves like NONE.
func ( portDirectionSet) ( func(geo.Orientation) bool) bool {
	if  == 0 {
		return (geo.NONE)
	}
	for  := geo.TopLeft;  <= geo.NONE; ++ {
		if .has() && () {
			return true
		}
	}
	return false
}

func ( portDirectionSet) ( func(geo.Orientation) geo.Orientation) portDirectionSet {
	var  portDirectionSet
	.any(func( geo.Orientation) bool {
		 |= newPortDirectionSet(())
		return false
	})
	return 
}

func ( *geo.Point) *OVGNode {
	// Edges grows lazily. A large share of candidate visibility nodes are
	// rejected before connection, so eagerly allocating four slots here creates
	// substantial garbage without helping surviving nodes.
	return &OVGNode{Point: }
}

func ( *OVGNode) () {
	if .portOwnersByNode == nil {
		.portOwnersByNode = make(map[*layoutgraph.Node]ovgPortMetadata, 2)
	}
}

func ( *OVGNode) ( *layoutgraph.Node,  ovgPortMetadata) {
	if  == nil {
		return
	}
	.ensurePortOwners()

	 := .portOwnersByNode[]
	.directions |= .directions
	.isCenterPort = .isCenterPort || .isCenterPort
	.portOwnersByNode[] = 
}

func ( *OVGNode) ( *layoutgraph.Node,  geo.Orientation,  bool) {
	.addPortMetadata(, ovgPortMetadata{
		directions:   newPortDirectionSet(),
		isCenterPort: ,
	})
}

func ( *OVGNode) ( *layoutgraph.Node) {
	,  := .portMetadataFor()
	if ! {
		return
	}
	.isCenterPort = true
	.addPortMetadata(, )
}

func ( *OVGNode) ( *layoutgraph.Node) (ovgPortMetadata, bool) {
	,  := .portOwnersByNode[]
	return , 
}

func ( *OVGNode) () bool {
	return len(.portOwnersByNode) > 0
}

func ( *OVGNode) ( *layoutgraph.Node) bool {
	,  := .portMetadataFor()
	return 
}

func ( *OVGNode) ( *layoutgraph.Node) (portDirectionSet, bool) {
	,  := .portMetadataFor()
	return .directions, 
}

func ( *OVGNode) ( *layoutgraph.Node,  geo.Orientation) bool {
	,  := .portDirectionsFor()
	return  && .has()
}

func ( *OVGNode) ( *layoutgraph.Node) portDirectionSet {
	,  := .portDirectionsFor()
	return 
}

func ( *OVGNode) ( *layoutgraph.Node,  portDirectionSet) {
	,  := .portMetadataFor()
	if ! {
		return
	}
	.ensurePortOwners()
	.directions = 
	.portOwnersByNode[] = 
}

func ( *OVGNode) ( *layoutgraph.Node) bool {
	,  := .portMetadataFor()
	return  && .isCenterPort
}

func ( *OVGNode) () map[*layoutgraph.Node]ovgPortMetadata {
	return .portOwnersByNode
}

func ( *OVGNode) ( *OVGNode) bool {
	if  == nil ||  == nil {
		return false
	}
	for  := range .portOwners() {
		if .isPortOf() {
			return true
		}
	}
	return false
}

func ( *OVGNode) ( *OVGEdge) {
	.Edges = append(.Edges, )
}

func ( *OVGNode) ( *OVGEdge) *OVGNode {
	if  == .From {
		return .To
	}
	return .From
}

func ( *OVGNode) ( *OVGEdge) *OVGNode { return .adjacent() }

// hasUnobstructedLineToPorts checks if an OVG node has unobstructed lines to non-container ports
// numNodes indicates how many ports belonging to different nodes it must have lines to
func ( *OVGNode) ( *OVG,  *ovgPortIndex,  int,  *ovgBuildGuard) (bool, error) {
	if  := .check();  != nil {
		return false, 
	}
	// Only care about ovg nodes which are port aligned
	 := 0

	 := .alignedOwners(.X, .Y)
	 := len(.byX) > 0 || len(.byY) > 0
	for {
		, ,  := .next()
		if  != nil {
			return false, 
		}
		if ! {
			break
		}
		 := .owners[]
		 := .Ports[]
		for ,  := range  {
			if  := .step();  != nil {
				return false, 
			}
			if .X != .X && .Y != .Y {
				continue
			}
			// Port nodes which can make a straight line between it, its shape's edge, and the point, are NOT candidates for unobstructed path
			// e.g. this is bad
			// ┌──────┐
			// │      │
			// x      │
			// ├──────┘
			// │
			// │
			// x
			//
			// this is fine
			//        ┌──────┐
			//        │      │
			// x──────x      │
			//        └──────┘
			if .X == .X && ((.X == .TopLeft.X) || (.X == .TopLeft.X+.Width)) {
				continue
			}
			if .Y == .Y && ((.Y == .TopLeft.Y) || (.Y == .TopLeft.Y+.Height)) {
				continue
			}

			// Remove nodes which are obstructed from a port by another node
			 := true
			 := .horizontalBlockers[.Y]
			if .X == .X {
				 = .verticalBlockers[.X]
			}
			for ,  := range  {
				if  := .step();  != nil {
					return false, 
				}
				if  ==  {
					continue
				}
				if .IsInvisible {
					continue
				}
				// don't count nodes of the same sequence as blocking each other
				if .Sequence != nil && .Sequence != nil && .Sequence == .Sequence {
					continue
				}
				if .PassesThrough(.Point, .Point) {
					 = false
					break
				}
			}

			if  {
				++
				if  ==  {
					return true, nil
				}
				break
			}
		}
	}

	return !, .check()
}

func ( *OVGNode) ( *layoutgraph.Node) float64 {
	 := .Point.X
	 := .Point.Y

	 := .TopLeft.X
	 := .TopLeft.Y
	 := .TopLeft.X + .Width
	 := .TopLeft.Y + .Height

	 :=  < 
	 :=  < 
	 :=  < 
	 :=  < 

	if  &&  {
		return geo.EuclideanDistance(, , , )
	} else if  &&  {
		return geo.EuclideanDistance(, , , )
	} else if  &&  {
		return geo.EuclideanDistance(, , , )
	} else if  &&  {
		return geo.EuclideanDistance(, , , )
	} else if  {
		return  - 
	} else if  {
		return  - 
	} else if  {
		return  - 
	} else if  {
		return  - 
	} else {
		return 0
	}
}