import win32com.client
from win32com.client import constants as c
import time
xsi = Application
gpvec = XSIMath.CreateVector3()
gpcross = XSIMath.CreateVector3()
gpcrossabs = XSIMath.CreateVector3()
vec2 = XSIMath.CreateVector3()
oColl2 = XSIFactory.CreateObject('XSI.Collection')
null = None
false = 0
true = 1
#accum = []

def XSILoadPlugin( in_reg ):
	in_reg.Author = "Julian Johnson"
	in_reg.Name = "jj_loopstep.py"
	in_reg.Email = "julian@exch.demon.co.uk"
	in_reg.URL = "http://www.exch.demon.co.uk/jj_loopstep.htm"
	in_reg.Major = 1
	in_reg.Minor = 0

	in_reg.RegisterCommand("jj_loopstep","jj_loopstep")
	#RegistrationInsertionPoint - do not remove this line

	return true

def XSIUnloadPlugin( in_reg ):
	strPluginName = in_reg.Name
	Application.LogMessage(str(strPluginName) + str(" has been unloaded."))
	return true

def jj_loopstep_Init( ctxt ):
	oCmd = ctxt.Source
	oCmd.Description = ""
	oCmd.ReturnValue = true
	oArgs = oCmd.Arguments
	oArgs.Add('dire',c.siArgumentInput,1)
	oArgs.Add('step',c.siArgumentInput,1)
	oArgs.Add('mode',c.siArgumentInput,"")

	return true

def jj_loopstep_Execute(dire,step,mode):
	oSel = xsi.Selection
	if oSel.Count == 0:
			oMessage = XSIUIToolkit.Msgbox( "Select at least a couple of polygons", c.siMsgOkOnly)	
			return
	if oSel(0).Type != "polySubComponent":
			oMessage = XSIUIToolkit.Msgbox( "Select at least a couple of polygons", c.siMsgOkOnly)
			return
	pol_a = oSel(0).SubComponent.ComponentCollection(0)
	pol_b = oSel(0).SubComponent.ComponentCollection(1)
	loophop(pol_a,pol_b,dire,step,mode)
	return true
	
	
def disp(ob):
	return win32com.client.Dispatch(ob)

def looper2(current,previous):
	"""
	If you input a couple of polygons this function returns a 
	collection of polygon objects that represents the whole loop
	"""
	
	cur = current
	pre = previous
	traverse = []
	oColl2.RemoveAll()
	oColl2.AddItems((cur,pre))
	for i in range(2):
		if i == 1:
			pre = current
			cur = previous
		while cur:
			if cur.Index in traverse:
				break
			traverse.append(cur.Index)
			pre_ptarray = disp(pre.NeighborVertices(1))
			pre_ptarray_ia = pre_ptarray.IndexArray 
			neighbours = cur.NeighborPolygons(1)
			#start = time.clock()
			#this is the start of the slow area of the script---------------------------------------------
			for x in neighbours:
				found = 0
				if x.Index != pre.Index:
					ind_arr = disp(x.NeighborVertices(1))
					if ind_arr.Count > 4:
						continue
					ind_arr_ia = ind_arr.IndexArray
					for o in ind_arr_ia :
						if o in pre_ptarray_ia:
							found = 1
							break
					if found == 1:
						continue
					new_poly =  x     # if you get here you have an 'unconnected' poly so break?
			#this is the end of the slow area of the script---------------------------------------------
			#finish = time.clock()
			
			#overall = finish - start
			#accum.append(overall)
			try:
				new_poly
			except UnboundLocalError:
				break
			oColl2.Add(new_poly)
			pre = cur
	
			cur = new_poly
	
	
	
	return oColl2

def vertpair(polx,poly):
	"""
	This returns two vertex objects in a Python List and the 
	position vector of the midpoint of  those verts
	"""
	twoverts = []
	polxvertices = polx.Vertices
	polyvertices = poly.Vertices
	for x in polxvertices:
		for y in polyvertices:
			if x.IsEqualTo(y):
				twoverts.append(x)
	vec2.Set((twoverts[0].position.X + twoverts[1].position.X)/2, (twoverts[0].position.Y + twoverts[1].position.Y)/2, (twoverts[0].position.Z + twoverts[1].position.Z)/2)
	
	return vec2,twoverts
				
def nbpolys(polx,poly,joinpts):
	"""
	If you input a couple of attached 'loop' polys and the two
	points that interconnect them, this returns the two pairs of 
	adjoining 'loop' polygons i.e. prev/next loop polygons
	"""
	jpneigh1 = joinpts[0].NeighborPolygons(1)#these are the four neighboouring polygons to one of the waist points
	jpneigh2 = joinpts[1].NeighborPolygons(1)#these are the other four neighbouring polygons
	group_a = []
	group_b = []
	for x in jpneigh1:
		if x.IsEqualTo(polx) or x.IsEqualTo(poly):
			continue#check that they're not the original two starter polys
		group_a.append(x)
	for x in jpneigh2:
		if x.IsEqualTo(polx) or x.IsEqualTo(poly):
			continue
		group_b.append(x)
	
	if not group_a:
		print 'not group a'
		group_a.extend((polx,poly))
		return group_a, group_b
	if not group_b:
		print 'not group b'
		group_b.extend((polx,poly))
		return group_a, group_b
	
	#print group_b[0].Index, group_b[1].Index
	#at this stage you have two 'groups' of two polygons
	#now to make sure we're always using the cross/dot of the 'same'
	#polygon for each loop (to prevent variations in the direction) we
	#need to find which one of each pair is connected to polx so that we
	#have some form of consistency.
	
	switch = 0
	for x in group_a[0].NeighborPolygons(1):
		if x.IsEqualTo(polx):
			group_a_sorted = [group_a[0],group_a[1]]
			switch = 1
			break
	if switch == 0:
		group_a_sorted = [group_a[1],group_a[0]]
	
	switch = 0
	for x in group_b[0].NeighborPolygons(1):
		if x.IsEqualTo(polx):
			group_b_sorted = [group_b[0],group_b[1]]
			switch = 1
			break
	if switch == 0:
		group_b_sorted = [group_b[1],group_b[0]]	

	return group_a_sorted, group_b_sorted	#this order reflects the orignal order for some reason

def axis(index):
	lookup = ((1,0,0),(0,1,0),(0,0,1))
	return XSIMath.CreateVector3(lookup[index])

def crosser(jverts,midpvec,norm):
	#start = time.clock()
	negpos2 = []
	for x in range(2):
		#this creates a vector from the midpoint  to the outer point showing direction
		gpvec.Sub(jverts[x].position,midpvec)
		gpcross.Cross(gpvec, norm)
		gpcrossabs.Absolute(gpcross)
		maxindex = gpcrossabs.MaxComponentIndex()
		dot = gpcross.Dot(axis(maxindex))
		sign = jverts[x].position.Value(maxindex)
		if sign < 0:
			dot = dot * -1
		negpos2.append(dot)
	#finish = time.clock()
	#overall = finish - start
	#accum.append(overall)
	#xsi.LogMessage(overall)
	return negpos2

def loophop(pol_a,pol_b,direction,step,mode):
	if mode == 'infin':
		oColl3 = XSIFactory.CreateObject('XSI.Collection')
		travpols = []
		traverse = 1
		oColl3.AddItems(looper2(pol_a, pol_b))
		while traverse:
			for t in range(step):
				midpvector, joinverts = vertpair(pol_a,pol_b)	
				gp_a, gp_b = nbpolys(pol_a,pol_b,joinverts)
				
				negpos = crosser(joinverts,midpvector,pol_a.Nodes(0).Normal)
				
				if direction == 1:
					if negpos[0] < 0:
						dirpolys = gp_b
					elif negpos[0] > 0:
						dirpolys = gp_a
				else:
					if negpos[0] > 0:
						dirpolys = gp_b
					elif negpos[0] < 0:
						dirpolys = gp_a
				
				if dirpolys[0].Index in travpols:
					traverse = 0
					break
				elif dirpolys[1].Index in travpols:
					traverse = 0
					break
				
				pol_a = dirpolys[0]
				pol_b = dirpolys[1]
				
				travpols.extend((pol_a.Index,pol_b.Index))
			if traverse != 0:
				oColl3.AddItems(looper2(pol_a, pol_b))

		Application.SelectGeometryComponents(oColl3)
		#result = reduce(lambda x,y: x+y, accum)
		#xsi.LogMessage(result)
		#xsi.LogMessage(len(accum))
		
	if mode == 'next':
		oColl3 = XSIFactory.CreateObject('XSI.Collection')
		for t in range(step):
			midpvector, joinverts = vertpair(pol_a,pol_b)	
			gp_a, gp_b = nbpolys(pol_a,pol_b,joinverts)
			
			negpos = crosser(joinverts,midpvector,pol_a.Nodes(0).Normal)
			
			if direction == 1:
				if negpos[0] < 0:
					dirpolys = gp_b
				elif negpos[0] > 0:
					dirpolys = gp_a
			else:
				if negpos[0] > 0:
					dirpolys = gp_b
				elif negpos[0] < 0:
					dirpolys = gp_a
			
			pol_a = dirpolys[0]
			pol_b = dirpolys[1]
		oColl3.AddItems(looper2(pol_a, pol_b))

		Application.SelectGeometryComponents(oColl3) 


