# -*- coding: utf-8 -*-
# Author: Ruslan Krenzler.
# Date: 11 February 2018
# Create a pipe frame box.

import math
import csv
import os.path

from PySide import QtCore, QtGui
import FreeCAD
import Spreadsheet
import Sketcher
import Part
import Draft

tu = FreeCAD.Units.parseQuantity

def GetMacroPath():
	param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
	return param.GetString("MacroPath","")

# This version of the macro does not create corners.
DIMENSIONS_USED = ["G", "LX", "LY", "LZ", "POD", "PID"] 

# It must contain unique values in the column "Name" and also, dimensions listened below.
PIPE_DIMENSIONS_USED = ["ID", "OD"]
CORNER_DIMENSIONS_USED = ["G", "H", "M", "POD", "PID"]

PIPE_CSV_TABLE_PATH =  GetMacroPath()+"/pipe.csv"
CORNER_CSV_TABLE_PATH =  GetMacroPath()+"/outer-corner.csv"


# The value RELATIVE_EPSILON is used to slightly change the size of a subtracted part
# to prevent problems with boolean operations.
# This value does not change the appearance of part and can be large.
# If the original value is L then we often use the value L*(1+RELATIVE_EPSILON) instead.
# The relative deviation is then (L*(1+RELATIVE_EPSILON)-L)/L = RELATIVE_EPSILON.
# That is why the constant has "relative" in its name.
# On my version of freecad 0.16 The macro works even with RELATIVE_EPSILON = 0.0.
# Maybe there is no more problems with boolean operations.
RELATIVE_EPSILON = 0.1

def nestedObjects(group):
	res = []
	if group.OutList == []:
		res.append(group)
	else:
		# Append children first.
		for o in group.OutList:
			res += nestedObjects(o)
		res.append(group)
	return res

def toSolid(document, part, name):
	"""Convert object to a solid.
		Basically those are commands, which FreeCAD runs when user converts a part to a solid.
	"""
	s = part.Shape.Faces
	s = Part.Solid(Part.Shell(s))
	o = document.addObject("Part::Feature", name)
	o.Label=name
	o.Shape=s
	return o


class Error(Exception):
	"""Base class for exceptions in this module."""
	def __init__(self, message):
		super(Error, self).__init__(message)


class UnplausibleDimensions(Error):
	"""Exception raised when dimensions are unplausible. For example if
	outer diameter is larger than the iner one.

	Attributes:
	message -- explanation of the error
	"""

	def __init__(self, message):
		super(UnplausibleDimensions, self).__init__(message)
class Pipe:
	def __init__(self, document):
		self.document = document
		self.ID = tu("2 cm")
		self.OD = tu("3 cm")
		self.L = tu("1 m")

	def checkDimensions(self):
		if not (self.ID > tu("0 mm")):
			raise UnplausibleDimensions("ID (inner diameter) of the pipe must be positive. It is %s instead"%(self.ID))
		if not (self.OD > self.ID):
			raise UnplausibleDimensions("OD (outer diameter) %s must be larger than ID (inner dimater) %s "%(self.OD, self.ID))
		if not (self.L > tu("0 mm")):
			raise UnplausibleDimensions("Length L=%s must be positive"%self.L)

	def create(self, convertToSolid):
		""" A pipe which is a differences of two cilinders: outer cylinder - inner cylinder.
		:param convertToSolid: if true, the resulting part will be solid.
			if false, the resulting part will be a cut.
		:return resulting part.
		"""
		self.checkDimensions()
		# Create outer cylinder.
		outer_cylinder = self.document.addObject("Part::Cylinder","OuterCylinder")
		outer_cylinder.Radius = self.OD/2
		outer_cylinder.Height = self.L
		
		# Create inner cylinder. It is a little bit longer than the outer cylider in both ends.
		# This should prevent numerical problems when calculating difference
		# between the outer and innter cylinder.
		inner_cylinder = self.document.addObject("Part::Cylinder","InnerCylinder")
		inner_cylinder.Radius = self.ID/2
		inner_cylinder.Height = self.L*(1+2*RELATIVE_EPSILON)
		inner_cylinder.Placement.Base = App.Vector(0,0,-self.L*RELATIVE_EPSILON)
		pipe = self.document.addObject("Part::Cut","Pipe")
		pipe.Base = outer_cylinder
		pipe.Tool = inner_cylinder

		if convertToSolid:
			# Before making a solid, recompute documents. Otherwise there will be
			#    s = Part.Solid(Part.Shell(s))
			#    <class 'Part.OCCError'>: Shape is null
			# exception.
			self.document.recompute()
			# Now convert all parts to solid, and remove intermediate data.
			solid = toSolid(self.document, pipe, "pipe (solid)")
			# Remove previous (intermediate parts).
			parts = nestedObjects(pipe)
			# Document.removeObjects can remove multple objects, when we use
			# parts directly. To prevent exceptions with deleted objects,
			# use the name list instead.
			names_to_remove = []
			for part in parts:
				if part.Name not in names_to_remove:
					names_to_remove.append(part.Name)
			for name in names_to_remove:
				print("Deleting temporary objects %s."%name)
				self.document.removeObject(name)
			return solid
		return pipe

class OuterCorner:
	def __init__(self, document):
		self.document = document
		self.G = tu("2 in")
		self.H = tu("3 in")
		self.M = tu("3 in")
		self.POD = tu("2 in")
		self.PID = tu("1 in")

		
	def checkDimensions(self):
		if not ( self.POD > tu("0 mm") and self.PID > tu("0 mm") ):
			raise UnplausibleDimensions("Pipe dimensions must be positive. They are POD=%s and PID=%s instead"%(self.POD, self.PID))
		if not (self.M > self.POD and self.POD > self.PID):
			raise UnplausibleDimensions("Outer diameter M %s must be larger than outer pipe POD %s diamter. ",
						"Outer pipe diameter POD %s must be larger than inner pipe diameter PID %s"%(self.M, self.POD, self.PID))
		if not (self.G > self.PID/2):
			raise UnplausibleDimensions("Length G %s must be larger than inner pipe radius PID/2=%s."%(self.G, self.PID/2))

		if not (self.H > self.G):
			raise UnplausibleDimensions("Length G %s must be larger than H %s."%(self.G, self.H))

	def createPrimitiveCorner(self, L, D):
		"""Create corner consisting of two cylinder along x-,y- and y axis and a ball in the center."""
		x_cylinder = self.document.addObject("Part::Cylinder","XCynlider")
		x_cylinder.Radius = D/2
		x_cylinder.Height = L
		x_cylinder.Placement = App.Placement(App.Vector(0,0,0), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0))
		y_cylinder = self.document.addObject("Part::Cylinder","YCynlider")
		y_cylinder.Radius = D/2
		y_cylinder.Height = L
		y_cylinder.Placement = App.Placement(App.Vector(0,0,0), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0))
		z_cylinder = self.document.addObject("Part::Cylinder","ZCynlider")
		z_cylinder.Radius = D/2
		z_cylinder.Height = L
		sphere = self.document.addObject("Part::Sphere","Sphere")
		sphere.Radius = D/2
		fusion = self.document.addObject("Part::MultiFuse","Fusion")
		fusion.Shapes = [x_cylinder,y_cylinder,z_cylinder,sphere]
		return fusion

	def addSockets(self, fusion):
		"""Add socket cylinders to the fusion."""
		x_socket = self.document.addObject("Part::Cylinder","XSocket")
		x_socket.Radius = self.POD / 2
		x_socket.Height = self.H - self.G
		x_socket.Placement = App.Placement(App.Vector(self.G, 0,0), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0))
		y_socket = self.document.addObject("Part::Cylinder","YSocket")
		y_socket.Radius = self.POD / 2
		y_socket.Height = self.H - self.G
		y_socket.Placement = App.Placement(App.Vector(0, self.G,0), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0))
		z_socket = self.document.addObject("Part::Cylinder","ZSocket")
		z_socket.Radius = self.POD / 2
		z_socket.Height = self.H - self.G
		z_socket.Placement.Base = App.Vector(0, 0, self.G)
		fusion.Shapes = fusion.Shapes + [x_socket, y_socket, z_socket] # fusion.Shapes.append does not work.
		return fusion

	def createOuterPart(self):
		return self.createPrimitiveCorner(self.H, self.M)

	def createInnerPart(self):
		return self.createPrimitiveCorner(self.H, self.PID)

	def create(self, convertToSolid):
		self.checkDimensions()
		outer = self.createOuterPart()
		inner = self.createInnerPart()
		inner = self.addSockets(inner)

		# Remove inner part of the sockets.
		corner = self.document.addObject("Part::Cut","Cut")
		corner.Base = outer
		corner.Tool = inner
		
		if convertToSolid:
			# Before making a solid, recompute documents. Otherwise there will be
			#    s = Part.Solid(Part.Shell(s))
			#    <class 'Part.OCCError'>: Shape is null
			# exception.
			self.document.recompute()
			# Now convert all parts to solid, and remove intermediate data.
			solid = toSolid(self.document, corner, "corner (solid)")
			# Remove previous (intermediate parts).
			parts = nestedObjects(corner)
			# Document.removeObjects can remove multple objects, when we use
			# parts directly. To prevent exceptions with deleted objects,
			# use the name list instead.
			names_to_remove = []
			for part in parts:
				if part.Name not in names_to_remove:
					names_to_remove.append(part.Name)
			for name in names_to_remove:
				print("Deleting temporary objects %s."%name)
				self.document.removeObject(name)
			return solid
		return corner


class Box:
	def __init__(self, document):
		self.document = document
		self.G = tu("2 in")
		self.LX = tu("12 in")
		self.LY = tu("10 in")
		self.LZ = tu("8 in")
		self.POD = tu("2 in")
		self.PID = tu("1 in")
		self.corner = OuterCorner(document)
		
	def checkDimensions(self):
		if not ( self.POD > tu("0 mm") and self.PID > tu("0 mm") ):
			raise UnplausibleDimensions("Pipe dimensions must be positive. They are POD=%s and PID=%s instead"%(self.POD, self.PID))
		if not (self.LX > 2*self.G):
			raise UnplausibleDimensions("The length LX %smust be larger than 2*G %s"%(self.LX, 2*self.G))
		if not (self.LY > 2*self.G):
			raise UnplausibleDimensions("The length LY %smust be larger than 2*G %s"%(self.LY, 2*self.G))
		if not (self.LZ > 2*self.G):
			raise UnplausibleDimensions("The length LZ %smust be larger than 2*G %s"%(self.LZ, 2*self.G))
	
	def createPipes(self, group, convertToSolid):
		# Calculate pipe lengthes
		x_pipe_l = self.LX - 2*self.G
		y_pipe_l = self.LY - 2*self.G
		z_pipe_l = self.LZ - 2*self.G
		# First 3 pipes around the (0,0,0) origin in X,Y,Z direction
		pipe = Pipe(self.document)
		pipe.ID = self.PID
		pipe.OD = self.POD
		pipe.L = z_pipe_l
		zpipe = pipe.create(convertToSolid)
		group.addObject(zpipe)
		zpipe.Placement.Base = App.Vector(0,0,self.G)
		zpipe.Label = "z-"+zpipe.Label
		pipe.L = x_pipe_l
		xpipe = pipe.create(convertToSolid)
		group.addObject(xpipe)
		xpipe.Placement = App.Placement(App.Vector(self.G, 0,0), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0))
		xpipe.Label = "x-"+xpipe.Label
		pipe.L = y_pipe_l
		ypipe = pipe.create(convertToSolid)
		group.addObject(ypipe)
		ypipe.Placement = App.Placement(App.Vector(0, self.G,0), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0))
		ypipe.Label = "y-"+ypipe.Label

		# Add 3 clones for each x,y,z-type of axis. Place them on the edges of the quebe
		# First add z-pipes (because it simple, and does not require rotation).
		tmp = Draft.clone(zpipe, App.Vector(self.LX,0,self.G))
		group.addObject(tmp)
		tmp = Draft.clone(zpipe, App.Vector(0, self.LY,self.G))
		group.addObject(tmp)
		tmp = Draft.clone(zpipe, App.Vector(self.LX, self.LY,self.G))
		group.addObject(tmp)
		# Then add x pipes.
		tmp = Draft.clone(xpipe)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(self.G, self.LY, 0), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0))
		tmp = Draft.clone(xpipe)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(self.G, 0, self.LZ), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0))
		tmp = Draft.clone(xpipe)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(self.G, self.LY, self.LZ), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0))
		# Finally add y pipes.
		tmp = Draft.clone(ypipe)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(self.LX, self.G, 0), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0))
		tmp = Draft.clone(ypipe)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(0, self.G, self.LZ), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0))
		tmp = Draft.clone(ypipe)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(self.LX, self.G, self.LZ), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0))

	def addCorners(self, group, convertToSolid):
		corner = self.corner.create(convertToSolid)
		group.addObject(corner)
		# clone the corners and put them on right positions.
		tmp = Draft.clone(corner)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(0, 0, self.LZ), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0))
		tmp = Draft.clone(corner)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(self.LX,0, self.LZ), App.Rotation(App.Vector(0,1,0),180), App.Vector(0,0,0))
		tmp = Draft.clone(corner)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(self.LX,0,0), App.Rotation(App.Vector(0,1,0),270), App.Vector(0,0,0))

		tmp = Draft.clone(corner)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(self.LX,self.LY,0), App.Rotation(App.Vector(0,0,1),180), App.Vector(0,0,0))
		tmp = Draft.clone(corner)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(0,self.LY,0), App.Rotation(App.Vector(0,0,1),270), App.Vector(0,0,0))

		tmp = Draft.clone(corner)
		group.addObject(tmp)
		tmp.Placement = App.Placement(App.Vector(0,self.LY, self.LZ), App.Rotation(App.Vector(1,0,0),180), App.Vector(0,0,0))
		tmp = Draft.clone(corner)
		group.addObject(tmp)
		# First rotation.
		tmp.Placement = App.Placement(App.Vector(0,0,0), App.Rotation(App.Vector(0,0,1),180), App.Vector(0,0,0))
		# Second rotation + shift.
		tmp.Placement = App.Placement(App.Vector(self.LX,self.LY, self.LZ), App.Rotation(App.Vector(1,0,0),90), App.Vector(0,0,0)).multiply(tmp.Placement)
		
	def create(self, convertToSolid):
		self.checkDimensions()
		group = self.document.addObject("App::DocumentObjectGroup", "frame box group")
		self.createPipes(group, convertToSolid)
		self.addCorners(group, convertToSolid)
		return group
class CsvTable:
	""" Read coupling dimensions from a csv file.
	one part of the column must be unique and contains a unique key.
	It is the column "Name".
	"""
	def __init__(self, mandatoryDims=[]):
		"""
		@param mandatoryDims: list of column names which must be presented in the CSV files apart
		the "Name" column
		"""
		self.headers = []
		self.data = []
		self.hasValidData = False
		self.mandatoryDims=mandatoryDims
	def load(self, filename):
		"""Load data from a CSV file."""
		self.hasValidData = False
		with open(filename, "r") as csvfile:
			csv_reader = csv.reader(csvfile, delimiter=',', quotechar='"')
			self.headers = csv_reader.next()
			# Fill the talble
			self.data = []
			names = []
			ni = self.headers.index("Name")
			for row in csv_reader:
				# Check if the name is unique
				name = row[ni]
				if name in names:
					print('Error: Not unique name "%s" found in %s'%(name, filename))
					exit(1)
				else:
					names.append(name)
				self.data.append(row)
			csvfile.close() # Should I close this file explicitely?
			self.hasValidData = self.hasNecessaryColumns()

	def hasNecessaryColumns(self):
		""" Check if the data contains all calumns required to create a coupling."""
		return all(h in self.headers for h in (self.mandatoryDims + ["Name"]))

	def findPart(self, name):
		"""Return first first raw with the particular part name as a dictionary."""
		# First find out the index of the column "Name".
		ci = self.headers.index("Name")
		# Search for the first appereance of the name in this column.
		for row in self.data:
			if row[ci] == name:
				# Convert row to dicionary.
				return dict(zip(self.headers, row))
		return None

	def getPartName(self, index):
		"""Return part name of a row with the index *index*."""
		ci = self.headers.index("Name")
		return self.data[index][ci]

class PipeFromTable:
	"""Create a part with dimensions from a CSV table."""
	def __init__ (self, document, table):
		self.document = document
		self.table = table
	def create(self, partName, length, convertToSolid = True):
		pipe = Pipe(self.document)
		row = self.table.findPart(partName)
		if row is None:
			print("Part not found")
			return
		pipe.ID = tu(row["ID"])
		pipe.OD = tu(row["OD"])
		pipe.L = length

		part = pipe.create(convertToSolid)
		part.Label = partName
		return part


class BoxFromTable:
	"""Create a part with dimensions from a CSV table."""
	def __init__ (self, document, pipe_table, corner_table):
		self.document = document
		self.pipe_table = pipe_table
		self.corner_table = corner_table
		self.LX = tu("12 in")
		self.LY = tu("10 in")
		self.LZ = tu("8 in")

	def getCorner(self, partName):
		corner = OuterCorner(self.document)
		row = self.corner_table.findPart(partName)
		if row is None:
			print('Corner part "%s" not found'%partName)
			return
		corner.G = tu(row["G"])
		corner.H = tu(row["H"])
		corner.M = tu(row["M"])
		corner.POD = tu(row["POD"])
		corner.PID = tu(row["PID"])
		return corner
		
	def create(self, pipeName, cornerName, convertToSolid = True):
		frame_box = Box(self.document)
		frame_box.LX = self.LX
		frame_box.LY = self.LY
		frame_box.LZ = self.LZ
		# Init corner datata
		frame_box.corner = self.getCorner(cornerName)
		frame_box.G = frame_box.corner.G

		"setup pipe dimensions"
		row = self.pipe_table.findPart(pipeName)
		if row is None:
			print('Pipe part "%s" not found'%pipeName)
			return
		frame_box.PID = tu(row["ID"])
		frame_box.POD = tu(row["OD"])
		return frame_box.create(convertToSolid)

class MainDialog(QtGui.QDialog):
	QSETTINGS_APPLICATION = "OSE piping freecad macros"
	QSETTINGS_NAME = "fram bos user input"
	def __init__(self, pipeTable, cornerTable):
		super(MainDialog, self).__init__()
		self.pipeTable = pipeTable
		self.cornerTable = cornerTable
		self.initUi()

	def initUi(self): 
		Dialog = self # Added 
		self.result = -1 
		self.setupUi(self)
		# Restore previous user input. Ignore exceptions to prevent this part
		# part of the code to prevent GUI from starting, once settings are broken.
		try:
			self.restoreInput()
		except Exception as e:
			print ("Could not restore old user input!")
			print(e)
		self.show()

# The following lines are from QtDesigner .ui-file processed by pyside-uic
# pyside-uic --indent=0 create-pipe.ui -o tmp.py
#
# The file paths needs to be adjusted manually. For example
# self.label.setPixmap(QtGui.QPixmap( GetMacroPath()+"/pipe-frame-box-dimensions.png"))
# access datata in some special FreeCAD directory.
	def setupUi(self, Dialog):
		Dialog.setObjectName("Dialog")
		Dialog.resize(614, 566)
		self.verticalLayout = QtGui.QVBoxLayout(Dialog)
		self.verticalLayout.setObjectName("verticalLayout")
		self.horizontalWidget = QtGui.QWidget(Dialog)
		self.horizontalWidget.setMinimumSize(QtCore.QSize(0, 134))
		self.horizontalWidget.setLayoutDirection(QtCore.Qt.LeftToRight)
		self.horizontalWidget.setObjectName("horizontalWidget")
		self.checkBoxCreateSolid = QtGui.QCheckBox(self.horizontalWidget)
		self.checkBoxCreateSolid.setGeometry(QtCore.QRect(0, 0, 121, 26))
		self.checkBoxCreateSolid.setChecked(True)
		self.checkBoxCreateSolid.setObjectName("checkBoxCreateSolid")
		self.label_3 = QtGui.QLabel(self.horizontalWidget)
		self.label_3.setGeometry(QtCore.QRect(0, 30, 21, 25))
		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
		sizePolicy.setHorizontalStretch(0)
		sizePolicy.setVerticalStretch(0)
		sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
		self.label_3.setSizePolicy(sizePolicy)
		self.label_3.setMaximumSize(QtCore.QSize(200, 16777215))
		self.label_3.setObjectName("label_3")
		self.lineEditLX = QtGui.QLineEdit(self.horizontalWidget)
		self.lineEditLX.setGeometry(QtCore.QRect(20, 30, 71, 27))
		self.lineEditLX.setObjectName("lineEditLX")
		self.label_5 = QtGui.QLabel(self.horizontalWidget)
		self.label_5.setGeometry(QtCore.QRect(110, 30, 21, 25))
		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
		sizePolicy.setHorizontalStretch(0)
		sizePolicy.setVerticalStretch(0)
		sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth())
		self.label_5.setSizePolicy(sizePolicy)
		self.label_5.setMaximumSize(QtCore.QSize(200, 16777215))
		self.label_5.setObjectName("label_5")
		self.lineEditLY = QtGui.QLineEdit(self.horizontalWidget)
		self.lineEditLY.setGeometry(QtCore.QRect(130, 30, 71, 27))
		self.lineEditLY.setObjectName("lineEditLY")
		self.label_6 = QtGui.QLabel(self.horizontalWidget)
		self.label_6.setGeometry(QtCore.QRect(220, 30, 21, 25))
		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
		sizePolicy.setHorizontalStretch(0)
		sizePolicy.setVerticalStretch(0)
		sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth())
		self.label_6.setSizePolicy(sizePolicy)
		self.label_6.setMaximumSize(QtCore.QSize(200, 16777215))
		self.label_6.setObjectName("label_6")
		self.lineEditLZ = QtGui.QLineEdit(self.horizontalWidget)
		self.lineEditLZ.setGeometry(QtCore.QRect(240, 30, 71, 27))
		self.lineEditLZ.setObjectName("lineEditLZ")
		self.label_2 = QtGui.QLabel(self.horizontalWidget)
		self.label_2.setGeometry(QtCore.QRect(0, 70, 91, 17))
		self.label_2.setObjectName("label_2")
		self.lineEditPipeName = QtGui.QLineEdit(self.horizontalWidget)
		self.lineEditPipeName.setGeometry(QtCore.QRect(80, 70, 171, 27))
		self.lineEditPipeName.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
		self.lineEditPipeName.setObjectName("lineEditPipeName")
		self.pushButtonSelectPipe = QtGui.QPushButton(self.horizontalWidget)
		self.pushButtonSelectPipe.setEnabled(False)
		self.pushButtonSelectPipe.setGeometry(QtCore.QRect(260, 70, 121, 27))
		self.pushButtonSelectPipe.setObjectName("pushButtonSelectPipe")
		self.pushButtonSelectCorner = QtGui.QPushButton(self.horizontalWidget)
		self.pushButtonSelectCorner.setEnabled(False)
		self.pushButtonSelectCorner.setGeometry(QtCore.QRect(260, 100, 121, 27))
		self.pushButtonSelectCorner.setObjectName("pushButtonSelectCorner")
		self.lineEditCornerName = QtGui.QLineEdit(self.horizontalWidget)
		self.lineEditCornerName.setGeometry(QtCore.QRect(80, 100, 171, 27))
		self.lineEditCornerName.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
		self.lineEditCornerName.setObjectName("lineEditCornerName")
		self.label_7 = QtGui.QLabel(self.horizontalWidget)
		self.label_7.setGeometry(QtCore.QRect(0, 100, 91, 17))
		self.label_7.setObjectName("label_7")
		self.verticalLayout.addWidget(self.horizontalWidget)
		self.label = QtGui.QLabel(Dialog)
		self.label.setText("")
		self.label.setPixmap(QtGui.QPixmap(GetMacroPath()+"/pipe-frame-box-dimensions.png"))
		self.label.setAlignment(QtCore.Qt.AlignCenter)
		self.label.setObjectName("label")
		self.verticalLayout.addWidget(self.label)
		self.buttonBox = QtGui.QDialogButtonBox(Dialog)
		self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
		self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
		self.buttonBox.setObjectName("buttonBox")
		self.verticalLayout.addWidget(self.buttonBox)

		self.retranslateUi(Dialog)
		QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept)
		QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject)
		QtCore.QMetaObject.connectSlotsByName(Dialog)

	def retranslateUi(self, Dialog):
		Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Create frame box", None, QtGui.QApplication.UnicodeUTF8))
		self.checkBoxCreateSolid.setText(QtGui.QApplication.translate("Dialog", "Create Solid", None, QtGui.QApplication.UnicodeUTF8))
		self.label_3.setText(QtGui.QApplication.translate("Dialog", "LX:", None, QtGui.QApplication.UnicodeUTF8))
		self.lineEditLX.setText(QtGui.QApplication.translate("Dialog", "12 in", None, QtGui.QApplication.UnicodeUTF8))
		self.label_5.setText(QtGui.QApplication.translate("Dialog", "LY:", None, QtGui.QApplication.UnicodeUTF8))
		self.lineEditLY.setText(QtGui.QApplication.translate("Dialog", "8 in", None, QtGui.QApplication.UnicodeUTF8))
		self.label_6.setText(QtGui.QApplication.translate("Dialog", "LZ:", None, QtGui.QApplication.UnicodeUTF8))
		self.lineEditLZ.setText(QtGui.QApplication.translate("Dialog", "4 in", None, QtGui.QApplication.UnicodeUTF8))
		self.label_2.setText(QtGui.QApplication.translate("Dialog", "Pipe name:", None, QtGui.QApplication.UnicodeUTF8))
		self.lineEditPipeName.setText(QtGui.QApplication.translate("Dialog", "NPS 1 in PVC SCH 40", None, QtGui.QApplication.UnicodeUTF8))
		self.pushButtonSelectPipe.setText(QtGui.QApplication.translate("Dialog", "Select Pipe", None, QtGui.QApplication.UnicodeUTF8))
		self.pushButtonSelectCorner.setText(QtGui.QApplication.translate("Dialog", "Select Corner", None, QtGui.QApplication.UnicodeUTF8))
		self.lineEditCornerName.setText(QtGui.QApplication.translate("Dialog", "F0013WE", None, QtGui.QApplication.UnicodeUTF8))
		self.label_7.setText(QtGui.QApplication.translate("Dialog", "Pipe name:", None, QtGui.QApplication.UnicodeUTF8))

	def accept(self):
		"""User clicked OK"""
		# Update active document.  If there is none, show a warning message and do nothing.
		document = App.activeDocument()
		if document is not None:
			# Get dimensions from the table
			box = BoxFromTable(document, self.pipeTable, self.cornerTable)
			box.LX = tu(self.lineEditLX.text())
			if box.LX == "":
				msgBox = QtGui.QMessageBox()
				msgBox.setText("Set LX length.")
				msgBox.exec_()
				return
			box.LY = tu(self.lineEditLY.text())
			if box.LY == "":
				msgBox = QtGui.QMessageBox()
				msgBox.setText("Set LY length.")
				msgBox.exec_()
				return
			box.LZ = tu(self.lineEditLZ.text())
			if box.LZ == "":
				msgBox = QtGui.QMessageBox()
				msgBox.setText("Set LZ length.")
				msgBox.exec_()
				return
		
			pipeName = self.lineEditPipeName.text()
			if pipeName == "":
				msgBox = QtGui.QMessageBox()
				msgBox.setText("Enter pipe name.")
				msgBox.exec_()
				return
			cornerName = self.lineEditCornerName.text()
			if cornerName == "":
				msgBox = QtGui.QMessageBox()
				msgBox.setText("Enter corner name.")
				msgBox.exec_()
				return

			createSolid = self.checkBoxCreateSolid.isChecked()
			box.create(pipeName, cornerName, createSolid)
			document.recompute()
			# Save user input for the next dialog call.
			self.saveInput()
			# Call parent class.
			super(MainDialog, self).accept()
		else:
			text = "I have not found any active document were I can create a frame box.\n"\
				"Use menu File->New to create a new document first, "\
				"then try to create the box again."
			msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the frame box failed.", text)
			msgBox.exec_()

	def saveInput(self):
		"""Store user input for the next run."""
		settings = QtCore.QSettings(MainDialog.QSETTINGS_APPLICATION, MainDialog.QSETTINGS_NAME)
		check = self.checkBoxCreateSolid.checkState()
		settings.setValue("checkBoxCreateSolid", int(check))
		settings.setValue("lineEditLX", self.lineEditLX.text())
		settings.setValue("lineEditLY", self.lineEditLY.text())
		settings.setValue("lineEditLZ", self.lineEditLZ.text())
		settings.setValue("lineEditPipeName", self.lineEditPipeName.text())
		settings.setValue("lineEditCornerName", self.lineEditCornerName.text())
		settings.sync()

	def restoreInput(self):
		settings = QtCore.QSettings(MainDialog.QSETTINGS_APPLICATION, MainDialog.QSETTINGS_NAME)
		checkState = QtCore.Qt.CheckState(int(settings.value("checkBoxCreateSolid")))
		self.checkBoxCreateSolid.setCheckState(checkState)
		text = settings.value("lineEditLX")
		if text is not None:
			self.lineEditLX.setText(text)
		text = settings.value("lineEditLY")
		if text is not None:
			self.lineEditLY.setText(text)
		text = settings.value("lineEditLZ")
		if text is not None:
			self.lineEditLZ.setText(text)
		text = settings.value("lineEditPipeName")
		if text is not None:
			self.lineEditPipeName.setText(text)
		text = settings.value("lineEditCornerName")
		if text is not None:
			self.lineEditCornerName.setText(text)

def GuiCheckPipeTable():
	# Check if the CSV file exists.
	if os.path.isfile(PIPE_CSV_TABLE_PATH) == False:
		text = "This macro requires %s  but this file does not exist."%(PIPE_CSV_TABLE_PATH)
		msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the box failed.", text)
		msgBox.exec_()
		exit(1) # Error

	print("Trying to load CSV file with pipe dimensions: %s"%PIPE_CSV_TABLE_PATH) 
	table = CsvTable(PIPE_DIMENSIONS_USED)
	table.load(PIPE_CSV_TABLE_PATH)

	if table.hasValidData == False:
		text = 'Invalid %s.\n'\
			'It must contain columns %s.'%(PIPE_CSV_TABLE_PATH, ", ".join(PIPE_DIMENSIONS_USED))
		msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the box failed.", text)
		msgBox.exec_()
		exit(1) # Error
	return table

def GuiCheckCornerTable():
	# Check if the CSV file exists.
	if os.path.isfile(CORNER_CSV_TABLE_PATH) == False:
		text = "This macro requires %s  but this file does not exist."%(CORNER_CSV_TABLE_PATH)
		msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the box failed.", text)
		msgBox.exec_()
		exit(1) # Error

	print("Trying to load CSV file with dimensions: %s"%CORNER_CSV_TABLE_PATH) 
	table = CsvTable(CORNER_DIMENSIONS_USED)
	table.load(CORNER_CSV_TABLE_PATH)

	if table.hasValidData == False:
		text = 'Invalid %s.\n'\
			'It must contain columns %s.'%(CORNER_CSV_TABLE_PATH, ", ".join(CORNER_DIMENSIONS_USED))
		msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the box failed.", text)
		msgBox.exec_()
		exit(1) # Error
	return table

# Test macros.
def TestBox():
	document = App.activeDocument()
	box = Box(document)
	box.create(True)
	document.recompute()

def TestTable():
	document = App.activeDocument()
	pipe_table = CsvTable(PIPE_DIMENSIONS_USED)
	corner_table = CsvTable(CORNER_DIMENSIONS_USED)
	pipe_table.load(PIPE_CSV_TABLE_PATH)
	corner_table.load(CORNER_CSV_TABLE_PATH)
	box = BoxFromTable(document, pipe_table, corner_table)
	pipeName = pipe_table.getPartName(0)
	cornerName = corner_table.getPartName(0)

	print("Using pipe %s"%pipeName)
	print("Using corner %s"%cornerName)
	
	box.create(pipeName, cornerName, False)
	document.recompute()

#TestBox()
#TestTable()
pipeTable = GuiCheckPipeTable() # Open a CSV file, check its content, and return it as a CsvTable object.
cornerTable = GuiCheckCornerTable() # Open a CSV file, check its content, and return it as a CsvTable object.
form = MainDialog(pipeTable, cornerTable)


