<#--
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

	  https://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
-->
<#-- Prevent freemarker from escaping stuff -->
<#outputformat "undefined">
<#-- Declare the name and type of variables passed in to the template -->
<#-- @ftlvariable name="languageName" type="java.lang.String" -->
<#-- @ftlvariable name="protocolName" type="java.lang.String" -->
<#-- @ftlvariable name="outputFlavor" type="java.lang.String" -->
<#-- @ftlvariable name="helper" type="org.apache.plc4x.language.go.GoLanguageTemplateHelper" -->
<#-- @ftlvariable name="tracer" type="org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer" -->
<#-- @ftlvariable name="type" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
${helper.fileName(protocolName, languageName, outputFlavor)?replace(".", "/")}/model/${type.name}.go
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package model

<#macro importSectionWithContentBelow>
	<#local sectionContent><#nested></#local>
import (
	<#if helper.getRequiredImports()?has_content>
	<#list helper.getRequiredImports() as import>
	${import}
	</#list>
	</#if>
)

	${sectionContent}
</#macro>

<#macro emitImport import>${helper.emitRequiredImport(import)}</#macro>
<#macro emitImportWithAlias alias import>${helper.emitRequiredImport(alias, import)}</#macro>

<@importSectionWithContentBelow><@emitImport import="github.com/apache/plc4x/plc4go/spi/utils" />
// Code generated by code-generation. DO NOT EDIT.

<#-- TODO: the code below implies that parserArguments will be null if not present... not pretty  -->
<#if type.parserArguments.isPresent()><#assign parserArguments=type.allParserArguments.orElseThrow()></#if>
<#if type.constFields?has_content>

// Constant values.
	<#list type.constFields as field>
		<#if helper.isEnumField(field)>
const ${type.name}_${field.name?upper_case} ${helper.getLanguageTypeNameForField(field)} = ${helper.toParseExpression(field, field.type, field.referenceValue, parserArguments)}
		<#else>
const ${type.name}_${field.name?upper_case} ${helper.getLanguageTypeNameForField(field)} = ${helper.toParseExpression(field, field.type, field.referenceValue, parserArguments)}
		</#if>
	</#list>
</#if>

// ${type.name} is the corresponding interface of ${type.name?cap_first}
type ${type.name} interface {
	utils.LengthAware
	utils.Serializable
<#if type.isDiscriminatedChildTypeDefinition()>
	${type.parentType.orElseThrow().name?cap_first}
</#if>
<#if type.isDiscriminatedParentTypeDefinition()>
	<#list helper.getDiscriminatorTypes() as discriminatorName, discriminatorType>
	<#-- If the discriminator name matches that of another field, suppress the methods generation -->
		<#if !type.isNonDiscriminatorField(discriminatorName)>
	// Get${discriminatorName?cap_first} returns ${discriminatorName?cap_first} (discriminator field)
	Get${discriminatorName?cap_first}() ${helper.getLanguageTypeNameForTypeReference(discriminatorType)}
		</#if>
	</#list>
</#if>
<#list type.propertyFields as field>
    <#assign typeRef=field.asTypedField().orElseThrow().type>
	// Get${field.name?cap_first} returns ${field.name?cap_first} (property field)
	Get${field.name?cap_first}() <#if helper.needsPointerAccess(field)>*</#if>${helper.getLanguageTypeNameForTypeReference(typeRef)}
</#list>
<#list type.virtualFields as field>
    <#assign typeRef=field.asTypedField().orElseThrow().type>
	// Get${field.name?cap_first} returns ${field.name?cap_first} (virtual field)
	Get${field.name?cap_first}() <#if helper.needsPointerAccess(field)>*</#if>${helper.getLanguageTypeNameForTypeReference(typeRef)}
</#list>
}

// ${type.name}Exactly can be used when we want exactly this type and not a type which fulfills ${type.name}.
// This is useful for switch cases.
type ${type.name}Exactly interface {
	${type.name}
	is${type.name}() bool
}

// _${type.name} is the data-structure of this message
type _${type.name} struct {
<#if type.parentType.isPresent()>
	*_${type.parentType.orElseThrow().name}
</#if>
<#if type.isDiscriminatedParentTypeDefinition()>
	_${type.name}ChildRequirements
</#if>
<#-- Property fields are fields that require a property in the pojo -->
<#if type.propertyFields?has_content>
    <#list type.propertyFields as field>
        ${field.name?cap_first} <#if helper.needsPointerAccess(field)>*</#if>${helper.getLanguageTypeNameForField(field)}
    </#list>
</#if>
<#if parserArguments?has_content>
    <#assign filteredParserArguments=parserArguments?filter(arg -> !type.isDiscriminatorField(arg.name) && !type.getPropertyFieldFromThisOrParentByName(arg.name).isPresent())>
	<#assign filteredParserArgumentsLocalToType=filteredParserArguments?filter(arg->type.getParentType().isEmpty()||(type.getParentType().orElseThrow().getAllParserArguments().isEmpty()||!type.getParentType().orElseThrow().isParserArgument(arg.name)))>
</#if>
<#if filteredParserArgumentsLocalToType?has_content>

	// Arguments.
    <#list filteredParserArgumentsLocalToType as parserArgument>
	${parserArgument.name?cap_first} ${helper.getLanguageTypeNameForTypeReference(parserArgument.type)}
    </#list>
</#if>
<#assign reservedFields=type.getFields()?filter(f->f.isReservedField())>
<#if reservedFields?has_content>
	// Reserved Fields
	<#list reservedFields as reservedField>
	reservedField${reservedField?index} *${helper.getLanguageTypeNameForTypeReference(reservedField.asReservedField().orElseThrow().type)}
	</#list>
</#if>
}

<#if type.isDiscriminatedParentTypeDefinition()>
<#assign discriminatedParentType = type.asComplexTypeDefinition().orElseThrow()>
type _${type.name}ChildRequirements interface {
	utils.Serializable
	GetLengthInBits() uint16
	GetLengthInBitsConditional(lastItem bool) uint16
    <#list discriminatedParentType.getDiscriminatorNames() as discriminatorName>
    <#-- If the discriminator name matches that of another field, suppress the methods generation -->
        <#if !type.isNonDiscriminatorField(discriminatorName)>
            <#assign typeRef=helper.getDiscriminatorTypes()[discriminatorName]>
	Get${discriminatorName?cap_first}() <#if typeRef.isNonSimpleTypeReference() && !typeRef.isEnumTypeReference()>I</#if>${helper.getLanguageTypeNameForTypeReference(typeRef)}
        </#if>
    </#list>
}
</#if>

<#if type.isDiscriminatedChildTypeDefinition()>
	<#assign discriminatedChildType = type.asDiscriminatedComplexTypeDefinition().orElseThrow()>

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/////////////////////// Accessors for discriminator values.
///////////////////////

	<#list discriminatedChildType.getDiscriminatorMap() as discriminatorName, discriminatorTerm>
		<#-- If the discriminator name matches that of another field, suppress the methods generation -->
		<#if !type.isNonDiscriminatorField(discriminatorName)>
			<#assign typeRef=helper.getDiscriminatorTypes()[discriminatorName]>
func (m *_${type.name})  Get${discriminatorName?cap_first}() <#if typeRef.isNonSimpleTypeReference() && !typeRef.isEnumTypeReference()>I</#if>${helper.getLanguageTypeNameForTypeReference(typeRef)} {
			<@compress single_line=true>
				return
				<#if discriminatorTerm?? && !helper.isWildcard(discriminatorTerm)>
					<#if typeRef.isNonSimpleTypeReference()>
						<#if typeRef.isEnumTypeReference()>
							<#assign enumType=helper.getLanguageTypeNameForTypeReference(typeRef)>
							<#assign enumTypeWithoutTraces=tracer.removeTraces(enumType)>
							<#assign enumTypeTraces=tracer.extractTraces(enumType)>
							<#assign enumValue=helper.toParseExpression(null, typeRef, discriminatorTerm, parserArguments)>
							<#assign enumValueWithoutTraces=tracer.removeTraces(enumValue)>
							<#assign enumValueTraces=tracer.extractTraces(enumValue)>
							${enumTypeTraces}${enumValueTraces}${enumTypeWithoutTraces}_${enumValueWithoutTraces?replace("Get","")?replace("()","")}
						<#else>
							${helper.toParseExpression(null, typeRef, discriminatorTerm, parserArguments)}
						</#if>
					<#else>
						${helper.toParseExpression(null, typeRef, discriminatorTerm, parserArguments)}
					</#if>
				<#else>
					${helper.getNullValueForTypeReference(typeRef)}
				</#if>
			</@compress>
}

		</#if>
	</#list>
///////////////////////
///////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
</#if>
<#if type.isDiscriminatedParentTypeDefinition()>

type ${type.name}Parent interface {
	SerializeParent(writeBuffer utils.WriteBuffer, child ${type.name}, serializeChildFunction func() error) error
	GetTypeName() string
}

type ${type.name}Child interface {
	utils.Serializable
<@compress single_line=true>
	InitializeParent(parent ${type.name}
	<#if type.getPropertyFields()?has_content>, </#if>
	<#list type.getPropertyFields() as field>
		${field.name}

		<#if helper.needsPointerAccess(field)>*</#if>
		${helper.getLanguageTypeNameForField(field)}
		<#sep>, </#sep>
	</#list>
	)
</@compress>

	GetParent() *${type.name}

	GetTypeName() string
	${type.name}
}
<#elseif type.parentType.isPresent()>

<@compress single_line=true>
func (m *_${type.name}) InitializeParent(parent ${type.parentType.orElseThrow().name}
	<#assign parentTypeHasPropertyFields=type.parentType.orElseThrow().asComplexTypeDefinition().orElseThrow().allPropertyFields?has_content>
	<#if parentTypeHasPropertyFields>, </#if>
    <#list type.parentPropertyFields as parentField>
        ${parentField.name}

        <#if helper.needsPointerAccess(parentField)>*</#if>
        ${helper.getLanguageTypeNameForField(parentField)}
        <#sep>, </#sep>
    </#list>
	)
	{
</@compress>
	<#list type.parentType.orElseThrow().asComplexTypeDefinition().orElseThrow().allPropertyFields as field>
	m.${field.name?cap_first} = ${field.name}
	</#list>
}

func (m *_${type.name})  GetParent() ${type.parentType.orElseThrow().name} {
	return m._${type.parentType.orElseThrow().name}
}
</#if>
<#if type.propertyFields?has_content>
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/////////////////////// Accessors for property fields.
///////////////////////

	<#list type.propertyFields as field>
    	<#assign typeRef=field.asTypedField().orElseThrow().type>
func (m *_${type.name}) Get${field.name?cap_first}() <#if helper.needsPointerAccess(field)>*</#if>${helper.getLanguageTypeNameForTypeReference(typeRef)} {
	return m.${field.name?cap_first}
}

	</#list>
///////////////////////
///////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
</#if>
<#if type.virtualFields?has_content>
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/////////////////////// Accessors for virtual fields.
///////////////////////

	<#list type.virtualFields as field>
		<#assign typeRef=field.asTypedField().orElseThrow().type>
func (m *_${type.name}) Get${field.name?cap_first}() <#if helper.needsPointerAccess(field)>*</#if>${helper.getLanguageTypeNameForTypeReference(typeRef)} {
  	  <#list type.propertyFields?filter(f->f.isOptionalField()) as field>
	${field.name} := m.${field.name?cap_first}
	_ = ${field.name}
  	  </#list>
	return <#if typeRef.isNonSimpleTypeReference()>Cast</#if>${helper.getLanguageTypeNameForTypeReference(typeRef)}(${helper.toSerializationExpression(field, field.type, field.valueExpression, parserArguments)})
}

	</#list>
///////////////////////
///////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
</#if>
<#if type.constFields?has_content>
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/////////////////////// Accessors for const fields.
///////////////////////

	<#list type.constFields as field>
		<#assign typeRef=field.asTypedField().orElseThrow().type>
func (m *_${type.name}) Get${field.name?cap_first}() ${helper.getLanguageTypeNameForTypeReference(typeRef)} {
	return ${type.name}_${field.name?upper_case}
}

	</#list>
///////////////////////
///////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
</#if>

<#if type.parentType.isPresent() && type.parentType.orElseThrow().allParserArguments.isPresent()>
    <#assign filteredParentParserArguments = type.parentType.orElseThrow().allParserArguments.orElseThrow()?filter(arg -> !type.parentType.orElseThrow().asComplexTypeDefinition().orElseThrow().isDiscriminatorField(arg.name))>
</#if>

// New${type.name} factory function for _${type.name}
<@compress single_line=true>
func New${type.name}(
	<#list type.propertyFields as field>
		${field.name} <#if helper.needsPointerAccess(field)>*</#if>${helper.getLanguageTypeNameForField(field)}
		<#sep>, </#sep>
	</#list>
	<#if type.parentType.isPresent()>
		<#if type.propertyFields?has_content && type.parentPropertyFields?has_content>, </#if>
		<#list type.parentPropertyFields as parentField>
			${parentField.name} <#if helper.needsPointerAccess(parentField)>*</#if>${helper.getLanguageTypeNameForField(parentField)}
			<#sep>, </#sep>
		</#list>
	</#if>
    <#if filteredParserArguments?has_content>
        <#if type.allPropertyFields?has_content>, </#if>
        <#list filteredParserArguments as parserArgument>
			${parserArgument.name} ${helper.getLanguageTypeNameForTypeReference(parserArgument.type)}
            <#sep>, </#sep>
        </#list>
    </#if>
	) *_${type.name} {
</@compress>

<#if type.parentType.isPresent()>
	_result := &_${type.name}{
	<#list type.propertyFields as field>
		${field.name?cap_first}: ${field.name},
	</#list>
    	_${type.parentType.orElseThrow().name}: New${type.parentType.orElseThrow().name}(<#list type.parentPropertyFields as parentField>${parentField.name}<#sep>, </#list><#if filteredParentParserArguments?has_content><#if type.parentPropertyFields?has_content>, </#if><#list filteredParentParserArguments as arg>${arg.name}<#sep>, </#sep></#list></#if>),
	}
	_result._${type.parentType.orElseThrow().name}._${type.parentType.orElseThrow().name}ChildRequirements = _result
	return _result
<#else>
	<@compress single_line=true>
	return &_${type.name}{
		<#list type.propertyFields as field>
			${field.name?cap_first}: ${field.name}
			<#sep>, </#sep>
		</#list>
        <#if filteredParserArguments?has_content>
            <#if type.propertyFields?has_content>, </#if>
            <#list filteredParserArguments as parserArgument>
                ${parserArgument.name?cap_first}: ${parserArgument.name}
                <#sep>, </#sep>
            </#list>
        </#if>
	}
    </@compress>

</#if>
}

// Deprecated: use the interface for direct cast
func Cast${type.name}(structType interface{}) ${type.name} {
    if casted, ok := structType.(${type.name}); ok {
		return casted
	}
	if casted, ok := structType.(*${type.name}); ok {
		return *casted
	}
	return nil
}

func (m *_${type.name}) GetTypeName() string {
	return "${type.name}"
}

<#if !type.isDiscriminatedParentTypeDefinition()>
func (m *_${type.name}) GetLengthInBits() uint16 {
	return m.GetLengthInBitsConditional(false)
}
</#if>

<#if !type.isDiscriminatedParentTypeDefinition()>
<#-- TODO: use serializer args instead of a fixed bool for one case -->
func (m *_${type.name}) GetLengthInBitsConditional(lastItem bool) uint16 {
		<#if type.isDiscriminatedChildTypeDefinition()>
	lengthInBits := uint16(m.GetParentLengthInBits())
		<#else>
	lengthInBits := uint16(0)
		</#if>
		<#list type.fields as field>
			<#switch field.typeName>
				<#case "array">
					<#assign arrayField = field.asArrayField().orElseThrow()>
                    <#assign arrayElementType = arrayField.type.elementTypeReference>

	// Array field
	if len(m.${arrayField.name?cap_first}) > 0 {
				<#if arrayElementType.isVstringTypeReference()>
                    <#assign vstringTypeReference = arrayElementType.asVstringTypeReference().orElseThrow()>
		lengthInBits += uint16(${helper.toSerializationExpression(arrayField, helper.intTypeReference, vstringTypeReference.lengthExpression, parserArguments)}) * uint16(len(m.${arrayField.name?cap_first}))
				<#elseif arrayElementType.isSimpleTypeReference()>
                    <#assign simpleTypeReference = arrayElementType.asSimpleTypeReference().orElseThrow()>
		lengthInBits += ${simpleTypeReference.sizeInBits} * uint16(len(m.${arrayField.name?cap_first}))
				<#else>
				<#if arrayField.isCountArrayField()>
		for i, element := range m.${arrayField.name?cap_first} {
			last := i == len(m.${arrayField.name?cap_first}) -1
			lengthInBits += element.(interface{GetLengthInBitsConditional(bool) uint16}).GetLengthInBitsConditional(last)
				<#else>
		for _, element := range m.${arrayField.name?cap_first} {
			lengthInBits += element.GetLengthInBits()
				</#if>
		}
				</#if>
	}
					<#break>
				<#case "checksum">
					<#assign checksumField = field.asChecksumField().orElseThrow()>
					<#assign simpleTypeReference = checksumField.type.asSimpleTypeReference().orElseThrow()>

	// Checksum Field (checksum)
	lengthInBits += ${simpleTypeReference.sizeInBits}
					<#break>
				<#case "const">
					<#assign constField = field.asConstField().orElseThrow()>

	// Const Field (${constField.name})
				<#if constField.type.isVstringTypeReference()>
					<#assign vstringTypeReference = constField.type.asVstringTypeReference().orElseThrow()>
	lengthInBits += uint16(${helper.toSerializationExpression(constField, helper.intTypeReference, vstringTypeReference.lengthExpression, parserArguments)})
				<#else>
                    <#assign simpleTypeReference = constField.type.asSimpleTypeReference().orElseThrow()>
	lengthInBits += ${simpleTypeReference.sizeInBits}
				</#if>
					<#break>
				<#case "discriminator">
					<#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
	// Discriminator Field (${discriminatorField.name})
		<#if discriminatorField.type.isSimpleTypeReference()>
            <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>
			<#if discriminatorField.type.isVstringTypeReference()>
                <#assign vstringTypeReference = discriminatorField.type.asVstringTypeReference().orElseThrow()>
			lengthInBits += uint16(${helper.toSerializationExpression(discriminatorField, helper.intTypeReference, vstringTypeReference.lengthExpression, parserArguments)})
			<#else>
			lengthInBits += ${simpleTypeReference.sizeInBits};
			</#if>
		<#elseif helper.isEnumField(discriminatorField)>
		lengthInBits += ${helper.getEnumBaseTypeReference(discriminatorField.type).sizeInBits};
		<#else>
		lengthInBits += ${discriminatorField.name}.getLengthInBits();
		</#if>
					<#break>
				<#case "enum">
					<#assign enumField = field.asEnumField().orElseThrow()>

	// Enum Field (${enumField.name})
	lengthInBits += ${helper.getEnumBaseTypeReference(enumField.type).sizeInBits}
					<#break>
				<#case "implicit">
					<#assign implicitField = field.asImplicitField().orElseThrow()>
					<#assign simpleTypeReference = implicitField.type.asSimpleTypeReference().orElseThrow()>

	// Implicit Field (${implicitField.name})
	lengthInBits += ${simpleTypeReference.sizeInBits}
					<#break>
				<#case "manualArray">
					<#assign manualArrayField = field.asManualArrayField().orElseThrow()>
	                <#assign arrayElementType = manualArrayField.type.elementTypeReference>

	// Manual Array Field (${manualArrayField.name})
	lengthInBits += uint16(${helper.toSerializationExpression(manualArrayField, helper.intTypeReference, manualArrayField.lengthExpression, parserArguments)})
					<#break>
				<#case "manual">
					<#assign manualField = field.asManualField().orElseThrow()>

	// Manual Field (${manualField.name})
	lengthInBits += uint16(${helper.toSerializationExpression(manualField, helper.intTypeReference, manualField.lengthExpression, parserArguments)})
					<#break>
				<#case "optional">
					<#assign optionalField = field.asOptionalField().orElseThrow()>

	// Optional Field (${optionalField.name})
	if m.${optionalField.name?cap_first} != nil {
					<#if optionalField.type.isSimpleTypeReference()>
						<#assign simpleTypeReference = optionalField.type.asSimpleTypeReference().orElseThrow()>
		lengthInBits += ${simpleTypeReference.sizeInBits}
					<#elseif helper.isEnumField(optionalField)>
		lengthInBits += ${helper.getEnumBaseTypeReference(optionalField.type).sizeInBits}
                    <#elseif optionalField.type.isComplexTypeReference()>
		lengthInBits += m.${optionalField.name?cap_first}.GetLengthInBits()
					<#else>
		lengthInBits += (*m.${optionalField.name?cap_first}).GetLengthInBits()
					</#if>
	}
					<#break>
				<#case "padding">
					<#assign paddingField = field.asPaddingField().orElseThrow()>
					<#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>

	// Padding Field (padding)
	_timesPadding := uint8(${helper.toSerializationExpression(paddingField, helper.intTypeReference, paddingField.paddingCondition, parserArguments)})
	for ;_timesPadding > 0; _timesPadding-- {
		lengthInBits += ${simpleTypeReference.sizeInBits}
	}
					<#break>
				<#case "reserved">
					<#assign reservedField = field.asReservedField().orElseThrow()>
					<#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>

	// Reserved Field (reserved)
	lengthInBits += ${simpleTypeReference.sizeInBits}
					<#break>
				<#case "simple">
					<#assign simpleField = field.asSimpleField().orElseThrow()>

	// Simple field (${simpleField.name})
					<#if simpleField.type.isSimpleTypeReference()>
						<#if simpleField.type.isVstringTypeReference()>
							<#assign vstringTypeReference = simpleField.type.asVstringTypeReference().orElseThrow()>
	lengthInBits += uint16(${helper.toSerializationExpression(simpleField, helper.intTypeReference, vstringTypeReference.lengthExpression, parserArguments)})
						<#else>
							<#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>
	lengthInBits += ${simpleTypeReference.sizeInBits};
						</#if>
					<#elseif helper.isEnumField(field)>
	lengthInBits += ${helper.getEnumBaseTypeReference(simpleField.type).sizeInBits}
					<#else>
	lengthInBits += m.${simpleField.name?cap_first}.GetLengthInBits()
					</#if>
					<#break>
				<#case "switch">
					<#assign switchField = field.asSwitchField().orElseThrow()>

	// Length of sub-type elements will be added by sub-type...
	lengthInBits += m.GetLengthInBits()
					<#break>
				<#case "unknown">
					<#assign unknownField = field.asUnknownField().orElseThrow()>
					<#assign simpleTypeReference = unknownField.type.asSimpleTypeReference().orElseThrow()>

	// Unknown field
	lengthInBits += ${simpleTypeReference.sizeInBits}

					<#break>
				<#case "virtual">
					<#assign virtualField = field.asVirtualField().orElseThrow()>

	// A virtual field doesn't have any in- or output.
					<#break>
			</#switch>
		</#list>

	return lengthInBits
}
</#if>

<#if type.isDiscriminatedParentTypeDefinition()>
func (m *_${type.name}) GetParentLengthInBits() uint16 {
	lengthInBits := uint16(0)
	<#list type.fields as field>
		<#switch field.typeName>
			<#case "array">
				<#assign arrayField = field.asArrayField().orElseThrow()>
                <#assign arrayElementType = arrayField.type.elementTypeReference>

	// Array field
	if len(m.${arrayField.name?cap_first}) > 0 {
				<#if arrayElementType.isVstringTypeReference()>
					<#assign vstringTypeReference=arrayElementType.asVstringTypeReference().orElseThrow()>
		lengthInBits += uint16(${helper.toSerializationExpression(arrayField, helper.intTypeReference, vstringTypeReference.lengthExpression, parserArguments)}) * uint16(len(m.${arrayField.name?cap_first}))
				<#elseif arrayElementType.isSimpleTypeReference()>
                    <#assign simpleTypeReference = arrayElementType.asSimpleTypeReference().orElseThrow()>
		lengthInBits += ${simpleTypeReference.sizeInBits} * uint16(len(m.${arrayField.name?cap_first}))
				<#else>
					<#if arrayField.isCountArrayField()>
		for i, element := range m.${arrayField.name?cap_first} {
			last := i >= len(m.${arrayField.name?cap_first}) -1
			lengthInBits += element.(interface{GetLengthInBitsConditional(bool) uint16}).GetLengthInBitsConditional(last)
					<#else>
		for _, element := range m.${arrayField.name?cap_first} {
			lengthInBits += element.GetLengthInBits()
					</#if>
		}
				</#if>
				}
				<#break>
			<#case "checksum">
				<#assign checksumField = field.asChecksumField().orElseThrow()>
				<#assign simpleTypeReference = checksumField.type.asSimpleTypeReference().orElseThrow()>

	// Checksum Field (checksum)
	lengthInBits += ${simpleTypeReference.sizeInBits}
				<#break>
			<#case "const">
				<#assign constField = field.asConstField().orElseThrow()>
				<#assign simpleTypeReference = constField.type>

	// Const Field (${constField.name})
				<#if constField.type.isVstringTypeReference()>
                    <#assign vstringTypeReference = constField.type.asVstringTypeReference().orElseThrow()>
					lengthInBits += uint16(${helper.toSerializationExpression(constField, helper.intTypeReference, vstringTypeReference.lengthExpression, parserArguments)})
				<#elseif constField.type.isEnumTypeReference()>
	lengthInBits += ${helper.getEnumBaseTypeReference(constField.type).sizeInBits};
				<#else>
	lengthInBits += ${simpleTypeReference.asSimpleTypeReference().orElseThrow().sizeInBits}
				</#if>
				<#break>
			<#case "discriminator">
				<#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
				<#assign simpleTypeReference = discriminatorField.type>
	// Discriminator Field (${discriminatorField.name})
				<#if simpleTypeReference.isSimpleTypeReference()>
					<#if discriminatorField.type.isVstringTypeReference()>
                        <#assign vstringTypeReference = discriminatorField.type.asVstringTypeReference().orElseThrow()>
	lengthInBits += uint16(${helper.toSerializationExpression(discriminatorField, helper.intTypeReference, vstringTypeReference.lengthExpression, parserArguments)})
					<#else>
	lengthInBits += ${simpleTypeReference.asSimpleTypeReference().orElseThrow().sizeInBits};
					</#if>
				<#elseif helper.isEnumField(discriminatorField)>
	lengthInBits += ${helper.getEnumBaseTypeReference(discriminatorField.type).sizeInBits};
				<#else>
	lengthInBits += ${discriminatorField.name}.GetLengthInBits();
				</#if>
				<#break>
			<#case "enum">
				<#assign enumField = field.asEnumField().orElseThrow()>

	// Enum Field (${enumField.name})
	lengthInBits += ${helper.getEnumBaseTypeReference(enumField.type).sizeInBits}
				<#break>
			<#case "implicit">
				<#assign implicitField = field.asImplicitField().orElseThrow()>
				<#assign simpleTypeReference = implicitField.type>

	// Implicit Field (${implicitField.name})
	lengthInBits += ${simpleTypeReference.asSimpleTypeReference().orElseThrow().sizeInBits}
				<#break>
			<#case "manualArray">
				<#assign manualArrayField = field.asManualArrayField().orElseThrow()>
	            <#assign arrayElementType = manualArrayField.type.elementTypeReference>

	// Manual Array Field (${manualArrayField.name})
	lengthInBits += uint16(${helper.toParseExpression(manualArrayField, arrayElementType, manualArrayField.lengthExpression, parserArguments)})
				<#break>
			<#case "manual">
				<#assign manualField = field.asManualField().orElseThrow()>

	// Manual Field (${manualField.name})
	lengthInBits += uint16(${helper.toParseExpression(manualField, manualField.type, manualField.lengthExpression, parserArguments)})
				<#break>
			<#case "optional">
				<#assign optionalField = field.asOptionalField().orElseThrow()>

	// Optional Field (${optionalField.name})
	if m.${optionalField.name?cap_first} != nil {
				<#if optionalField.type.isSimpleTypeReference()>
					<#assign simpleTypeReference = optionalField.type.asSimpleTypeReference().orElseThrow()>
		lengthInBits += ${simpleTypeReference.sizeInBits}
				<#elseif helper.isEnumField(field)>
		lengthInBits += ${helper.getEnumBaseTypeReference(optionalField.type).sizeInBits}
                <#elseif optionalField.type.isComplexTypeReference()>
		lengthInBits += m.${optionalField.name?cap_first}.GetLengthInBits()
                <#else>
		lengthInBits += (*m.${optionalField.name?cap_first}).GetLengthInBits()
				</#if>
	}
				<#break>
			<#case "padding">
				<#assign paddingField = field.asPaddingField().orElseThrow()>
				<#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>

	// Padding Field (padding)
	_timesPadding := uint8(${helper.toSerializationExpression(paddingField, helper.intTypeReference, paddingField.paddingCondition, parserArguments)})
	for ;_timesPadding > 0; _timesPadding-- {
		lengthInBits += ${simpleTypeReference.sizeInBits}
	}
				<#break>
			<#case "reserved">
				<#assign reservedField = field.asReservedField().orElseThrow()>
				<#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>

	// Reserved Field (reserved)
	lengthInBits += ${simpleTypeReference.sizeInBits}
				<#break>
			<#case "simple">
				<#assign simpleField = field.asSimpleField().orElseThrow()>

	// Simple field (${simpleField.name})
				<#if simpleField.type.isSimpleTypeReference()>
					<#if simpleField.type.isVstringTypeReference()>
						<#assign vstringTypeReference = simpleField.type.asVstringTypeReference().orElseThrow()>
	lengthInBits += uint16(${helper.toSerializationExpression(simpleField, helper.intTypeReference, vstringTypeReference.lengthExpression, parserArguments)})
					<#else>
						<#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>
	lengthInBits += ${simpleTypeReference.sizeInBits};
					</#if>
				<#elseif helper.isEnumField(field)>
	lengthInBits += ${helper.getEnumBaseTypeReference(simpleField.type).sizeInBits}
				<#else>
	lengthInBits += m.${simpleField.name?cap_first}.GetLengthInBits()
				</#if>
				<#break>
			<#case "unknown">
				<#assign unknownField = field.asUnknownField().orElseThrow()>
			<#assign simpleTypeReference = unknownField.type.asSimpleTypeReference().orElseThrow()>

	// Unknown field
	lengthInBits += ${simpleTypeReference.sizeInBits}

				<#break>
			<#case "virtual">
				<#assign virtualField = field.asVirtualField().orElseThrow()>

	// A virtual field doesn't have any in- or output.
				<#break>
		</#switch>
	</#list>

	return lengthInBits
}
</#if>

func (m *_${type.name}) GetLengthInBytes() uint16 {
	return m.GetLengthInBits() / 8
}

<#assign hasParserArguments=parserArguments?has_content/>
<#assign parserArgumentList><#if hasParserArguments><#list parserArguments as parserArgument>${parserArgument.name} ${helper.getLanguageTypeNameForTypeReference(parserArgument.type)}<#sep>, </#sep></#list></#if></#assign>
func ${type.name}Parse(readBuffer utils.ReadBuffer<#if hasParserArguments>, ${parserArgumentList}</#if>) (${type.name}, error) {
	positionAware := readBuffer
	_ = positionAware
	if pullErr := readBuffer.PullContext("${type.name}"); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
	currentPos := positionAware.GetPos()
	_ = currentPos
	<#if helper.requiresStartPos() || helper.requiresCurPos()>
	var startPos = positionAware.GetPos()
	_ = startPos
	</#if>
	<#if helper.requiresCurPos()>
	var curPos uint16
	</#if>
	<#assign reservedFieldIndex=0>
	<#list type.fields as field>
		<#switch field.typeName>
			<#case "array">
				<#assign arrayField = field.asArrayField().orElseThrow()>
                <#assign arrayElementType = arrayField.type.elementTypeReference>
				<#if arrayElementType.isByteBased()>
	// Byte Array field (${arrayField.name})
	<#assign numberOfBytesExpression>
		<#compress>
		<#if field.isCountArrayField()>
			numberOfBytes${arrayField.name} := int(${helper.toIntegerParseExpression(field, 16, arrayField.loopExpression, parserArguments)})
		<#elseif field.isLengthArrayField()>
			numberOfBytes${arrayField.name} := int(${helper.toIntegerParseExpression(field, 16, arrayField.loopExpression, parserArguments)})
		<#elseif field.isTerminatedArrayField()>
			<#-- TODO: we need to find out to implement this-->
			numberOfBytes${arrayField.name} := int(${helper.toIntegerParseExpression(field, 16, arrayField.loopExpression, parserArguments)})
		<#else>
			<#-- TODO: we should throw a exception here-->
			numberOfBytes${arrayField.name} := int(-1)
		</#if>
		</#compress>
	</#assign>
	${numberOfBytesExpression}
	${arrayField.name}, _readArrayErr := readBuffer.ReadByteArray("${arrayField.name}", numberOfBytes${arrayField.name})
	if _readArrayErr != nil {
		return nil, errors.Wrap(_readArrayErr, "Error parsing '${arrayField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
				<#else>

	// Array field (${arrayField.name})
	if pullErr := readBuffer.PullContext("${arrayField.name}", utils.WithRenderAsList(true)); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for ${arrayField.name}")<@emitImport import="github.com/pkg/errors" />
	}
			<#-- Only update curPos if the length expression uses it -->
					<#if arrayField.loopExpression.contains("curPos")>
	curPos = positionAware.GetPos() - startPos
					</#if>
			<#-- If this is a count array, we can directly initialize an array with the given size -->
					<#if field.isCountArrayField()>
	// Count array
	${arrayField.name} := make(${helper.getLanguageTypeNameForField(field)}, ${helper.toIntegerParseExpression(arrayField, 16, arrayField.loopExpression, parserArguments)})
	// This happens when the size is set conditional to 0
	if len(${arrayField.name}) == 0 {
		${arrayField.name} = nil
	}
	{
		for curItem := uint16(0); curItem < uint16(${helper.toIntegerParseExpression(arrayField, 16, arrayField.loopExpression, parserArguments)}); curItem++ {
						<#if (!arrayElementType.isSimpleTypeReference()) && helper.requiresVariable(arrayField, "lastItem")>
			lastItem := curItem == uint16(${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)} - 1)
						</#if>
                        <@compress single_line=true>
			_item, _err :=
                            <#if arrayElementType.isSimpleTypeReference()>
                                ${helper.getReadBufferReadMethodCall("", arrayElementType.asSimpleTypeReference().orElseThrow(), arrayField)}
                            <#else>
                                <#assign complexTypeReference=arrayElementType.asComplexTypeReference().orElseThrow()>
                                <#assign typeName=complexTypeReference.name>
                                <#assign typeDefinition=complexTypeReference.typeDefinition>
                                <#if typeDefinition.isDiscriminatedChildTypeDefinition()>
                                <#-- Usually you don't use child directly unless they are parameterized #-->
                                    <#assign typeName=typeDefinition.parentType.orElseThrow().name>
                                    <#assign typeDefinition=typeDefinition.parentType.orElseThrow()>
                                </#if>
                                <#assign hasRefParams=complexTypeReference.params.isPresent()>
                                <#assign refParams>
                                    <#if hasRefParams>
										,
                                        <#list complexTypeReference.params.orElseThrow() as parserArgument>
                                            <#if <#--TODO: here DF1ResponseMessage throws a payloadLength not present: meaning that some feature is not rightfully implemented as the result should be a error not a missing pointer-->type.getPropertyFieldFromThisOrParentByName(parserArgument.name).isPresent() && helper.needsPointerAccess(type.getPropertyFieldFromThisOrParentByName(parserArgument.name).orElseThrow())>*</#if>
                                            ${helper.toTypedParseExpression(arrayField, helper.getArgumentType(arrayElementType, parserArgument?index), parserArgument, parserArguments)}
                                            <#sep>, </#sep>
                                        </#list>
                                    </#if>
                                </#assign>
                                ${typeName}Parse(readBuffer${refParams})
                            </#if>
                        </@compress>

			if _err != nil {
				return nil, errors.Wrap(_err, "Error parsing '${arrayField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
			}
                        <#if arrayElementType.isSimpleTypeReference()>
			${arrayField.name}[curItem] = _item
                        <#else>
			${arrayField.name}[curItem] = _item.(${arrayElementType.asComplexTypeReference().orElseThrow().name})
                        </#if>
		}
	}
				<#-- In all other cases do we have to work with a list, that is later converted to an array -->
					<#elseif field.isLengthArrayField()>
				<#-- For a length array, we read data till the read position of the buffer reaches a given position -->
	// Length array
	var ${arrayField.name} ${helper.getLanguageTypeNameForField(field)}
	{
		_${arrayField.name}Length := ${helper.toIntegerParseExpression(arrayField, 16, arrayField.loopExpression, parserArguments)}
		_${arrayField.name}EndPos := positionAware.GetPos() + uint16(_${arrayField.name}Length)
		for ;positionAware.GetPos() < _${arrayField.name}EndPos; {
                        <@compress single_line=true>
							_item, _err := <#if arrayElementType.isSimpleTypeReference()>
                            ${helper.getReadBufferReadMethodCall("", arrayElementType.asSimpleTypeReference().orElseThrow(), arrayField)}
                        <#else>
                            <#assign complexTypeReference=arrayElementType.asComplexTypeReference().orElseThrow()>
                            <#assign typeName=complexTypeReference.name>
                            <#assign typeDefinition=complexTypeReference.typeDefinition>
                            <#if typeDefinition.isComplexTypeDefinition() && typeDefinition.asComplexTypeDefinition().orElseThrow().isDiscriminatedChildTypeDefinition()>
                            <#-- Usually you don't use child directly unless they are parameterized #-->
                                <#assign typeName=typeDefinition.asComplexTypeDefinition().orElseThrow().parentType.orElseThrow().name>
                                <#assign typeDefinition=typeDefinition.asComplexTypeDefinition().orElseThrow().parentType.orElseThrow()>
                            </#if>
                            <#assign hasRefParams=complexTypeReference.params.isPresent()>
                            <#assign refParams>
                                <#if hasRefParams>
									,
                                    <#list complexTypeReference.params.orElseThrow() as parserArgument>
                                        ${helper.toTypedParseExpression(arrayField, helper.getArgumentType(arrayElementType, parserArgument?index), parserArgument, parserArguments)}
                                        <#sep>, </#sep>
                                    </#list>
                                </#if>
                            </#assign>
                            ${typeName}Parse(readBuffer${refParams})
                        </#if>
                        </@compress>

			if _err != nil {
				return nil, errors.Wrap(_err, "Error parsing '${arrayField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
			}
                        <#if arrayElementType.isSimpleTypeReference()>
			${arrayField.name} = append(${arrayField.name}, _item)
                        <#else>
			${arrayField.name} = append(${arrayField.name}, _item.(${arrayElementType.asComplexTypeReference().orElseThrow().name}))
                        </#if>
						<#-- After parsing, update the current position, but only if it's needed -->
							<#if arrayField.loopExpression.contains("curPos")>
			curPos = positionAware.GetPos() - startPos
							</#if>
		}
	}
					<#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
					<#elseif field.isTerminatedArrayField()>
	// Terminated array
	var ${arrayField.name} ${helper.getLanguageTypeNameForField(field)}
	{
		for ;!bool(${helper.toParseExpression(arrayField, helper.boolTypeReference, arrayField.loopExpression, parserArguments)}); {
						<@compress single_line=true>
			_item, _err := <#if arrayElementType.isSimpleTypeReference()>
								${helper.getReadBufferReadMethodCall("", arrayElementType.asSimpleTypeReference().orElseThrow(), arrayField)}
							<#else>
                                <#assign complexTypeReference=arrayElementType.asComplexTypeReference().orElseThrow()>
                                <#assign typeName=complexTypeReference.name>
                                <#assign typeDefinition=complexTypeReference.typeDefinition>
                                <#if typeDefinition.isComplexTypeDefinition() && typeDefinition.asComplexTypeDefinition().orElseThrow().isDiscriminatedChildTypeDefinition()>
                                <#-- Usually you don't use child directly unless they are parameterized #-->
                                    <#assign typeName=typeDefinition.asComplexTypeDefinition().orElseThrow().parentType.orElseThrow().name>
                                    <#assign typeDefinition=typeDefinition.asComplexTypeDefinition().orElseThrow().parentType.orElseThrow()>
                                </#if>
                                <#assign hasRefParams=complexTypeReference.params.isPresent()>
                                <#assign refParams>
                                    <#if hasRefParams>
										,
                                        <#list complexTypeReference.params.orElseThrow() as parserArgument>
                                            ${helper.toTypedParseExpression(arrayField, helper.getArgumentType(arrayElementType, parserArgument?index), parserArgument, parserArguments)}
                                            <#sep>, </#sep>
                                        </#list>
                                    </#if>
                                </#assign>
                                ${typeName}Parse(readBuffer${refParams})
							</#if>
						</@compress>

			if _err != nil {
				return nil, errors.Wrap(_err, "Error parsing '${arrayField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
			}
						<#if arrayElementType.isSimpleTypeReference()>
			${arrayField.name} = append(${arrayField.name}, _item)
						<#else>
			${arrayField.name} = append(${arrayField.name}, _item.(${arrayElementType.asComplexTypeReference().orElseThrow().name}))
						</#if>

						<#-- After parsing, update the current position, but only if it's needed -->
							<#if arrayField.loopExpression.contains("curPos")>
			curPos = positionAware.GetPos() - startPos
							</#if>
		}
	}
					</#if>
	if closeErr := readBuffer.CloseContext("${arrayField.name}", utils.WithRenderAsList(true)); closeErr != nil {
		return nil, errors.Wrap(closeErr, "Error closing for ${arrayField.name}")<@emitImport import="github.com/pkg/errors" />
	}
				</#if>
				<#break>
			<#case "checksum">
				<#assign checksumField = field.asChecksumField().orElseThrow()>
				<#assign simpleTypeReference = checksumField.type.asSimpleTypeReference().orElseThrow()>

	// Checksum Field (checksum)
	{
		checksumRef, _checksumRefErr := ${helper.getReadBufferReadMethodCall("checksum", simpleTypeReference, checksumField)}
		if _checksumRefErr != nil {
			return nil, errors.Wrap(_checksumRefErr, "Error parsing 'checksum' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
		}
		checksum, _checksumErr:= ${helper.toParseExpression(checksumField, checksumField.type, checksumField.checksumExpression, parserArguments)}
		if _checksumErr != nil {
			return nil, errors.Wrap(_checksumErr, "Checksum verification failed")
		}
		if checksum != checksumRef {
			return nil, errors.Errorf("Checksum verification failed. Expected %x but got %x", checksumRef, checksum)<@emitImport import="github.com/pkg/errors" />
		}
	}
				<#break>
			<#case "const">
				<#assign constField = field.asConstField().orElseThrow()>

	// Const Field (${constField.name})
				<#if constField.type.isEnumTypeReference()>
                    <#--<#assign constField=constField.asEnumField().orElseThrow()>-->
					<#-- TODO: finish me-->
                    <#if false && constField.fieldName.isPresent()>
	${constField.name}${constField.fieldName.orElseThrow()?cap_first}, _${constField.name}${constField.fieldName.orElseThrow()?cap_first}Err := ${helper.getReadBufferReadMethodCall(constField.name, helper.getEnumFieldSimpleTypeReference(constField.type, constField.fieldName.orElseThrow()), constField)}
	if _${constField.name}${constField.fieldName.orElseThrow()?cap_first}Err != nil {
		return nil, errors.Wrap(_${constField.name}${constField.fieldName.orElseThrow()?cap_first}Err, "Error serializing '${constField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
	${constField.name}, _${constField.name}Err := ${helper.getLanguageTypeNameForField(field)}FirstEnumForField${constField.fieldName.orElseThrow()?cap_first}(${constField.name}${constField.fieldName.orElseThrow()?cap_first})
	if _${constField.name}Err != nil {
		return nil, errors.Wrap(_${constField.name}Err, "Error serializing '${constField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
                    <#else>
	${constField.name}, _${constField.name}Err := ${helper.getLanguageTypeNameForField(field)}Parse(readBuffer)
	if _${constField.name}Err != nil {
		return nil, errors.Wrap(_${constField.name}Err, "Error parsing '${constField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
                    </#if> 
				<#else>
	${constField.name}, _${constField.name}Err := ${helper.getReadBufferReadMethodCall(constField.name, constField.type.asSimpleTypeReference().orElseThrow(), constField)}
				</#if>
	if _${constField.name}Err != nil {
		return nil, errors.Wrap(_${constField.name}Err, "Error parsing '${constField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
	if ${constField.name} != ${type.name}_${constField.name?upper_case} {
		return nil, errors.New("Expected constant value " + fmt.Sprintf("%<#if constField.type.isBooleanTypeReference()>t<#else>d</#if>", ${type.name}_${constField.name?upper_case}) + " but got " + fmt.Sprintf("%<#if constField.type.isBooleanTypeReference()>t<#else>d</#if>", ${constField.name}))<@emitImport import="github.com/pkg/errors" /><@emitImport import="fmt" />
	}
				<#break>
			<#case "discriminator">
				<#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>

	// Discriminator Field (${discriminatorField.name}) (Used as input to a switch field)
	<#if helper.isEnumField(field)>
        <#assign namedField = field.asNamedField().orElseThrow()>
	if pullErr := readBuffer.PullContext("${namedField.name}"); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for ${namedField.name}")<@emitImport import="github.com/pkg/errors" />
	}
	${helper.getVariableName(field)}_temp, _${discriminatorField.name}Err := ${helper.getLanguageTypeNameForField(discriminatorField)}Parse(readBuffer)
	var ${helper.getVariableName(field)} ${helper.getLanguageTypeNameForField(discriminatorField)} = ${helper.getVariableName(field)}_temp
	if closeErr := readBuffer.CloseContext("${namedField.name}"); closeErr != nil {
		return nil, errors.Wrap(closeErr, "Error closing for ${namedField.name}")<@emitImport import="github.com/pkg/errors" />
	}
	<#else>
        <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>
	${helper.getVariableName(field)}, _${discriminatorField.name}Err := ${helper.getReadBufferReadMethodCall(discriminatorField.name, simpleTypeReference, discriminatorField)}
	</#if>
	if _${discriminatorField.name}Err != nil {
		return nil, errors.Wrap(_${discriminatorField.name}Err, "Error parsing '${discriminatorField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
				<#break>
			<#case "enum">
				<#assign enumField = field.asEnumField().orElseThrow()>

	if pullErr := readBuffer.PullContext("${enumField.name}"); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for ${enumField.name}")<@emitImport import="github.com/pkg/errors" />
	}
	// Enum field (${enumField.name})
	${enumField.name}${enumField.fieldName?cap_first}, _${enumField.name}${enumField.fieldName?cap_first}Err := ${helper.getReadBufferReadMethodCall(enumField.type.name, helper.getEnumFieldSimpleTypeReference(enumField.type, enumField.fieldName), enumField)}
	if _${enumField.name}${enumField.fieldName?cap_first}Err != nil {
		return nil, errors.Wrap(_${enumField.name}${enumField.fieldName?cap_first}Err, "Error serializing '${enumField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
	${enumField.name}, _${enumField.name}Err := ${helper.getLanguageTypeNameForField(field)}FirstEnumForField${enumField.fieldName?cap_first}(${enumField.name}${enumField.fieldName?cap_first})
	if _${enumField.name}Err != nil {
		return nil, errors.Wrap(_${enumField.name}Err, "Error serializing '${enumField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
	if closeErr := readBuffer.CloseContext("${enumField.name}"); closeErr != nil {
		return nil, errors.Wrap(closeErr, "Error closing for ${enumField.name}")<@emitImport import="github.com/pkg/errors" />
	}
				<#break>
			<#case "implicit">
				<#assign implicitField = field.asImplicitField().orElseThrow()>
				<#assign simpleTypeReference = implicitField.type.asSimpleTypeReference().orElseThrow()>

	// Implicit Field (${implicitField.name}) (Used for parsing, but its value is not stored as it's implicitly given by the objects content)
	${implicitField.name}, _${implicitField.name}Err := ${helper.getReadBufferReadMethodCall(implicitField.name, simpleTypeReference, implicitField)}
	_ = ${implicitField.name}
	if _${implicitField.name}Err != nil {
		return nil, errors.Wrap(_${implicitField.name}Err, "Error parsing '${implicitField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
				<#break>
			<#case "manualArray">
				<#assign manualArrayField = field.asManualArrayField().orElseThrow()>
            	<#assign arrayElementType = manualArrayField.type.elementTypeReference>
	if pullErr := readBuffer.PullContext("${manualArrayField.name}", utils.WithRenderAsList(true)); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for ${manualArrayField.name}")<@emitImport import="github.com/pkg/errors" />
	}
	// Manual Array Field (${manualArrayField.name})
			<#-- Only update curPos if the length expression uses it -->
				<#if manualArrayField.loopExpression.contains("curPos")>
	curPos = positionAware.GetPos() - startPos
				</#if>
			<#-- If this is a count array, we can directly initialize an array with the given size -->
				<#if field.isCountArrayField()>
	// Count array
	_${manualArrayField.name}Count := ${helper.toParseExpression(manualArrayField, helper.intTypeReference, manualArrayField.loopExpression, parserArguments)}
	${manualArrayField.name} := make(${helper.getLanguageTypeNameForField(field)}, _${manualArrayField.name}Count)
	// This happens when the size is set conditional to 0
	if len(${manualArrayField.name}) == 0 {
		${manualArrayField.name} = nil
	}
	for i := 0; i < _${manualArrayField.name}Count; i++ {
		${manualArrayField.name}[i] = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(manualArrayField, arrayElementType, manualArrayField.parseExpression, parserArguments)})
	}
				<#-- In all other cases do we have to work with a list, that is later converted to an array -->
				<#else>
				<#-- For a length array, we read data till the read position of the buffer reaches a given position -->
					<#if field.isLengthArrayField()>
	// Length array
	_${manualArrayField.name}Length := ${helper.toIntegerParseExpression(manualArrayField, 16, manualArrayField.loopExpression, parserArguments)}
	var _${manualArrayField.name}List ${helper.getLanguageTypeNameForField(manualArrayField)}
	${manualArrayField.name}EndPos := positionAware.GetPos() + _${manualArrayField.name}Length
	for ;positionAware.GetPos() < ${manualArrayField.name}EndPos; {
		_${manualArrayField.name}List = append(_${manualArrayField.name}List, ((${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(manualArrayField, arrayElementType, manualArrayField.parseExpression, parserArguments)})))
					<#-- After parsing, update the current position, but only if it's needed -->
						<#if manualArrayField.loopExpression.contains("curPos")>
		curPos = positionAware.GetPos() - startPos
						</#if>
	}
					<#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
					<#elseif field.isTerminatedArrayField()>
	// Terminated array
	var _${manualArrayField.name}List ${helper.getLanguageTypeNameForField(manualArrayField)}
	{
		_values := &_${manualArrayField.name}List
		_ = _values
		for ;!((bool) (${helper.toParseExpression(manualArrayField, helper.boolTypeReference, manualArrayField.loopExpression, parserArguments, true)})); {
		_${manualArrayField.name}List = append(_${manualArrayField.name}List, ((${helper.getLanguageTypeNameForTypeReference(arrayElementType)}) (${helper.toParseExpression(manualArrayField, arrayElementType, manualArrayField.parseExpression, parserArguments)})))

					<#-- After parsing, update the current position, but only if it's needed -->
						<#if manualArrayField.loopExpression.contains("curPos")>
			curPos = positionAware.GetPos() - startPos
						</#if>
		}
	}
					</#if>
				<#--
					Convert the list into an array. However if the array is of a primitive
					type we have to iterate over it's elements and explicitly cast them.
					Otherwise a simple toArray call is fine.
				-->
					<#if manualArrayField.type.isSimpleTypeReference()>
	${manualArrayField.name} := make(${helper.getLanguageTypeNameForField(field)}, len(_${manualArrayField.name}List))
	// This happens when the size is set conditional to 0
	if len(${manualArrayField.name}) == 0 {
		${manualArrayField.name} = nil
	}
	for i := 0; i < len(_${manualArrayField.name}List); i++ {
		${manualArrayField.name}[i] = ${helper.getLanguageTypeNameForField(field)}(_${manualArrayField.name}List[i])
	}
					<#else>
	${manualArrayField.name} := _${manualArrayField.name}List
					</#if>
				</#if>
	if closeErr := readBuffer.CloseContext("${manualArrayField.name}", utils.WithRenderAsList(true)); closeErr != nil {
		return nil, errors.Wrap(closeErr, "Error closing for ${manualArrayField.name}")<@emitImport import="github.com/pkg/errors" />
	}
				<#break>
			<#case "manual">
				<#assign manualField = field.asManualField().orElseThrow()>

	// Manual Field (${manualField.name})
	_${manualField.name}, _${manualField.name}Err := ${helper.toParseExpression(manualField, manualField.type, manualField.parseExpression, parserArguments)}
	if _${manualField.name}Err != nil {
		return nil, errors.Wrap(_${manualField.name}Err, "Error parsing '${manualField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
	var ${manualField.name} ${helper.getLanguageTypeNameForField(manualField)}
	if _${manualField.name} != nil {
            ${manualField.name} = _${manualField.name}.(${helper.getLanguageTypeNameForField(manualField)})
	}
			<#break>
			<#case "optional">
				<#assign optionalField = field.asOptionalField().orElseThrow()>

	// Optional Field (${optionalField.name}) (Can be skipped, if a given expression evaluates to false)
				<#if optionalField.conditionExpression.present && optionalField.conditionExpression.get().contains("curPos")>
	curPos = positionAware.GetPos() - startPos
				</#if>
	var ${optionalField.name} <#if !optionalField.type.isComplexTypeReference()>*</#if>${helper.getLanguageTypeNameForField(field)} = nil
	<#if optionalField.conditionExpression.present>
	if ${helper.toBooleanParseExpression(optionalField, optionalField.conditionExpression.get(), parserArguments)} </#if>{
			<#if optionalField.type.isSimpleTypeReference()>
		_val, _err := ${helper.getReadBufferReadMethodCall(optionalField.name, optionalField.type.asSimpleTypeReference().orElseThrow(), optionalField)}
		if _err != nil {
			return nil, errors.Wrap(_err, "Error parsing '${optionalField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
		}
		${optionalField.name} = &_val
			<#elseif helper.isEnumField(field)>
		if pullErr := readBuffer.PullContext("${optionalField.name}"); pullErr != nil {
			return nil, errors.Wrap(pullErr, "Error pulling for ${optionalField.name}")<@emitImport import="github.com/pkg/errors" />
		}
		_val, _err := ${helper.getLanguageTypeNameForField(field)}Parse(readBuffer)
		if _err != nil {
			return nil, errors.Wrap(_err, "Error parsing '${optionalField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
		}
		${optionalField.name} = &_val
		if closeErr := readBuffer.CloseContext("${optionalField.name}"); closeErr != nil {
			return nil, errors.Wrap(closeErr, "Error closing for ${optionalField.name}")<@emitImport import="github.com/pkg/errors" />
		}
			<#else>
		currentPos = positionAware.GetPos()
		if pullErr := readBuffer.PullContext("${optionalField.name}"); pullErr != nil {
			return nil, errors.Wrap(pullErr, "Error pulling for ${optionalField.name}")<@emitImport import="github.com/pkg/errors" />
		}
				<@compress single_line=true>
                <#assign complexTypeReference=optionalField.type.asComplexTypeReference().orElseThrow()>
                <#assign typeName=complexTypeReference.name>
                <#assign typeDefinition=complexTypeReference.typeDefinition>
                <#if typeDefinition.isDiscriminatedChildTypeDefinition()>
                <#-- Usually you don't use child directly unless they are parameterized #-->
                    <#assign typeName=typeDefinition.parentType.orElseThrow().name>
                    <#assign typeDefinition=typeDefinition.parentType.orElseThrow()>
                </#if>
				<#assign hasRefParams=complexTypeReference.params.isPresent()>
				<#assign refParams>
					<#if hasRefParams>
							,
						<#list complexTypeReference.params.orElseThrow() as parserArgument>
							${helper.toTypedParseExpression(optionalField, helper.getArgumentType(optionalField.type, parserArgument?index), parserArgument, parserArguments)}
							<#sep>, </#sep>
						</#list>
					</#if>
				</#assign>
		_val, _err := ${typeName}Parse(readBuffer${refParams})
                </@compress>

		switch {
		case errors.Is(_err, utils.ParseAssertError{}) || errors.Is(_err, io.EOF):<@emitImport import="io" />
			Plc4xModelLog.Debug().Err(_err).Msg("Resetting position because optional threw an error")
			readBuffer.Reset(currentPos)
		case _err != nil:
			return nil, errors.Wrap(_err, "Error parsing '${optionalField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
		default:
			${optionalField.name} = _val.(${helper.getLanguageTypeNameForField(field)})
			if closeErr := readBuffer.CloseContext("${optionalField.name}"); closeErr != nil {
				return nil, errors.Wrap(closeErr, "Error closing for ${optionalField.name}")<@emitImport import="github.com/pkg/errors" />
			}
		}
			</#if>
	}
			<#break>
			<#case "assert">
				<#assign assertField = field.asAssertField().orElseThrow()>

	// Assert Field (${assertField.name}) (Can be skipped, if a given expression evaluates to false)
				<#if assertField.conditionExpression.contains("curPos")>
	curPos = positionAware.GetPos() - startPos
				</#if>
			<#if assertField.type.isSimpleTypeReference()>
	${assertField.name}, _err := ${helper.getReadBufferReadMethodCall(assertField.name, assertField.type.asSimpleTypeReference().orElseThrow(), assertField)}
	if _err != nil {
		return nil, errors.Wrap(_err, "Error parsing '${assertField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
			<#elseif helper.isEnumField(field)>
	${assertField.name}, _err := ${helper.getLanguageTypeNameForField(field)}Parse(readBuffer)
	if _err != nil {
		return nil, errors.Wrap(_err, "Error parsing '${assertField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
			<#else>
				<@compress single_line=true>
                <#assign complexTypeReference=assertField.type.asComplexTypeReference().orElseThrow()>
                <#assign typeName=complexTypeReference.name>
                <#assign typeDefinition=complexTypeReference.typeDefinition>
                <#if typeDefinition.isDiscriminatedChildTypeDefinition()>
                <#-- Usually you don't use child directly unless they are parameterized #-->
                    <#assign typeName=typeDefinition.parentType.orElseThrow().name>
                    <#assign typeDefinition=typeDefinition.parentType.orElseThrow()>
                </#if>
				<#assign hasRefParams=complexTypeReference.params.isPresent() && typeDefinition.parserArguments.isPresent()>
				<#assign refParams> <#if hasRefParams>, <#list complexTypeReference.params.orElseThrow() as typeParam>${helper.toTypedParseExpression(assertField, null, typeParam, null)}<#sep>, </#sep></#list> </#if> </#assign>
                </@compress>
	_val, _err := ${helper.getLanguageTypeNameForField(field)}Parse(readBuffer${refParams})
	if _err != nil {
		return nil, errors.Wrap(_err, "Error parsing '${assertField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
	${assertField.name} = _val
			</#if>
	if ${assertField.name} != ${helper.toParseExpression(assertField, assertField.type, assertField.conditionExpression, parserArguments)} {
		return nil, errors.WithStack(utils.ParseAssertError{"${assertField.conditionExpression} doesn't match"})<@emitImport import="github.com/pkg/errors" />
	}
				<#break>
			<#case "padding">
				<#assign paddingField = field.asPaddingField().orElseThrow()>
				<#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>

	// Padding Field (padding)
	{
		if pullErr := readBuffer.PullContext("padding", utils.WithRenderAsList(true)); pullErr != nil {
			return nil, errors.Wrap(pullErr, "Error pulling for padding")<@emitImport import="github.com/pkg/errors" />
		}
		_timesPadding := (${helper.toParseExpression(paddingField, helper.intTypeReference, paddingField.paddingCondition, parserArguments)})
		for ;(readBuffer.HasMore(${helper.getNumBits(simpleTypeReference)})) && (_timesPadding > 0);_timesPadding-- {
			// Just read the padding data and ignore it
			_, _err := ${helper.getReadBufferReadMethodCall("", simpleTypeReference, paddingField)}
			if _err != nil {
				return nil, errors.Wrap(_err, "Error parsing 'padding' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
			}
		}
		if closeErr := readBuffer.CloseContext("padding", utils.WithRenderAsList(true)); closeErr != nil {
			return nil, errors.Wrap(closeErr, "Error closing for padding")<@emitImport import="github.com/pkg/errors" />
		}
	}
				<#break>
			<#case "reserved">
				<#assign reservedField = field.asReservedField().orElseThrow()>
				<#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>

	var reservedField${reservedFieldIndex} *${helper.getLanguageTypeNameForTypeReference(simpleTypeReference)}
	// Reserved Field (Compartmentalized so the "reserved" variable can't leak)
	{
		reserved, _err := ${helper.getReadBufferReadMethodCall("reserved", simpleTypeReference, reservedField)}
		if _err != nil {
			return nil, errors.Wrap(_err, "Error parsing 'reserved' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
		}
		if ${helper.toTypeSafeCompare(reservedField)} {
			Plc4xModelLog.Info().Fields(map[string]interface{}{
				"expected value": ${helper.getReservedValue(reservedField)},
				"got value": reserved,
			}).Msg("Got unexpected response for reserved field.")
			// We save the value, so it can be re-serialized
			reservedField${reservedFieldIndex}<#assign reservedFieldIndex=reservedFieldIndex+1> = &reserved
		}
	}
				<#break>
			<#case "simple">
				<#assign simpleField = field.asSimpleField().orElseThrow()>

	// Simple Field (${simpleField.name})
				<#if !simpleField.type.isSimpleTypeReference()>
	if pullErr := readBuffer.PullContext("${simpleField.name}"); pullErr != nil {
		return nil, errors.Wrap(pullErr, "Error pulling for ${simpleField.name}")<@emitImport import="github.com/pkg/errors" />
	}
				</#if>
    <#assign simpleFieldLogicalName><#if simpleField.type.isSimpleTypeReference() && !helper.isEnumField(field)>${simpleField.name}<#else>${simpleField.typeName}</#if></#assign>
	<@compress single_line=true>
	_${simpleField.name}, _${simpleField.name}Err :=
	<#if simpleField.type.isSimpleTypeReference()>
		${helper.getReadBufferReadMethodCall(simpleFieldLogicalName, simpleField.type.asSimpleTypeReference().orElseThrow(), null, simpleField)}
	<#else>
        <#assign nonSimpleTypeReference=simpleField.type.asNonSimpleTypeReference().orElseThrow()>
        <#assign typeName=nonSimpleTypeReference.name>
        <#assign typeDefinition=nonSimpleTypeReference.typeDefinition>
        <#if typeDefinition.isComplexTypeDefinition() && typeDefinition.asComplexTypeDefinition().orElseThrow().isDiscriminatedChildTypeDefinition()>
        <#-- Usually you don't use child directly unless they are parameterized #-->
            <#assign typeName=typeDefinition.asComplexTypeDefinition().orElseThrow().parentType.orElseThrow().name>
            <#assign typeDefinition=typeDefinition.asComplexTypeDefinition().orElseThrow().parentType.orElseThrow()>
        </#if>
        <#assign hasRefParams=nonSimpleTypeReference.params.isPresent()>
        <#assign refParams>
            <#if hasRefParams>
				,
                <#list nonSimpleTypeReference.params.orElseThrow() as parserArgument>
					<#assign parserTypeRef=helper.getArgumentType(simpleField.type, parserArgument?index)>
					<#if !parserTypeRef.isComplexTypeReference()>${helper.getLanguageTypeNameForTypeReference(parserTypeRef)}(</#if>
                    ${helper.toTypedParseExpression(simpleField, parserTypeRef, parserArgument, parserArguments)}
                    <#if !parserTypeRef.isComplexTypeReference()>)</#if>
                    <#sep>, </#sep>
                </#list>
            </#if>
        </#assign>
		${typeName}Parse(readBuffer${refParams})
	</#if>
    </@compress>

	if _${simpleField.name}Err != nil {
			<#if helper.isEnumField(field)>
		return nil, errors.Wrap(_${simpleField.name}Err, "Error parsing '${simpleField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
			<#else>
		return nil, errors.Wrap(_${simpleField.name}Err, "Error parsing '${simpleField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
			</#if>
	}
	${simpleField.name} := _${simpleField.name}<#if helper.isComplex(field) && !helper.isEnumField(field) && !field.isArrayField()>.(${helper.getLanguageTypeNameForField(field)})</#if>
				<#if !simpleField.type.isSimpleTypeReference()>
	if closeErr := readBuffer.CloseContext("${simpleField.name}"); closeErr != nil {
		return nil, errors.Wrap(closeErr, "Error closing for ${simpleField.name}")<@emitImport import="github.com/pkg/errors" />
	}
				</#if>
				<#break>

			<#case "switch">
				<#assign switchField = field.asSwitchField().orElseThrow()>

	// Switch Field (Depending on the discriminator values, passes the instantiation to a sub-type)
	type ${type.name}ChildSerializeRequirement interface {
		${type.name}
		InitializeParent(${type.name}<#if type.propertyFields?has_content>, </#if> <#list type.propertyFields as field><#if helper.needsPointerAccess(field)>*</#if>${helper.getLanguageTypeNameForField(field)}<#sep>, </#sep></#list>)
		GetParent() ${type.name}
	}
	var _childTemp interface{}
	var _child ${type.name}ChildSerializeRequirement
	var typeSwitchError error
	switch {
				<#list switchField.cases as case>
					<@compress single_line=true>
						<#if case.discriminatorValueTerms?has_content>
							case
							<#list case.discriminatorValueTerms as discriminatorValueTerm>
								<#if helper.isWildcard(discriminatorValueTerm)> <#--TODO: we could use the above isWildcard as filter but then the switchField.discriminatorExpressions[discriminatorValueTerm?index] is offset. Solution would be to have it as a pair list-->
                                    ${discriminatorValueTerm?index}==${discriminatorValueTerm?index}
								<#else>
									<#assign discriminatorExpression=switchField.discriminatorExpressions[discriminatorValueTerm?index]>
									<#assign parsedDiscriminatorExpression=helper.toParseExpression(null, null, discriminatorExpression, parserArguments)>
									<#-- We remove debug informations as we need them for lookup-->
									<#assign parsedDiscriminatorExpression=tracer.removeTraces(parsedDiscriminatorExpression)>
									<#if helper.getDiscriminatorTypes()[parsedDiscriminatorExpression]??><#assign discriminatorType=helper.getDiscriminatorTypes()[parsedDiscriminatorExpression]></#if>
									${helper.toParseExpression(null, null, discriminatorExpression, parserArguments)} ==
									<#if helper.discriminatorValueNeedsStringEqualityCheck(discriminatorExpression)>"${helper.toParseExpression(null, discriminatorType,discriminatorValueTerm, parserArguments)}"
									<#elseif discriminatorType?has_content && discriminatorType.isNonSimpleTypeReference()>
										<#if discriminatorType.isEnumTypeReference()>
											<#assign enumType=helper.getLanguageTypeNameForTypeReference(discriminatorType)>
											<#assign enumTypeWithoutTraces=tracer.removeTraces(enumType)>
											<#assign enumTypeTraces=tracer.extractTraces(enumType)>
											<#assign enumValue=helper.toParseExpression(null, discriminatorType,discriminatorValueTerm, parserArguments)>
											<#assign enumValueWithoutTraces=tracer.removeTraces(enumValue)>
											<#assign enumValueTraces=tracer.extractTraces(enumValue)>
											${enumTypeTraces}${enumValueTraces}${enumTypeWithoutTraces}_${enumValueWithoutTraces?replace("Get","")?replace("()","")}
										<#else>
											${helper.toParseExpression(null, discriminatorType,discriminatorValueTerm, parserArguments)}
										</#if>
									<#else>
										${helper.toParseExpression(null, discriminatorType,discriminatorValueTerm, parserArguments)}
									</#if>
								</#if>
								<#sep> && </#sep>
							</#list>
						<#else>
							<#assign defaultCaseOutput=true>
							case true
						</#if>: // ${case.name}
                    </@compress>

		_childTemp, typeSwitchError = ${case.name}Parse(readBuffer<#if case.allParserArguments.isPresent()>, <#list case.allParserArguments.orElseThrow() as parserArgument><#if <#--TODO: here DF1ResponseMessage throws a payloadLength not present: meaning that some feature is not rightfully implemented as the result should be a error not a missing pointer-->type.getPropertyFieldFromThisOrParentByName(parserArgument.name).isPresent() && type.getPropertyFieldFromThisOrParentByName(parserArgument.name).orElseThrow().isOptionalField()>*</#if>${parserArgument.name}<#sep>, </#sep></#list></#if>)
				</#list>
	default:
		typeSwitchError = errors.Errorf("Unmapped type for<#if switchField.getDiscriminatorExpressions()?has_content> parameters [<#list switchField.getDiscriminatorExpressions() as discriminatorExpression>${discriminatorExpression.stringRepresentation()}=%v<#sep>, </#sep></#list>]</#if>"<#if switchField.getDiscriminatorExpressions()?has_content>, <#list switchField.getDiscriminatorExpressions() as discriminatorExpression>${helper.toParseExpression(null, null, discriminatorExpression, parserArguments)}<#sep>, </#sep></#list></#if>)<@emitImport import="github.com/pkg/errors" />
	}
	if typeSwitchError != nil {
		return nil, errors.Wrap(typeSwitchError, "Error parsing sub-type for type-switch of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
	_child = _childTemp.(${type.name}ChildSerializeRequirement)
				<#break>

			<#case "unknown">
				<#assign unknownField = field.asUnknownField().orElseThrow()>

	// Unknown field
	_, _UnknownErr := ${helper.getReadBufferReadMethodCall("unknown", unknownField.type.asSimpleTypeReference().orElseThrow(), null, unknownField)}
	if _UnknownErr != nil {
		return nil, errors.Wrap(_UnknownErr, "Error parsing unknown field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
				<#break>

			<#case "virtual">
				<#assign virtualField = field.asVirtualField().orElseThrow()>

	// Virtual field
	_${virtualField.name} := ${helper.toParseExpression(virtualField, virtualField.type, virtualField.valueExpression, parserArguments, true)}
			<#if virtualField.type.isSimpleTypeReference() || virtualField.type.isEnumTypeReference()>
	${virtualField.name} := ${helper.getLanguageTypeNameForTypeReference(virtualField.type)}(_${virtualField.name})
			<#else>
	${virtualField.name} := _${virtualField.name}
			</#if>
	_ = ${virtualField.name}
				<#break>
            <#case "validation">
                <#assign validationField = field.asValidationField().orElseThrow()>

	// Validation
	if (!(${helper.toParseExpression(validationField, helper.boolTypeReference, validationField.getValidationExpression(), null)})) {
		<#assign errorType="ParseValidationError">
		<#if !validationField.shouldFail()><#assign errorType="ParseAssertError"></#if>
		return nil, errors.WithStack(utils.${errorType}{${validationField.getDescription().orElse("\"Validation failed\"")}})<@emitImport import="github.com/pkg/errors" />
	}
                <#break>
            <#case "peek">
                <#assign peekField = field.asPeekField().orElseThrow()>

				// Peek Field (${peekField.name})
                <#if peekField.type.isSimpleTypeReference()>
				currentPos = positionAware.GetPos()
				${peekField.name}, _err := ${helper.getReadBufferReadMethodCall(peekField.name, peekField.type.asSimpleTypeReference().orElseThrow(), peekField)}
				if _err != nil {
					return nil, errors.Wrap(_err, "Error parsing '${peekField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
				}
                <#elseif helper.isEnumField(field)>
				currentPos = positionAware.GetPos()
				if pullErr := readBuffer.PullContext("${peekField.name}"); pullErr != nil {
					return nil, errors.Wrap(pullErr, "Error pulling for ${peekField.name}")<@emitImport import="github.com/pkg/errors" />
				}
				${peekField.name}, _err := ${helper.getLanguageTypeNameForField(field)}Parse(readBuffer)
				if _err != nil {
					return nil, errors.Wrap(_err, "Error parsing '${peekField.name}' field of ${type.name}")<@emitImport import="github.com/pkg/errors" />
				}
				if closeErr := readBuffer.CloseContext("${peekField.name}"); closeErr != nil {
					return nil, errors.Wrap(closeErr, "Error closing for ${peekField.name}")<@emitImport import="github.com/pkg/errors" />
				}
                <#else>
				currentPos = positionAware.GetPos()
				if pullErr := readBuffer.PullContext("${peekField.name}"); pullErr != nil {
					return nil, errors.Wrap(pullErr, "Error pulling for ${peekField.name}")<@emitImport import="github.com/pkg/errors" />
				}
                    <@compress single_line=true>
                        <#assign complexTypeReference=peekField.type.asComplexTypeReference().orElseThrow()>
                        <#assign typeName=complexTypeReference.name>
                        <#assign typeDefinition=complexTypeReference.typeDefinition>
                        <#if typeDefinition.isDiscriminatedChildTypeDefinition()>
                        <#-- Usually you don't use child directly unless they are parameterized #-->
                            <#assign typeName=typeDefinition.parentType.orElseThrow().name>
                            <#assign typeDefinition=typeDefinition.parentType.orElseThrow()>
                        </#if>
                        <#assign hasRefParams=complexTypeReference.params.isPresent()>
                        <#assign refParams>
                            <#if hasRefParams>
								,
                                <#list complexTypeReference.params.orElseThrow() as parserArgument>
                                    ${helper.toTypedParseExpression(peekField, helper.getArgumentType(peekField.type, parserArgument?index), parserArgument, parserArguments)}
                                    <#sep>, </#sep>
                                </#list>
                            </#if>
                        </#assign>
				${peekField.name}, _ := ${typeName}Parse(readBuffer${refParams})
                    </@compress>
                </#if>

				readBuffer.Reset(currentPos)
                <#break>
		</#switch>
	</#list>

	if closeErr := readBuffer.CloseContext("${type.name}"); closeErr != nil {
		return nil, errors.Wrap(closeErr, "Error closing for ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}

	<#if type.isDiscriminatedParentTypeDefinition()>
	// Finish initializing
		<@compress single_line=true>
	_child.InitializeParent(_child
			<#if type.propertyFields?has_content>, </#if>
			<#list type.propertyFields as field>
				${field.name}
				<#sep>, </#sep>
			</#list>
			)
		</@compress>

        <#list reservedFields as reservedField>
	_child.GetParent().(*_${type.name}).reservedField${reservedField?index} = reservedField${reservedField?index}
        </#list>
	return _child, nil
	<#elseif type.parentType.isPresent()>
	// Create a partially initialized instance
	_child := &_${type.name}{
		_${type.parentType.orElseThrow().name}: &_${type.parentType.orElseThrow().name}{
        <#if filteredParserArguments?has_content>
            <#list filteredParserArguments?filter(arg->type.getParentType().orElseThrow().isParserArgument(arg.name)) as arg>
			${arg.name?cap_first}: ${arg.name},
            </#list>
        </#if>
		},
		<#list type.propertyFields as field>
		${field.name?cap_first}: ${field.name},
		</#list>
        <#list reservedFields as reservedField>
		reservedField${reservedField?index}: reservedField${reservedField?index},
        </#list>
	}
	_child._${type.parentType.orElseThrow().name}._${type.parentType.orElseThrow().name}ChildRequirements = _child
	return _child, nil
	<#else>
	// Create the instance
	return &_${type.name}{
		<#if filteredParserArguments?has_content>
        	<#list filteredParserArguments as arg>
            ${arg.name?cap_first}: ${arg.name},
        	</#list>
		</#if>
		<#list type.propertyFields as field>
			${field.name?cap_first}: ${field.name},
		</#list>
		<#list reservedFields as reservedField>
			reservedField${reservedField?index}: reservedField${reservedField?index},
        </#list>
		}, nil
	</#if>
}

<#if type.isDiscriminatedParentTypeDefinition()>
func (pm *_${type.name}) SerializeParent(writeBuffer utils.WriteBuffer, child ${type.name}, serializeChildFunction func() error) error {
	// We redirect all calls through client as some methods are only implemented there
	m := child
	_ = m
<#else>
func (m *_${type.name}) Serialize(writeBuffer utils.WriteBuffer) error {
</#if>
	positionAware := writeBuffer
	_ = positionAware
	<#if helper.hasFieldOfType("unknown")>
	return errors.New("Unknown field not serializable")
	<#else>
		<#if !type.isDiscriminatedChildTypeDefinition()>
	if pushErr :=writeBuffer.PushContext("${type.name}"); pushErr != nil {
		return errors.Wrap(pushErr, "Error pushing for ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
		</#if>
		<#assign arraySizeInBytesHelpers=helper.requiresHelperFunctions("ARRAY_SIZE_IN_BYTES")>
		<#if arraySizeInBytesHelpers?has_content>
			<#list arraySizeInBytesHelpers?keys as key>
				<#assign typeName=arraySizeInBytesHelpers[key]>
	${key}ArraySizeInBytes := func(items ${typeName}) uint32 {
		var sizeInBytes uint32 = 0
		for _, v := range items {
			sizeInBytes += uint32(v.GetLengthInBytes())
		}
		return sizeInBytes
	}
			</#list>
		</#if>
		<#if type.isDiscriminatedChildTypeDefinition()>
	ser := func() error {
		if pushErr := writeBuffer.PushContext("${type.name}"); pushErr != nil {
			return errors.Wrap(pushErr, "Error pushing for ${type.name}")<@emitImport import="github.com/pkg/errors" />
		}
		</#if>
        <#assign reservedFieldIndex=0>
		<#list type.fields as field>
			<#switch field.typeName>
				<#case "array">
					<#assign arrayField = field.asArrayField().orElseThrow()>
					<#assign arrayElementType = arrayField.type.elementTypeReference>

	// Array Field (${arrayField.name})
					<#if arrayElementType.isByteBased()>
	// Byte Array field (${arrayField.name})
	if err := writeBuffer.WriteByteArray("${arrayField.name}", m.Get${arrayField.name?cap_first}()); err != nil {
		return errors.Wrap(err, "Error serializing '${arrayField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
					<#else>
	if pushErr := writeBuffer.PushContext("${arrayField.name}", utils.WithRenderAsList(true)); pushErr != nil {
		return errors.Wrap(pushErr, "Error pushing for ${arrayField.name}")<@emitImport import="github.com/pkg/errors" />
	}
						<#if arrayElementType.isComplexTypeReference() && (helper.needsVariable(arrayField, "curItem", true) || helper.needsVariable(arrayField, "lastItem", true))>
	itemCount := uint16(len(m.Get${arrayField.name?cap_first}()))
	var curItem uint16 = 0
					</#if>
	for _, _element := range m.Get${arrayField.name?cap_first}() {
						<#if arrayElementType.isSimpleTypeReference()>
							<#assign simpleTypeReference = arrayElementType.asSimpleTypeReference().orElseThrow()>
		_elementErr := ${helper.getWriteBufferWriteMethodCall("", simpleTypeReference, "_element", arrayField)}
						<#else>
						<#assign complexTypeReference = arrayElementType>
						<#if helper.needsVariable(arrayField, "lastItem", true)>
		var lastItem bool = curItem == (itemCount - 1)
						</#if>
		_elementErr := writeBuffer.WriteSerializable(_element)
					</#if>
		if _elementErr != nil {
			return errors.Wrap(_elementErr, "Error serializing '${arrayField.name}' field")<@emitImport import="github.com/pkg/errors" />
		}
						<#if helper.needsVariable(arrayField, "curItem", true) || helper.needsVariable(arrayField, "lastItem", true)>
		curItem++
					</#if>
	}
	if popErr := writeBuffer.PopContext("${arrayField.name}", utils.WithRenderAsList(true)); popErr != nil {
		return errors.Wrap(popErr, "Error popping for ${arrayField.name}")<@emitImport import="github.com/pkg/errors" />
	}
					</#if>
					<#break>
				<#case "checksum">
					<#assign checksumField = field.asChecksumField().orElseThrow()>
					<#assign simpleTypeReference = checksumField.type.asSimpleTypeReference().orElseThrow()>

	// Checksum Field (checksum) (Calculated)
	{
		_checksum, _checksumErr := ${helper.toSerializationExpression(checksumField, checksumField.type, checksumField.checksumExpression, parserArguments)}
		if _checksumErr != nil {
			return errors.Wrap(_checksumErr, "Checksum calculation failed")<@emitImport import="github.com/pkg/errors" />
 		}
		_checksumWriteErr := ${helper.getWriteBufferWriteMethodCall("checksum", simpleTypeReference, "(_checksum)", checksumField)}
		if _checksumWriteErr != nil {
			return errors.Wrap(_checksumWriteErr, "Error serializing 'checksum' field")
		}
	}
					<#break>
				<#case "const">
					<#assign constField = field.asConstField().orElseThrow()>

					<#if constField.type.isEnumTypeReference()>
						<#-- TODO: finish me-->
                        <#if constField.fieldName?has_content>
                            <#assign enumValueFieldAccessor=type.name+"_"+constField.name?upper_case>
	// Const field (${constField.name})
	_${constField.name}Err := ${helper.getWriteBufferWriteMethodCall(constField, constField.type.name, helper.getEnumFieldSimpleTypeReference(constField.type, constField.fieldName), enumValueFieldAccessor, constField, "utils.WithAdditionalStringRepresentation(${type.name}_${constField.name?upper_case}.PLC4XEnumName())")}
	if _${constField.name}Err != nil {
		return errors.Wrap(_${constField.name}Err, "Error serializing '${constField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
                        <#else>
	// Const field (${constField.name})
	${constField.name} := Cast${helper.getLanguageTypeNameForField(field)}(${type.name}_${constField.name?upper_case})
	_${constField.name}Err := writeBuffer.WriteSerializable(${constField.name})
	if _${constField.name}Err != nil {
		return errors.Wrap(_${constField.name}Err, "Error serializing '${constField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
                        </#if>
					<#else>
	// Const Field (${constField.name})
	_${constField.name}Err := ${helper.getWriteBufferWriteMethodCall(constField.name, constField.type.asSimpleTypeReference().orElseThrow(), constField.referenceValue, constField)}
	if _${constField.name}Err != nil {
		return errors.Wrap(_${constField.name}Err, "Error serializing '${constField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
					</#if>
					<#break>
				<#case "discriminator">
					<#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>

	// Discriminator Field (${discriminatorField.name}) (Used as input to a switch field)
	${discriminatorField.name} := ${helper.getLanguageTypeNameForField(field)}(child.Get${discriminatorField.name?cap_first}())
					<#if helper.isEnumField(field)>
                        <#assign namedField = field.asNamedField().orElseThrow()>
	if pushErr := writeBuffer.PushContext("${namedField.name}"); pushErr != nil {
		return errors.Wrap(pushErr, "Error pushing for ${namedField.name}")<@emitImport import="github.com/pkg/errors" />
	}
	_${discriminatorField.name}Err := writeBuffer.WriteSerializable(${discriminatorField.name})
	if popErr := writeBuffer.PopContext("${namedField.name}"); popErr != nil {
		return errors.Wrap(popErr, "Error popping for ${namedField.name}")<@emitImport import="github.com/pkg/errors" />
	}
					<#else>
                        <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>
	_${discriminatorField.name}Err := ${helper.getWriteBufferWriteMethodCall(discriminatorField.name, simpleTypeReference, "(" + discriminatorField.name + ")", discriminatorField)}
					</#if>

	if _${discriminatorField.name}Err != nil {
		return errors.Wrap(_${discriminatorField.name}Err, "Error serializing '${discriminatorField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
					<#break>
				<#case "enum">
					<#assign enumField = field.asEnumField().orElseThrow()>

	if pushErr := writeBuffer.PushContext("${enumField.name}"); pushErr != nil {
		return errors.Wrap(pushErr, "Error pushing for ${enumField.name}")<@emitImport import="github.com/pkg/errors" />
	}
							<#assign enumValueFieldAccessor="m."+enumField.name?cap_first+"."+enumField.fieldName?cap_first+"()">
	// Enum field (${enumField.name})
	_${enumField.name}Err := ${helper.getWriteBufferWriteMethodCall(enumField.type.asNonSimpleTypeReference().orElseThrow().name, helper.getEnumFieldSimpleTypeReference(enumField.type, enumField.fieldName), enumValueFieldAccessor, enumField, "utils.WithAdditionalStringRepresentation(m.Get${enumField.name?cap_first}().PLC4XEnumName())")}
	if _${enumField.name}Err != nil {
		return errors.Wrap(_${enumField.name}Err, "Error serializing '${enumField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
	if popErr := writeBuffer.PopContext("${enumField.name}"); popErr != nil {
		return errors.Wrap(popErr, "Error popping for ${enumField.name}")<@emitImport import="github.com/pkg/errors" />
	}
					<#break>
				<#case "implicit">
					<#assign implicitField = field.asImplicitField().orElseThrow()>
					<#assign simpleTypeReference = implicitField.type.asSimpleTypeReference().orElseThrow()>

	// Implicit Field (${implicitField.name}) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
	${implicitField.name} := ${helper.getLanguageTypeNameForField(field)}(${helper.toSerializationExpression(implicitField, implicitField.type, implicitField.serializeExpression, parserArguments)})
	_${implicitField.name}Err := ${helper.getWriteBufferWriteMethodCall(implicitField.name, simpleTypeReference, "(" + implicitField.name + ")", implicitField)}
	if _${implicitField.name}Err != nil {
		return errors.Wrap(_${implicitField.name}Err, "Error serializing '${implicitField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
					<#break>
				<#case "manualArray">
					<#assign manualArrayField = field.asManualArrayField().orElseThrow()>
                	<#assign arrayElementType = manualArrayField.type.elementTypeReference>

	// Manual Array Field (${manualArrayField.name})
	if pushErr := writeBuffer.PushContext("${manualArrayField.name}", utils.WithRenderAsList(true)); pushErr != nil {
		return errors.Wrap(pushErr, "Error pushing for ${manualArrayField.name}")<@emitImport import="github.com/pkg/errors" />
	}
	for _, m := range m.Get${manualArrayField.name?cap_first}() {
			<#-- TODO at the moment the implementation below is broken as element get prefixed wrong therefore we substract it with substring-->
			${helper.toSerializationExpression(manualArrayField, arrayElementType, manualArrayField.serializeExpression, parserArguments)?replace("m.","")}
	}
	if popErr := writeBuffer.PopContext("${manualArrayField.name}", utils.WithRenderAsList(true)); popErr != nil {
		return errors.Wrap(popErr, "Error popping for ${manualArrayField.name}")<@emitImport import="github.com/pkg/errors" />
	}
					<#break>
				<#case "manual">
					<#assign manualField = field.asManualField().orElseThrow()>

	// Manual Field (${manualField.name})
	_${manualField.name}Err := ${helper.toSerializationExpression(manualField, manualField.type, manualField.serializeExpression, parserArguments)}
	if _${manualField.name}Err != nil {
		return errors.Wrap(_${manualField.name}Err, "Error serializing '${manualField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
					<#break>
				<#case "optional">
					<#assign optionalField = field.asOptionalField().orElseThrow()>

	// Optional Field (${optionalField.name}) (Can be skipped, if the value is null)
	var ${optionalField.name} <#if !optionalField.type.isComplexTypeReference()>*</#if>${helper.getLanguageTypeNameForField(field)} = nil
	if m.Get${optionalField.name?cap_first}() != nil {
					<#if optionalField.type.isSimpleTypeReference()>
						<#assign simpleTypeReference = optionalField.type.asSimpleTypeReference().orElseThrow()>
		${optionalField.name} = m.Get${optionalField.name?cap_first}()
		_${optionalField.name}Err := ${helper.getWriteBufferWriteMethodCall(optionalField.name, simpleTypeReference, "*(" + optionalField.name + ")", optionalField)}
					<#else>
						<#assign complexTypeReference = optionalField.type>
		if pushErr := writeBuffer.PushContext("${optionalField.name}"); pushErr != nil {
			return errors.Wrap(pushErr, "Error pushing for ${optionalField.name}")<@emitImport import="github.com/pkg/errors" />
		}
		${optionalField.name} = m.Get${optionalField.name?cap_first}()
		_${optionalField.name}Err := writeBuffer.WriteSerializable(${optionalField.name})
		if popErr := writeBuffer.PopContext("${optionalField.name}"); popErr != nil {
			return errors.Wrap(popErr, "Error popping for ${optionalField.name}")<@emitImport import="github.com/pkg/errors" />
		}
					</#if>
		if _${optionalField.name}Err != nil {
			return errors.Wrap(_${optionalField.name}Err, "Error serializing '${optionalField.name}' field")<@emitImport import="github.com/pkg/errors" />
		}
	}
					<#break>
				<#case "padding">
					<#assign paddingField = field.asPaddingField().orElseThrow()>
					<#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>

	// Padding Field (padding)
	{
		if pushErr := writeBuffer.PushContext("padding", utils.WithRenderAsList(true)); pushErr != nil {
			return errors.Wrap(pushErr, "Error pushing for padding")<@emitImport import="github.com/pkg/errors" />
		}
		_timesPadding := uint8(${helper.toSerializationExpression(paddingField, helper.intTypeReference, paddingField.paddingCondition, parserArguments)})
		for ;_timesPadding > 0; _timesPadding-- {
			_paddingValue := ${helper.getLanguageTypeNameForField(field)}(${helper.toSerializationExpression(paddingField, paddingField.type, paddingField.paddingValue, parserArguments)})
			_paddingErr := ${helper.getWriteBufferWriteMethodCall("", simpleTypeReference, "(_paddingValue)", paddingField)}
			if _paddingErr != nil {
				return errors.Wrap(_paddingErr, "Error serializing 'padding' field")<@emitImport import="github.com/pkg/errors" />
			}
		}
		if popErr := writeBuffer.PopContext("padding", utils.WithRenderAsList(true)); popErr != nil {
			return errors.Wrap(popErr, "Error popping for padding")<@emitImport import="github.com/pkg/errors" />
		}
	}
					<#break>
				<#case "reserved">
					<#assign reservedField = field.asReservedField().orElseThrow()>
					<#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>

	// Reserved Field (reserved)
	{
		var reserved ${helper.getLanguageTypeNameForTypeReference(simpleTypeReference)} = ${helper.getReservedValue(reservedField)}
		if <#if type.isAbstract()>p</#if>m.reservedField${reservedFieldIndex} != nil {
			Plc4xModelLog.Info().Fields(map[string]interface{}{
				"expected value": ${helper.getReservedValue(reservedField)},
				"got value": reserved,
			}).Msg("Overriding reserved field with unexpected value.")
			reserved = *<#if type.isAbstract()>p</#if>m.reservedField${reservedFieldIndex}
		}<#assign reservedFieldIndex=reservedFieldIndex+1>
		_err := ${helper.getWriteBufferWriteMethodCall("reserved", simpleTypeReference, "reserved", reservedField)}
		if _err != nil {
			return errors.Wrap(_err, "Error serializing 'reserved' field")<@emitImport import="github.com/pkg/errors" />
		}
	}
					<#break>
				<#case "simple">
					<#assign simpleField = field.asSimpleField().orElseThrow()>

	// Simple Field (${simpleField.name})
					<#if simpleField.type.isSimpleTypeReference()>
						<#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>
						<#assign simpleFieldLogicalName><#if simpleField.type.isSimpleTypeReference() && !helper.isEnumField(field)>${simpleField.name}<#else>${simpleField.typeName}</#if></#assign>
	${simpleField.name} := ${helper.getLanguageTypeNameForField(field)}(m.Get${simpleField.name?cap_first}())
						<#if helper.isEnumField(field)>
	_${simpleField.name}Err := ${helper.getWriteBufferWriteMethodCall(simpleFieldLogicalName, simpleTypeReference, "(" + simpleField.name + ")", simpleField, "utils.WithAdditionalStringRepresentation(m.Get${simpleField.name?cap_first}().PLC4XEnumName())")}
						<#else>
	_${simpleField.name}Err := ${helper.getWriteBufferWriteMethodCall(simpleFieldLogicalName, simpleTypeReference, "(" + simpleField.name + ")", simpleField)}
						</#if>
					<#else>
						<#assign complexTypeReference = simpleField.type>
	if pushErr := writeBuffer.PushContext("${simpleField.name}"); pushErr != nil {
		return errors.Wrap(pushErr, "Error pushing for ${simpleField.name}")<@emitImport import="github.com/pkg/errors" />
	}
	_${simpleField.name}Err := writeBuffer.WriteSerializable(m.Get${simpleField.name?cap_first}())
	if popErr := writeBuffer.PopContext("${simpleField.name}"); popErr != nil {
		return errors.Wrap(popErr, "Error popping for ${simpleField.name}")<@emitImport import="github.com/pkg/errors" />
	}
					</#if>
	if _${simpleField.name}Err != nil {
		return errors.Wrap(_${simpleField.name}Err, "Error serializing '${simpleField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
					<#break>
				<#case "switch">
					<#assign switchField = field.asSwitchField().orElseThrow()>

	// Switch field (Depending on the discriminator values, passes the serialization to a sub-type)
	if _typeSwitchErr := serializeChildFunction(); _typeSwitchErr != nil {
		return errors.Wrap(_typeSwitchErr, "Error serializing sub-type field")<@emitImport import="github.com/pkg/errors" />
	}
					<#break>
				<#case "virtual">
                	<#assign virtualField = field.asVirtualField().orElseThrow()>
                	<#assign typedField = field.asTypedField().orElseThrow()>
                	<#assign namedField = field.asNamedField().orElseThrow()>
	// Virtual field
	if _${namedField.name}Err := writeBuffer.WriteVirtual("${namedField.name}", m.Get${namedField.name?cap_first}()); _${namedField.name}Err != nil {
		return errors.Wrap(_${namedField.name}Err, "Error serializing '${namedField.name}' field")<@emitImport import="github.com/pkg/errors" />
	}
					<#break>
			</#switch>
		</#list>

		<#if type.isDiscriminatedChildTypeDefinition()>
		if popErr := writeBuffer.PopContext("${type.name}"); popErr != nil {
			return errors.Wrap(popErr, "Error popping for ${type.name}")<@emitImport import="github.com/pkg/errors" />
		}
		return nil
	}
	return m.SerializeParent(writeBuffer, m, ser)
		<#else>
	if popErr := writeBuffer.PopContext("${type.name}"); popErr != nil {
		return errors.Wrap(popErr, "Error popping for ${type.name}")<@emitImport import="github.com/pkg/errors" />
	}
	return nil
		</#if>
	</#if>
}

<#if filteredParserArgumentsLocalToType?has_content>

////
// Arguments Getter

    <#list filteredParserArgumentsLocalToType as parserArgument>
func (m *_${type.name}) Get${parserArgument.name?cap_first}() ${helper.getLanguageTypeNameForTypeReference(parserArgument.type)} {
	return m.${parserArgument.name?cap_first}
}
    </#list>
//
////
</#if>

func (m *_${type.name}) is${type.name}() bool {
	return true
}

func (m *_${type.name}) String() string {
	if m == nil {
		return "<nil>"
	}
	writeBuffer := utils.NewWriteBufferBoxBasedWithOptions(true, true)
	if err := writeBuffer.WriteSerializable(m); err != nil {
		return err.Error()
	}
	return writeBuffer.GetBox().String()
}

</@importSectionWithContentBelow>

</#outputformat>
