import os
import sys
import asyncio
import math

sys.path.append(os.path.join(os.path.dirname(__file__), 'utils'))
import vspt_freecad
import vspt_coroutine

verbose = False

project_folder = os.getcwd()

def apply_styles(page):
	modified = False
	
	dim_font_size = 3.0
	
	for view in page.Views:
		if view.TypeId == 'TechDraw::DrawViewDimension':
			if view.ViewObject.Fontsize != dim_font_size:
				view.ViewObject.Fontsize = dim_font_size
				modified = True
				
	return modified

async def generate_2d_drawing(file_name):
	doc = App.open(project_folder + '/' + file_name)
	
	modified = False
	
	page_name = doc.Name + '_Drawing'
	
	page = doc.getObject(page_name)
	if page is not None:
		print('2D drawing already exists - skipped')
	
	else:
		modified = True
		template_file_name = project_folder + '/lib/A4_Landscape_VSPT.svg'
	
		root_objects = []
		main_object = None
	
		for obj in doc.Objects:
			if len(obj.Parents) == 0:
				root_objects.append(obj)
				if obj.Label == doc.Name:
					main_object = obj
	
		if main_object is None and len(root_objects) == 1:
			main_object = root_objects[0]
	
		if main_object is None:
			raise Exception("Can't find main object in file " + doc.FileName + " (found " + str(len(root_objects)) + " root object(s), none named like the document " + doc.Name + ")")
	
		code_obj = doc.getObjectsByLabel('Code_Tube_Draft')
		if len(code_obj) == 1:
			code_obj = code_obj[0]
		else:
			code_obj = None
	
		sources = [main_object]
	
		bound_box = main_object.Shape.BoundBox
		proj_size = [0, 0, 0] # size of the original part front view after projection at scale 1:1
		if bound_box.XLength > bound_box.YLength:
			if bound_box.XLength > bound_box.ZLength:
				main_axis = 0
				proj_size[0] = bound_box.XLength
				proj_size[1] = bound_box.ZLength
				proj_size[2] = bound_box.YLength
			else:
				main_axis = 2
				proj_size[0] = bound_box.ZLength
				proj_size[1] = bound_box.XLength
				proj_size[2] = bound_box.YLength
		else:
			if bound_box.YLength > bound_box.ZLength:
				main_axis = 1
				proj_size[0] = bound_box.YLength
				proj_size[1] = bound_box.ZLength
				proj_size[2] = bound_box.XLength
			else:
				main_axis = 2
				proj_size[0] = bound_box.ZLength
				proj_size[1] = bound_box.XLength
				proj_size[2] = bound_box.YLength
	
		if verbose: print("Adding drawing page...");
				
		page = doc.addObject('TechDraw::DrawPage', page_name)
		template = doc.addObject('TechDraw::DrawSVGTemplate', 'Template')
	
		template.Template = template_file_name
		page.Template = template
		
		if verbose: print("Computing best scale...");
		scale_denominators = [4.0, 5.0, 6.0, 8.0, 10.0]
		scale_numerator = 1.0
		scale_denominator = scale_denominators[0]
		proj_total_size = [proj_size[0] + proj_size[2], proj_size[1] + proj_size[2]] # projected size of all views (without spacing) at scale 1:1
		spacingX = 20.0
		spacingY = 50.0
		maxSizeX = 280.0
		maxSizeY = 160.0
		for denom in scale_denominators:
			scale_denominator = denom
			if proj_total_size[0]*scale_numerator/denom + spacingX <= maxSizeX and proj_total_size[1]*scale_numerator/denom + spacingY <= maxSizeY:
				break
	
		if verbose: print("Adding projection group...");
		
		projGroup = doc.addObject('TechDraw::DrawProjGroup', doc.Name + '_ProjGroup')
		page.addView(projGroup)
		projGroup.ScaleType = 'Custom'
		projGroup.Scale = scale_numerator/scale_denominator
		projGroup.spacingX = 20.0
		projGroup.spacingY = 50.0
		projGroup.Source = sources
		projGroup.addProjection('Front')
		if main_axis == 0:
				projGroup.Anchor.Direction = App.Vector(0,1,0)
				projGroup.Anchor.XDirection = App.Vector(-1,0,0)
				projGroup.Anchor.RotationVector = App.Vector(-1,0,0)
		elif main_axis == 1:
				projGroup.Anchor.Direction = App.Vector(1,0,0)
				projGroup.Anchor.XDirection = App.Vector(0,1,0)
				projGroup.Anchor.RotationVector = App.Vector(0,1,0)
		elif main_axis == 2:
				projGroup.Anchor.Direction = App.Vector(0,1,0)
				projGroup.Anchor.XDirection = App.Vector(0,0,1)
				projGroup.Anchor.RotationVector = App.Vector(0,0,1)
		projGroup.addProjection('Top')
		projGroup.addProjection('Left')
		projGroup.X = 130.0
		projGroup.Y = 150.0
	
		texts = page.Template.EditableTexts
		texts['SCALE'] = str(int(scale_numerator+0.5))+':'+str(int(scale_denominator+0.5))
		try:
			texts['PM'] = main_object.Assembly_handbook_Material
		except:
			pass
		texts['PN'] = doc.Name
		texts['TITLELINE-1'] = doc.Name
		page.Template.EditableTexts = texts
	
		async def addDimensions():
			for view in projGroup.Views:
				if verbose: print("View: " + view.Label + "...")
	
				edges = []
				visibleEdges = view.getVisibleEdges()
				edgeIdx = 0
				lowestEdgeName = ''
				lowestEdgePos = 1000000
				while True:
					try:
						edge = view.getEdgeByIndex(edgeIdx)
					except:
						break
					edges.append(edge)
	
					if edge.BoundBox.YLength < 0.01 and edge.BoundBox.Center.y < lowestEdgePos:
						lowestEdgePos = edge.BoundBox.Center.y
						lowestEdgeName = 'Edge' + str(edgeIdx)
	
					edgeIdx = edgeIdx + 1
				
				vertices = []
				vertIdx = 0
				while True:
					try:
						vert = view.getVertexByIndex(vertIdx)
					except:
						break
					vertices.append(vert)
					vertIdx = vertIdx + 1
	
				def getFeatureName(edge):
					if edge.Curve.TypeId == 'Part::GeomCircle':
						vertIdx = 0
						c = edge.BoundBox.Center
						closestDist = 100000000
						closestVert = None
						for vert in vertices:
							dx = vert.X - c.x
							dy = vert.Y - c.y
							dist = math.sqrt(dx*dx + dy*dy)
							if dist < closestDist:
								closestDist = dist
								closestVert = vert
							vertIdx = vertIdx + 1
						if closestVert is not None:
							return 'Vertex' + str(vertices.index(closestVert))
						else:
							return ''
					else:
						return 'Edge'+str(edges.index(edge))
	
				if verbose: print("Listing features...")
				features = []
				for edge in edges:
					if (edge.Curve.TypeId == 'Part::GeomLine' and edge.BoundBox.XLength <= 0.01) or (edge.Curve.TypeId == 'Part::GeomCircle' and abs(edge.Curve.Radius * 2.0 - edge.BoundBox.XLength) < 0.001 and abs(edge.Curve.Radius * 2.0 - edge.BoundBox.YLength) < 0.001):
						featureName = getFeatureName(edge)
						if featureName == '':
							continue
						
						pos = edge.BoundBox.Center.x
						duplicate = False
						for otherFeature in features:
							if abs(otherFeature[0] - pos) < 0.1:
								duplicate = True
								break
						if not duplicate:
							features.append((pos, edge, featureName))
				features.sort(key=lambda e: e[0])
	
				def addDimension(edgeA, edgeB, posY):
					dim = doc.addObject('TechDraw::DrawViewDimension','Dimension')
					dim.Type = 'DistanceX'
					dim.FormatSpec = '%.1f'
					dim.References2D = [(view, (getFeatureName(edgeA), getFeatureName(edgeB)))]
					visibleEdgeA = visibleEdges[edges.index(edgeA)]
					visibleEdgeB = visibleEdges[edges.index(edgeB)]
					dim.X = (visibleEdgeA.BoundBox.Center.x + visibleEdgeB.BoundBox.Center.x) * 0.5
					dim.Y = posY
					page.addView(dim)
	
					if edgeB.Curve.TypeId == 'Part::GeomCircle': 
						if abs(edgeB.BoundBox.XLength - 6.5) > 0.01:
							dim = doc.addObject('TechDraw::DrawViewDimension','Dimension')
							dim.Type = 'Diameter'
							dim.FormatSpec = '%.1f'
							dim.References2D = [(view, ('Edge'+str(edges.index(edgeB)),))]
							dim.X = visibleEdgeB.BoundBox.Center.x + 6.0
							dim.Y = -6.0
							page.addView(dim)
	
						if abs(edgeB.BoundBox.Center.y) > 0.01 and lowestEdgeName != '':
							dim = doc.addObject('TechDraw::DrawViewDimension','Dimension')
							dim.Type = 'DistanceY'
							dim.FormatSpec = '%.1f'
							dim.References2D = [(view, (getFeatureName(edgeB),lowestEdgeName))]
							dim.X = visibleEdgeB.BoundBox.Center.x + 2.0
							dim.Y = -6.0
							page.addView(dim)
	
				if verbose: print("Adding dimensions...")
							
				if len(features) >= 2:
					if projGroup.Views.index(view) != 0:
						addDimension(features[0][1], features[len(features)-1][1], -25.0)
	
					if len(features) > 2:
						for featureIdx in range(0, len(features) - 1):
							if featureIdx == 0 or features[featureIdx][1].Curve.TypeId != 'Part::GeomLine':
								addDimension(features[featureIdx][1], features[featureIdx + 1][1], 15.0)
	
			if verbose: print("Adding secondary objects...")
			if code_obj is not None:
				projGroup.Source = projGroup.Source + [code_obj]
	
		page.recompute(True)
		await vspt_coroutine.get_main_loop().wait(1)
		await addDimensions()
		
	if apply_styles(page):
		modified = True
	
	if modified:
		if verbose: print("Saving...")
		page.recompute(True)
		page.ViewObject.Visibility = False # don't save the document with the page open or it will automatically reopen on load
		await vspt_coroutine.get_main_loop().wait(1)
		doc.save()
	
	if verbose: print("Closing...")
	vspt_freecad.close_all_docs()

async def run():
	try:
		folders = [
			'tubes'
		]

		for folder in folders:
			files = os.listdir(project_folder + '/' + folder)
			for source_file in files:
				if not source_file.endswith('.FCStd'): continue
				source_path = folder + '/' + source_file
				print(source_path)
				await generate_2d_drawing(source_path)
				
		# exit FreeCAD
		vspt_freecad.close_all_docs()
		FreeCADGui.getMainWindow().close()

	except Exception as e:
		print(e)

vspt_coroutine.get_main_loop().create_task(run())