class AwlStructInstance(object):
	"Data structure instance"

	def __init__(self, struct):
		# Store a reference to the data structure
		self.struct = struct
		# Allocate self.dataBytes
		from awlsim.core.datatypes import ByteArray

		
		self.dataBytes = ByteArray(self.struct.getSize())
		# Initialize the data structure
		for field in self.struct.fields:
			if not field.initBytes:
				continue
			try:
				self.dataBytes.store(field.offset, field.bitSize,
						     field.initBytes)
			except AwlSimError as e:
				raise AwlSimError("Data structure field '%s' "
					"initialization is out of range." %\
					str(field))

	def getFieldData(self, field, baseOffset=None):
		if baseOffset is None:
			return self.dataBytes.fetch(field.offset, field.bitSize)
		return self.dataBytes.fetch(baseOffset + field.offset, field.bitSize)

	def setFieldData(self, field, value, baseOffset=None):
		if baseOffset is None:
			self.dataBytes.store(field.offset,
					     field.bitSize, value)
		else:
			self.dataBytes.store(baseOffset + field.offset,
					     field.bitSize, value)

	def getFieldDataByName(self, name):
		return self.getFieldData(self.struct.getField(name))

	def setFieldDataByName(self, name, value):
		self.setFieldData(self.struct.getField(name), value)
	def __init__(self, struct):
		# Store a reference to the data structure
		self.struct = struct
		# Allocate self.dataBytes
		from awlsim.core.datatypes import ByteArray

		
		self.dataBytes = ByteArray(self.struct.getSize())
		# Initialize the data structure
		for field in self.struct.fields:
			if not field.initBytes:
				continue
			try:
				self.dataBytes.store(field.offset, field.bitSize,
						     field.initBytes)
			except AwlSimError as e:
				raise AwlSimError("Data structure field '%s' "
					"initialization is out of range." %\
					str(field))
	def addField(self, cpu, name, dataType, initBytes=None):
		if dataType.type == dataType.TYPE_UDT_X:
			# Add an UDT.
			try:
				udt = cpu.udts[dataType.index]
			except KeyError:
				assert(0) # Should never happen
			assert(not initBytes)
			# Assign the struct to the UDT data type, if
			# not already done so.
			assert(dataType.struct is None or
			       dataType.struct is udt.struct)
			dataType.setStruct(udt.struct)
			# Merge the UDT struct with this struct.
			return self.merge(udt.struct, name, dataType)

		if dataType.width < 0:
			raise AwlSimError("Width of data structure field '%s : %s' "
				"is undefined. This probably means that its data "
				"type is unsupported." %\
				(name, str(dataType)))

		if dataType.type == dataType.TYPE_STRUCT or\
		   dataType.type == dataType.TYPE_STRING:
			# Add a STRUCT (or STRING, which is represented as struct).
			# The struct is represented by the data types struct.
			# Merge the data type struct into this struct.
			assert(dataType.struct)
			baseField = self.merge(dataType.struct, name, dataType)
			baseField.override = AwlStructField(baseField.name,
							    baseField.offset,
							    "VOID")
			baseField.initBytes = initBytes

		if dataType.type == dataType.TYPE_ARRAY:
			# Add an ARRAY.
			# First add a field with the array's name.
			# It has the data type 'ARRAY' and is informational only.
			offset = AwlOffset(self.getUnalignedSize())
			baseField = AwlStructField(name, offset, dataType,
					override = AwlStructField(name, offset,
								  "VOID"))
			self.__registerField(baseField)
			# Add fields for each ARRAY entry.
			initOffset = AwlOffset()
			childIdent = AwlDataIdent(name,
					[ d[0] for d in dataType.arrayDimensions ],
					doValidateName = False)
			childType = dataType.arrayElementType
			if not childType.allowedInArray:
				raise AwlSimError("Data type '%s' not allowed in ARRAY" %\
					str(childType))
			for i in range(dataType.arrayGetNrElements()):
				try:
					if not initBytes:
						raise ValueError
					fieldInitData = ByteArray(intDivRoundUp(childType.width, 8))
					fieldInitData.store(AwlOffset(), childType.width,
							    initBytes.fetch(initOffset,
									    childType.width))
				except (AwlSimError, ValueError) as e:
					fieldInitData = None
				self.addField(cpu, str(childIdent), childType,
					      fieldInitData)
				initOffset += AwlOffset.fromBitOffset(childType.width)
				childIdent.advanceToNextArrayElement(dataType.arrayDimensions)
				if childType.width > 8 and\
				   intDivRoundUp(childType.width, 8) % 2 != 0:
					# Align each element to 2-byte-boundary, if word or bigger.
					self.addField(cpu, None, AwlDataType.makeByName("BYTE"))
			# Add a zero-length array-end guard field,
			# to enforce alignment of following fields.
			self.addDummyField()

		if dataType.type not in {dataType.TYPE_ARRAY,
					 dataType.TYPE_STRUCT,
					 dataType.TYPE_STRING}:
			# Add a single data type.
			if dataType.width == 1 and self.fields and\
			   self.fields[-1].bitSize == 1 and\
			   self.fields[-1].offset.bitOffset < 7:
				# Consecutive bitfields are merged into one byte
				offset = AwlOffset(self.fields[-1].offset.byteOffset,
						   self.fields[-1].offset.bitOffset + 1)
			else:
				offset = AwlOffset(self.getUnalignedSize())
			baseField = AwlStructField(name, offset, dataType, initBytes)
			self.__registerField(baseField)
		return baseField