from .. import constants
from ..geometry.shape import gds_shape, shape_property
from ..geometry.shapes import basic as shapes
from .elements import gds_dynamic_element, gds_layer_element, gds_compound_element, gds_compound_layer_element

from ..properties.descriptor import set_function_property, restricted_property
from ..properties.restrictions import restrict_value_list

from ..geometry import shape_info
from ..geometry import shape_modifier
import copy

from ..properties.predefined import number_property, bool_property

__all__ = ["gds_shape_element",
	   "gds_compound_layer_shape_element",
	   "gds_compound_shape_element",
	   "gds_layer_shape_element",
	   "gds_single_shape_element",
	   "el_arc_path",
	   "el_bend_path",
	   "el_bend_rel",
	   "el_boundary",
	   "el_circle",
	   "el_circle_path",
	   "el_cross",
	   "el_cross_path",
	   "el_ellipse",
	   "el_ellipse_arc",
	   "el_ellipse_arc_path",
	   "el_ellipse_path",
	   "el_hexagon",
	   "el_hexagon_path",
	   "el_line",
	   "el_parabolic",
	   "el_path",
	   "el_radial_line",
	   "el_radial_wedge",
	   "el_rect",
	   "el_rect_path",
	   "el_regular_polygon",
	   "el_regular_polygon_path",
	   "el_ring_segment",
	   "el_round_rect",
	   "el_round_rect_path",
	   "el_wedge"]
	
	   

class gds_shape_element(gds_dynamic_element):
	shape = shape_property(required = True)
	def __init__(self, shape, transformation = None, **kwargs):
		super(gds_shape_element, self).__init__(shape = shape, transformation = transformation, **kwargs )

	def is_calculated(self):
		return (self.shape.is_calculated() and gds_dynamic_element.is_calculated(self))

	def __calc__(self):
		self.shape.calc()
		gds_dynamic_element.__calc__(self)

	def size_info(self):
		return self.shape.size_info().transform_copy(self.transformation)


	def __copy__(self):
		self.calc()
		return gds_shape_element(self.shape, self.line_width, self.path_type, self.transformation)		

	#def __deepcopy__(self, memo):
		#return gds_shape_element(copy.deepcopy(self.shape), self.line_width, self.path_type, copy.deepcopy(self.transformation))		

	def is_empty(self):
		return self.shape.is_empty()

	def flat_copy(self, level = -1):
		S = gds_shape_element(self.shape, self.transformation)
		S.expand_transform()
		return S

	def expand_transform(self):
		if not self.transformation.is_identity():
			self.__make_static__()
			self.shape = self.shape.transform_copy(self.transformation)
			from ..geometry.transforms.identity import gds_identity_transform
			self.transformation = gds_identity_transform()
		return self

##########################################
# Basic Class
##########################################
class gds_single_shape_element(gds_shape_element):
	def __init__(self, shape, line_width = 0.0, path_type = constants.PATH_TYPE_NORMAL, transformation = None, **kwargs):
		super(gds_single_shape_element, self).__init__(shape = shape, line_width = line_width, path_type = path_type, transformation = transformation, **kwargs)

	def set_line_width(self, new_line_width):
		self.absolute_line_width = new_line_width < 0
		self.__line_width__ = abs(new_line_width)
		if self.__line_width__ > 0: 
			self.__min_length = 2
		else: 
			self.__min_length = 3
	line_width = set_function_property("__line_width__", set_line_width, required = True)
	path_type = restricted_property(restriction = restrict_value_list(constants.PATH_TYPES), default = constants.PATH_TYPE_NORMAL)
	absolute_line_width = bool_property(default = False)

	def size_info(self):
		if self.line_width == 0.0:
			return self.shape.size_info().transform_copy(self.transformation)
		else:
			# this is slower, but accurate
			from ..geometry.shapes.modifiers import gds_shape_path
			return gds_shape_path(self.shape, self.line_width, self.path_type).size_info().transform_copy(self.transformation)
			# this is fast, but inaccurate
			#return self.shape.size_info().transform_copy(self.transformation)


	def __copy__(self):
		return gds_single_shape_element(self.shape, self.line_width, self.path_type, self.transformation)		

	#def __deepcopy__(self, memo):
		#return gds_shape_element(copy.deepcopy(self.shape), self.line_width, self.path_type, copy.deepcopy(self.transformation))		

	def flat_copy(self, level = -1):
		S = gds_single_shape_element(self.shape, self.line_width, self.path_type, self.transformation)
		S.expand_transform()
		return S


	def is_empty(self):
		self.calc()
		S = self.shape
		return S.is_empty() or S.len_without_identicals() < self.__min_length

gds_single_shape_element.__static_item_class__ = gds_single_shape_element

##########################################
# With Layer 
##########################################
class gds_layer_shape_element(gds_single_shape_element, gds_layer_element):
	def __init__(self, layer, shape, line_width = 0.0, path_type = constants.PATH_TYPE_NORMAL, transformation = None, **kwargs):
		super(gds_layer_shape_element, self).__init__(layer = layer, 
							      shape = shape, 
							      line_width = line_width, 
							      path_type = path_type, 
							      transformation = transformation, 
							      **kwargs)

	def is_calculated(self):
		return (self.shape.is_calculated() and gds_layer_element.is_calculated(self))

	def __calc__(self):
		self.shape.calc()
		gds_layer_element.__calc__(self)


	def __copy__(self):
		self.calc()
		return gds_layer_shape_element(self.layer, self.shape, self.line_width, self.path_type, self.transformation)		

	def flat_copy(self, level = -1):
		S = gds_layer_shape_element(self.layer, self.shape, self.line_width, self.path_type, self.transformation)
		S.expand_transform()
		return S


	#def __deepcopy__(self, memo):
		#return gds_layer_shape_element(self.layer, copy.deepcopy(self.shape), self.line_width, self.path_type, copy.deepcopy(self.transformation))		

gds_layer_shape_element.__static_item_class__ = gds_layer_shape_element


def el_boundary (layer, shape, line_width = 0.0):
	s = gds_layer_shape_element(layer, shape, line_width, constants.PATH_TYPE_NORMAL)
	return s

def el_path (layer, shape, line_width = 1.0, path_type = constants.PATH_TYPE_NORMAL):
	s = gds_layer_shape_element(layer, shape, line_width, path_type)
	return s



##########################################
# Compound Shape Element
##########################################
class gds_compound_shape_element(gds_compound_element, gds_shape_element):
	def __init__(self, shape, transformation = None, **kwargs):
		super(gds_compound_shape_element, self).__init__(shape = shape, transformation = transformation, **kwargs)

	def is_calculated(self):
		return (self.shape.is_calculated() and gds_compound_element.is_calculated(self))

	def __calc__(self):
		self.shape.calc()
		gds_compound_element.__calc__(self)

	def __copy__(self):
		self.calc()
		return gds_compound_shape_element(self.shape, self.transformation)

	#def __deepcopy__(self, memo):
		#return gds_shape_element(self.layer, copy.deepcopy(self.shape), self.line_width, self.path_type, copy.deepcopy(self.transformation))		

gds_compound_shape_element.__static_item_class__ = gds_compound_element

##########################################
# Compound Shape Element with layer
##########################################
class gds_compound_layer_shape_element(gds_compound_layer_element, gds_shape_element):
	def __init__(self, layer, shape,transformation = None, **kwargs):
		super(gds_compound_layer_shape_element, self).__init__(layer = layer,
								       shape = shape,
								       transformation = transformation, 
								       **kwargs)
		
	def is_calculated(self):
		return (self.shape.is_calculated() and gds_compound_layer_element.is_calculated(self))

	def __calc__(self):
		self.shape.calc()
		gds_compound_element.__calc__(self)

	def __copy__(self):
		return gds_compound_layer_shape_element(self.layer, self.shape, self.transformation)		

	#def __deepcopy__(self, memo):
		#return gds_shape_element(self.layer, copy.deepcopy(self.shape), self.line_width, self.path_type, copy.deepcopy(self.transformation))		


###########################################
# Circles and ellipses (and arcs)
###########################################

def el_ellipse_arc(layer, center = (0.0,0.0), box_size = (1.0,1.0), line_width = 0.5, start_angle=0.0, end_angle = 90.0, angle_step = None, path_type = constants.PATH_TYPE_NORMAL, clockwise = False):
	shape = shapes.shape_ellipse_arc(center, box_size, line_width, start_angle, end_angle, angle_step , clockwise)
	return el_path (layer, coordinates, line_width, path_type )

def el_circle(layer, center = (0.0, 0.0),radius = 1.0, line_width = 0.0, angle_step = None):
	coordinates = shapes.shape_circle(center, radius, angle_step)
	return el_boundary(layer, coordinates, line_width)

def el_ellipse (layer, center = (0.0, 0.0),box_size = (1.0,1.0), line_width = 0.0, angle_step = None):
	coordinates = shapes.shape_ellipse(center, box_size, angle_step)
	return el_boundary(layer, coordinates, line_width)

def el_ring_segment(layer, center, angle_start, angle_end, inner_radius, outer_radius, angle_step = None):
	coordinates = shapes.shape_ring_segment(center, angle_start, angle_end, inner_radius, outer_radius, angle_step)
	return el_boundary(layer, coordinates)

def el_rect (layer, center = (0.0, 0.0),box_size = (1.0, 1.0), line_width=0):
	coordinates = shapes.shape_rect(center, box_size)
	return el_boundary(layer, coordinates, line_width)

def el_round_rect (layer, center = (0.0, 0.0), box_size = (1.0, 1.0), radius = 0, line_width = 0.0, angle_step = None):
	coordinates = shapes.shape_round_rect(center, box_size, radius, angle_step)
	return el_boundary(layer, coordinates, line_width)

def el_hexagon (layer, center = (0.0, 0.0),radius = 1.0, line_width = 0.0):
	coordinates = shapes.shape_hexagon(center, radius)
	return el_boundary (layer,coordinates, line_width)

def el_regular_polygon (layer, center = (0.0,0.0), radius = 1.0, no_sides = 8, line_width=0.0):
	coordinates = shapes.shape_regular_polygon(center, radius, no_sides)
	return el_boundary (layer,coordinates, line_width)

def el_cross (layer, center = (0.0,0.0), box_size = 20.0, thickness = 5.0, line_width = 0.0):
	coordinates = shapes.shape_cross(center, box_size, thickness)
	return el_boundary (layer,coordinates, line_width)

def el_wedge (layer, begin_coord=(0.0,0.0), end_coord = (10.0, 0.0), begin_width = 3.0, end_width = 1.0, line_width = 0.0):
	coordinates = shapes.shape_wedge (begin_coord, end_coord , begin_width, end_width )
	return el_boundary (layer,coordinates, line_width)

def el_radial_wedge(layer, center, inner_radius, outer_radius, inner_width, outer_width, angle):
	coordinates = shapes.shape_radial_wedge (center, inner_radius, outer_radius, inner_width, outer_width, angle)
	return el_boundary(layer, coordinates)

def el_parabolic (layer, begin_coord=(0.0,0.0), end_coord = (10.0, 0.0), begin_width = 3.0, end_width = 1.0, line_width = 0.0):
	coordinates = shapes.shape_parabolic (begin_coord, end_coord , begin_width, end_width )
	return el_boundary (layer,coordinates, line_width)

def el_line (layer, begin_coord = (0.0,0.0), end_coord=(10.0, 0.0), line_width=1.0, path_type = constants.PATH_TYPE_NORMAL):
	coordinates = gds_shape([begin_coord, end_coord], False)
	return el_path(layer, coordinates, line_width, path_type )

def el_radial_line(layer, center, inner_radius, outer_radius, width, angle):
	length = abs(outer_radius - inner_radius)
	return el_line(layer,
					(center[0] + inner_radius * cos(angle*DEG2RAD), center[1] + inner_radius * sin(angle*DEG2RAD) ),
					(center[0] + outer_radius * cos(angle*DEG2RAD), center[1] + outer_radius * sin(angle*DEG2RAD) ),
							width)


def el_bend_path (layer, bend_start = (0.0,0.0), radius = 1.0, line_width = 0.5, start_angle=0.0, end_angle = 90.0, angle_step = None, path_type = constants.PATH_TYPE_NORMAL, clockwise = False):
	coordinates = shapes.shape_bend(bend_start, radius, start_angle, end_angle , angle_step, clockwise )
	return el_path (layer, coordinates, line_width, path_type )

def el_bend_rel (layer, bend_start = (0.0,0.0), radius = 1.0, line_width = 0.5, start_angle=0.0, angle_amount = 90.0, angle_step = None, path_type = constants.PATH_TYPE_NORMAL):
	coordinates = shapes.shape_bend_rel(bend_start, radius, start_angle, angle_amount , angle_step)
	return el_path (layer, coordinates, line_width, path_type )

def el_arc_path (layer, center = (0.0,0.0), radius = 1.0, line_width = 0.5, start_angle=0.0, end_angle = 90.0, angle_step = None, path_type = constants.PATH_TYPE_NORMAL, clockwise = False):
	coordinates = shapes.shape_arc(center, radius, start_angle, end_angle , angle_step, clockwise )
	return el_path (layer, coordinates, line_width, path_type )

def el_ellipse_arc_path (layer, center = (0.0,0.0),box_size = (1.0,1.0), line_width = 0.5, start_angle=0.0, end_angle = 90.0, angle_step = None, path_type = constants.PATH_TYPE_NORMAL, clockwise = False):
	coordinates = shapes.shape_ellipse_arc(center, box_size, start_angle, end_angle , angle_step, clockwise )
	return el_path (layer, coordinates, line_width, path_type )

def el_ellipse_path (layer, center = (0.0,0.0), box_size = (1.0,1.0), line_width = 0.5, angle_step = None):
	coordinates = shapes.shape_ellipse(center, box_size, angle_step)
	return el_path(layer, coordinates, line_width)

def el_circle_path (layer, center = (0.0,0.0), radius = 1.0, line_width = 0.5, angle_step = None):
	coordinates = shapes.shape_circle(center, radius, angle_step)
	return el_path(layer, coordinates, line_width)

def el_rect_path (layer, center = (0.0, 0.0), box_size = (1.0, 1.0), line_width=1.0):
	coordinates = shapes.shape_rect(center, box_size)
	return el_path(layer, coordinates, line_width)

def el_round_rect_path (layer, center = (0.0, 0.0), box_size = (1.0, 1.0), radius = 0,  line_width = 0.5, angle_step = None):
	coordinates = shapes.shape_round_rect(center, box_size, radius, angle_step)
	return el_path(layer, coordinates, line_width)

def el_hexagon_path (layer, center = (0.0,0.0), radius = 1.0, line_width=0.5):
	coordinates = shapes.shape_hexagon(center, radius)
	return el_path (layer,coordinates, line_width)

def el_regular_polygon_path (layer, center = (0.0,0.0), radius = 1.0, no_sides = 8, line_width=0.5):
	coordinates = shapes.shape_regular_polygon(center, radius, no_sides)
	return el_path (layer,coordinates, line_width)

def el_cross_path (layer, center = (0.0,0.0), box_size = 20.0, thickness = 5.0, line_width = 0.5):
	coordinates = shapes.shape_cross(center, box_size, thickness)
	return el_path (layer,coordinates, line_width)

