
from gk_output import *

from time import *
from sys import *
from binascii import *
from struct import *

from gk_global import *
from gk_shape import *
from gds_records import *
from gk_shape_modify import *

import copy

class gds_output_gdsii(basic_gds_output):

	def __init__(self, output_stream = sys.stdout):
		basic_gds_output.__init__(self,output_stream)
		if sys.platform == "win32":
			import os, msvcrt
			msvcrt.setmode(self.o_stream.fileno(), os.O_BINARY)

	def write(self, item):
		self.o_stream.write(a2b_hex(self.str(item)))

	#----------------------------------------------------------------------------
	#Layout-level output functions
	#----------------------------------------------------------------------------

	def __list_library_header__(self, library):
		self.init_library_header(library)
		return [ __str_record__(RecordTypeHeader, __hex_int2__(5)),
			 __str_record__(RecordTypeBgnLib, __hex_date__(library.modified) + __hex_date__(library.accessed)),
			 __str_record__(RecordTypeLibName, __hex_text__(library.name)),
			 __str_record__(RecordTypeUnits, __hex_float__(self.library.grid/self.library.unit) + __hex_float__(self.library.grid))
		]

	def __list_library_footer__(self):
		self.fini_library_footer()
		return __str_record__(RecordTypeEndLib)

	def __list_structure_header__(self, item):
		sname = self.scramble_structure_name(item.name)
		return [__str_record__(RecordTypeBgnStr, __hex_date__(item.created) + __hex_date__(item.modified)),
			__str_record__(RecordTypeStrName, __hex_text__(sname))
			]

	#generate the footer for any structure
	def __list_structure_footer__(self, item):
		return __str_record__(RecordTypeEndStr)

	#----------------------------------------------------------------------------
	#Element level output functions
	#----------------------------------------------------------------------------

	#text
	def list_label_element (self, item, additional_transform = None):
		T = item.transformation + additional_transform # make a copy because there is also the height
		coordinates =  [T.__translate__(item.coordinate)]
		T.magnification *= item.height
		L = [ __str_record__(RecordTypeText),
		      self.__str_layer__(item.layer),
		      __str_record__(RecordTypeTextType, __hex_int2__(0)),
		      __str_record__(RecordTypePresentation, __hex_int2__( (item.h_alignment + 4 * item.v_alignment + 8 * item.font%4))),
		      __str_record__(RecordTypePathType, __hex_int2__(1))]
		L += __list_transformation__(T)
		L += [self.__str_coordinatelist__(coordinates),
		      __str_record__(RecordTypeString, __hex_text__(item.text)),
		      __str_record__(RecordTypeEndEl)]
		return L

	#references

	def list_sref_element (self, item, additional_transform = None):

		T = item.transformation + gds_translation(item.position) + additional_transform
		coordinates = gds_shape((0.0, 0.0)).transform(T)
		sname = self.scramble_structure_name(item.reference.name)
		L = [ __str_record__(RecordTypeSRef),
		      __str_record__(RecordTypeSName, __hex_text__(sname))]
		L += __list_transformation__(T)
		L += [self.__str_coordinatelist__(coordinates), 
		      __str_record__(RecordTypeEndEl)]
		return L

	def list_aref_element (self, item, additional_transform = None):
		T = item.transformation + gds_translation(item.origin) + additional_transform
		coordinates = gds_shape([(0.0, 0.0), (item.n_o_periods[0] * item.period[0], 0.0),(0.0, item.n_o_periods[1] * item.period[1])]).transform(T)

		sname = self.scramble_structure_name(item.reference.name)
		L = [ __str_record__(RecordTypeARef),
		      __str_record__(RecordTypeSName, __hex_text__(sname)),
		      __str_record__(RecordTypeColRow, __hex_int2__(item.n_o_periods[0]) + __hex_int2__(item.n_o_periods[1]))]
		L += __list_transformation__(T)
		L += [self.__str_coordinatelist__(coordinates), 
		      __str_record__(RecordTypeEndEl)]
		return L

	def list_box_element (self, item, additional_transform = None):
		T = item.transformation + additional_transform
		coordinates = T(shape_rect(item.center, item.box_size)).tolist()
		L = [__str_record__(RecordTypeBox),
		     self.__str_layer__(item.layer),
		     __str_record__ (RecordTypeBoxType, __hex_int2__(0)), 
		     self.__str_coordinatelist__(coordinates), 
		     __str_record__(RecordTypeEndEl)]
		return L

	def list_path_element (self, layer, coordinates, line_width, path_type):
		L = [__str_record__ (RecordTypePath),
		     self.__str_layer__(layer),
		     __str_record__ (RecordTypeDataType, __hex_int2__(0)),
		      __str_record__ (RecordTypePathType, __hex_int2__(path_type)),
		      __str_record__ (RecordTypeWidth, __hex_int4__(self.__db_value__(line_width))),
		     self.__str_coordinatelist__(coordinates),
		     __str_record__(RecordTypeEndEl)]
		return L

	def list_boundary_element (self, layer, coordinates):
		L = [__str_record__ (RecordTypeBoundary),
		     self.__str_layer__(layer),
		     __str_record__ (RecordTypeDataType, __hex_int2__(0)),
		     self.__str_coordinatelist__(coordinates),
		     __str_record__(RecordTypeEndEl)]
		return L

	#----------------------------------------------------------------------------
	# unit conversion
	#----------------------------------------------------------------------------

	def __db_value__(self, value):
		#convents absolute coordinates to database units
		return round(self.__scale_value__(value) * (self.library.grids_per_unit))

	#generate coordinate strings from a list of coordinates
	def __str_coordinatelist__ (self, coordinates):
		ret_data = []
		for c in coordinates:
			ret_data.append(__hex_int4__(self.__db_value__(c[0])))
			ret_data.append(__hex_int4__(self.__db_value__(c[1])))
		return __str_record__(RecordTypeXY, "".join(ret_data))

	def __str_layer__(self, layer):
		return __str_record__(RecordTypeLayer, __hex_int2__(layer.number))		



#----------------------------------------------------------------------------
# Low-level output
#----------------------------------------------------------------------------

#Generate record
def __str_record__(record_type, hex_data=""):
	length = len(hex_data)/2 + 4
	return __hex_int2__(length) + __hex_int2__(record_type) + hex_data

def __hex_int2__ (number):
	return __hex_text__(pack(">H",number))

def __hex_int4__ (number):
	return __hex_text__(pack(">l",number))

def __hex_float__(number):
	B1 = 0
	B2 = 0
	S3 = 0
	L4 = 0
	if not number == 0:
		if number < 0:
			B1 = 128
			number = abs(number)
		E = int(ceil(log(number)/log(16)))
		inumber = long(number * 16L**(-E+14))
		B1 += E+64
		B2 = (inumber/281474976710656)%256
		if B2 == 0:
			B2 = 16
			B1 += 1
		S3 = (inumber%281474976710656)/4294967296L
		L4 = inumber%4294967296L
	return __hex_text__(pack(">BBHL",B1, B2, S3, L4))

def __hex_text__ (text):
	t = b2a_hex(text)
	if not len(t)%4 == 0:
		t += "00"
	return t

#Create the date strings
def __hex_date__(T = time()):
	t = localtime(T)
	ret_data = [__hex_int2__(t[0]%100),
			  __hex_int2__(t[1]),
			  __hex_int2__(t[2]),
			  __hex_int2__(t[3]),
			  __hex_int2__(t[4]),
			  __hex_int2__(t[5])
			  ]
	return "".join(ret_data)

#Generate comment in the Key file
def __list_comment__(text):
	#not available n GDSII. Supported for compatibility with Key
	return ""

def __list_transformation__(transform):
	strans = 0
	flip = transform.flip
	rotation = transform.rotation

	if flip:
		strans += 32768
	if transform.absolute_rotation:
		strans += 2
	if transform.absolute_magnification:
		strans += 4

	if isinstance(transform.magnification, tuple):
		T = transform.magnification[0]
	else:
		T = transform.magnification

	return [__str_record__(RecordTypeSTrans, __hex_int2__(strans)),
				__str_record__(RecordTypeMag, __hex_float__(T)),
				__str_record__(RecordTypeAngle, __hex_float__(rotation))
				]
