from gk_global import *
from gk_dynamic_item import *
import gk_transformable 

import gk_shape_info
import copy

#----------------------------------------------------------------------------
# Basic shape
#----------------------------------------------------------------------------

class gds_shape(list, gds_static_item, gk_transformable.gds_transformable):
	def __init__(self, coordinate_list=[]):
		if isinstance(coordinate_list, gds_shape):
			# calculate the coordinates before copying
			coordinate_list.calc()
		list.__init__(self,coordinate_list)
		self.__size_info__ =  None

	def clear(self):
		l = list.__len__(self)
		list.__delslice__(self, 0, l)

	def is_closed(self):
		return (self[0] == self[-1])
	closed = property(is_closed)

	def transform(self, transformation):
		# applies a selected transformation
		for i in range(len(self)):
			self[i] = transformation.apply_to_coord(self[i])
		return self
	
	def transform_copy(self, transformation):
		S = gds_shape()
		for c in self:
			S.append(transformation.apply_to_coord(c))
		return S

	def reverse_transform(self, transformation):
		# applies a selected transformation
		if not isinstance(transformation, gds_reversible_transform):
			raise TypeError("gds_shape.reverse_transform() can only be used with a gds_reversible_transformation, not " + str(type(transformation)))
		for i in len(self):
			self[i] = transformation.reverse_on_coord(self[i])
		return self

	def move(self, position):
		# special function for speed
		for i in range(0, len(self)) :
			self[i] = (self[i][0] + position[0], self[i][1] + position[1])
		return self


	def __calc__(self):
		self.__reset_size_info__()
		#override this function to add coordinates

	def size_info(self):
		if self.__size_info__ == None: self.__size_info__ = gk_shape_info.gds_size_info(self)
		return self.__size_info__

	def __reset_size_info__(self):
		self.__size_info__ = None

	# Overload list functions that have no use
	def sort(self):
		print >> stderr, "Warning: Sort is not supported in gds_shape"

	def __add__(self, pointlist):
		self.__reset_size_info__()
		if isinstance(pointlist, gds_shape):
			if not pointlist.is_calculated(): pointlist.calc()
		if isinstance(pointlist, list): # other shape
			return list.__add__(self,pointlist)
		else: # indiviual points
			return list.__add__(self, [pointlist])


	def close(self):
		# closing the shape does not reset size info
		if not self.is_closed():
			self.append(self[0])

	# overload all list functions that add/delete or modify items
	def __iadd__(self, pointlist):
		self.__reset_size_info__()
		if isinstance(pointlist, gds_shape):
			if not pointlist.is_calculated(): pointlist.calc()
		if isinstance(pointlist, list):
			return list.__iadd__(self,pointlist)
		else: # indiviual points
			return list.__iadd__(self, [pointlist])

	def __delitem__(self, point):
		self.__reset_size_info__()
		return list.__delitem__(self,point)

	def __delslice__(self, i, j):
		self.__reset_size_info__()
		return list.__delslice__(list, i, j)

	def __imul__(self, integer):
		self.__reset_size_info__()
		return list.imul__(self, integer)

	def __setitem__(self, key, value):
		self.__reset_size_info__()
		return list.__setitem__(self, key,value)

	def __setslice__(self, i, j, pointlist):
		self.__reset_size_info__()
		return list.__setslice__(self, i, j, pointlist)

	def append(self, point):
		self.__reset_size_info__()
		return list.append(self, point)

	# set size_info to none
	def extend(self, other_shape):
		self.__reset_size_info__()
		if isinstance(other_shape, gds_shape):
			if not other_shape.is_calculated():
				other_shape.calc()
		list.extend(self,other_shape)

	def insert(self, i, item):
		self.__reset_size_info__()
		return list.insert(self, i, item)

	def pop(self, point):
		self.__reset_size_info__()
		return list.pop(self, point)

	def remove(self, item):
		self.__reset_size_info__()
		return list.remove(self, item)



# empty shape for quick reference
empty_shape = gds_shape()

#----------------------------------------------------------------------------
# dynamic shape: that fills its own parameters
#----------------------------------------------------------------------------
class gds_dynamic_shape(gds_dynamic_item, gds_shape):
	__static_item_class__ = gds_shape
	def __init__(self):
		gds_shape.__init__(self)
		self.__reset_calc__()

	def clear(self):
		l = list.__len__(self)
		list.__delslice__(self, 0, l)
		self.__reset_calc__()

	# transformations: make structure static first!
	# (this can be overloaded for certain shapes)
	def transform(self, transformation):
		# applies a selected transformation
		self.__make_static__()
		return gds_shape.transform(self, transformation)
		
	def reverse_transform(self, transformation):
		# applies a selected transformation in the reverse form
		self.__make_static__()
		return super(gds_shape, self).reverse_transform(transformation)

	def move(self, position):
		self.__make_static__()
		return super(gds_shape, self).move(position)

	def is_closed(self):
		if not self.is_calculated(): self.calc()
		return gds_shape.is_closed(self)



	def __reset_calc__ (self, value = False):
		if not value: self.__size_info__ = None 
		gds_dynamic_item.__reset_calc__(self, value)

	def extend(self, listofpoints):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.extend(self, listofpoints)

	def append(self, point):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.append(self, point)

	# overload all list functions that add/delete or modify items
	def __iadd__(self, pointlist):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.__iadd__(self, pointlist)

	def __delitem__(self, point):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.__delitem__(self, point)

	def __delslice__(self, i, j):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return list.__delslice__(self, i, j)

	def __imul__(self, integer):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.imul__(self,integer)

	def __setitem__(self, key, value):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.__setitem__(self,key,value)

	def __setslice__(self, i, j, pointlist):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.__setslice__(self,i, j, pointlist)


	def insert(self, i, item):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.insert(self,i, item)

	def pop(self, point):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.pop(self,point)

	def remove(self, item):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.remove(self,item)

	def reverse(self):
		if not self.is_call_internal(): self.__make_static__(gds_shape)
		return gds_shape.reverse(self)

	# overload all list functions that require the point lists
	def __mul__(self, integer):
		if not self.is_calculated(): self.calc()
		return gds_shape.__mul__(self,integer)

	def __add__(self, point):
		if not self.is_calculated(): self.calc()
		return gds_shape.__add__(self,point)

	def __contains__(self, point):
		if not self.is_calculated(): self.calc()
		return gds_shape.__add__(self,point)

	def __eq__(self, sh):
		if not self.is_calculated(): self.calc()
		return gds_shape.__eq__(self,sh)

	def __gt__(self, sh):
		if not self.is_calculated(): self.calc()
		return gds_shape.__gt__(self, sh)

	def __ge__(self, sh):
		if not self.is_calculated(): self.calc()
		return gds_shape.__ge__(self, sh)

	def __getitem__(self, index):
		if not self.is_calculated(): self.calc()
		return gds_shape.__getitem__(self, index)

	def __iter__(self):
		if not self.is_calculated(): self.calc()
		return gds_shape.__iter__(self)

	def __lt__(self, sh):
		if not self.is_calculated(): self.calc()
		return gds_shape.__lt__(self,sh)

	def __le__(self, sh):
		if not self.is_calculated(): self.calc()
		return gds_shape.__le__(self,sh)

	def __len__(self):
		if not self.is_calculated(): self.calc()
		return gds_shape.__len__(self)

	def __ne__(self, sh):
		if not self.is_calculated(): self.calc()
		return gds_shape.__ne__(self,sh)

	def __rmul__(self, number):
		if not self.is_calculated(): self.calc()
		return gds_shape.__rmul__(self, number)

	def count(self):
		if not self.is_calculated(): self.calc()
		return gds_shape.remove(self, i, j)

	def index(self, item):
		if not self.is_calculated(): self.calc()
		return gds_shape.index(self, item)

	def sort(self):
		if not self.is_calculated(): self.calc()
		return gds_shape.sort(self)

	def size_info(self):
		if not self.is_calculated(): self.calc()
		return gds_shape.size_info(self)


	def __copy__(self):
		t = self.__class__
		# temprorily static
		self.__class__ = gds_shape
		c = copy.copy(self)
		c.__class__ = t
		self.__class__ = t
		return c	

	def __deepcopy__(self, memo):
		t = self.__class__
		# temprorily static
		self.__class__ = gds_shape
		c = copy.deepcopy(self, memo)
		c.__class__ = t
		self.__class__ = t
		return c
