# coding: utf-8
#
# Copyright © 2020 MSC.Software Corporation
#  
# Permission to use Scripts
#  
# Permission is granted to MSC.Software customers with valid licenses of the MSC Apex™ software 
# to use script files posted on this page (collectively “Scripts”) solely for customers’ own authorized 
# internal use in conjunction with licensed use of the MSC Apex software.  
# No other use is permitted.  No license to MSC Apex or other software is granted under this Permission.
#  
# SCRIPTS ARE PROVIDED ON AN "AS-IS" BASIS, WITH NO WARRANTIES OF ANY KIND.  USE OF THE SCRIPTS IS AT CUSTOMER'S SOLE RISK.
#  
# IN NO EVENT SHALL MSC.SOFTWARE CORPORATION, ITS AFFILIATES, SUBSIDIARIES, SUPPLIERS, AGENTS, REPRESENTATIVES OR EMPLOYEES BE LIABLE FOR ANY 
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, 
# STRICT LIABILITY, OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION USE OF THE SCRIPTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
#  
# Customer shall indemnify, defend and hold MSC.Software Corporation, its affiliates, subsidiaries, suppliers, agents, 
# employees or representatives (collectively, “Indemnified Parties”) harmless from and against any third-party claims 
# brought against Indemnified Parties rising out of or in connection with Customer’s use of the Scripts.
#

# Author: Larry Pearce
# Revised by: Larry Pearce
# Revision History
# Date         Change                                 By
# 2020-10-13   Original code                          Larry Pearce

import apex


def main(dict={}):
	# If these attributes exist for the connector, then I can re-connect it
	validAttribs = ["end1_X", "end1_Y", "end1_Z", "end2_X", "end2_Y", "end2_Z"]

	myModel = apex.currentModel()

	#_selected = apex.selection.getCurrentSelection()

	# This tolerance is used to see if the found node is close to the saved location
	tol = float(dict["tolerance"])
	if tol <= 0.0: tol = 0.1
	print("Tolerance: " + str(tol))

	# Get all connectors in the model
	allConn = apex.entityCollection()
	assys = myModel.getAssemblies()
	if assys.len() == 0:
		print("No Assemblies found!")
		return
	for assy in assys:
		conns = apex.attribute.getConnectors([{"path": assy.getPath()}])
		allConn.extend(conns)
	if allConn.len() == 0:
		print("No connectors selected or found in an Assembly!")
		return

	connsToUpdate = []
	for conn in allConn:
		if conn.getEntityType() != apex.EntityType.Connector: continue

		# I am looking for the attribute named "au_P2P" as a flag that his connector was created by a tool
		# that supports saving the connector endpoint locations.  To be honest, I do not think this should
		# be required but I will keep it for now.
		attribs = conn.getUserAttributes(userAttributeNames=["au_P2P"])
		if attribs.len() == 0: continue

		# Get the attributes that contain the connector end locations
		attribs = conn.getUserAttributes(userAttributeNames=["end1_X", "end1_Y", "end1_Z", "end2_X", "end2_Y", "end2_Z"])

		if attribs.len() != 6: continue # I need all 6 attributes to continue

		# Extract the attribute values, these will be the coordinates of the connector endpoints
		numAttribs = 0
		attribVals = {}
		for attribute in attribs:
			if attribute.getAttributeName() in validAttribs:
				numAttribs += 1
				attribVals[attribute.getAttributeName()] = attribute.getFloatValue()

		if numAttribs != 6: continue # Double check that I have 6 attributes

		# At this point, I have the connector and its endpoints
		connsToUpdate.append([conn, attribVals])

	if len(connsToUpdate) == 0:
		print("Selected connectors did not have the user attributes required for re-connection!")
		return

	allParts = myModel.getParts(True)

	# Get all nodes in the model and set up the proximity search
	# Note that this does not include nodes associated with RBE spiders
	iNodes = apex.IPhysicalCollection()
	for part in allParts:
		meshBodies = part.getMeshes()
		for mesh in meshBodies:
			meshNodes = mesh.getNodes()
			iNodes.extend(meshNodes)

	mySearch = apex.utility.createProximitySearch()
	mySearch.insertCollection(iNodes)

	nodesUsed   = []
	updateConns = []
	for connToUpdate in connsToUpdate:
		conn = connToUpdate[0]
		xyz  = connToUpdate[1]

		# if the endpoints exist then there is no need to reconnect the connector
		if conn.getEnd1InterfacePoint() is not None and conn.getEnd2InterfacePoint() is not None: continue

		# put the saved input locations in a list for convenience
		gab = []
		gab.append(["ga", apex.Coordinate(xyz["end1_X"], xyz["end1_Y"], xyz["end1_Z"])])
		gab.append(["gb", apex.Coordinate(xyz["end2_X"], xyz["end2_Y"], xyz["end2_Z"])])

		# Determine if there is a close node within tolerance
		endPoints = {"ga": None, "gb": None}
		for g in gab:
			nearbyObjects = mySearch.findNearestObjects(g[1], 4)  # for g, 0 is the label and 1 is the coordinate
			foundObjects = nearbyObjects.foundObjects()
			dist = nearbyObjects.getDistances()

			for k, node in enumerate(foundObjects):
				id = node.getId()

				#if id in nodesUsed: continue
				if dist[k] <= tol:
					nodesUsed.append(id)
					endPoints[g[0]] = node
					break
		if endPoints["ga"] is None: continue
		if endPoints["gb"] is None: continue
		updateConns.append([conn, endPoints])

	if len(updateConns) == 0:
		print("No connectors to be updated!")
		return

	numConnectorsUpdated = 0
	for data in updateConns:
		conn      = data[0]
		endPoints = data[1]

		n1 = endPoints["ga"]
		n2 = endPoints["gb"]

		_endPoint1 = apex.construct.createLocationByEntity(entity=n1)
		_endPoint2 = apex.construct.createLocationByEntity(entity=n2)

		conn.update(end1InterfacePoint=_endPoint1, end2InterfacePoint=_endPoint2)

		numConnectorsUpdated += 1

	print("Connectors updated: " + str(numConnectorsUpdated))
