<#--
  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.c.CLanguageTemplateHelper" -->
<#-- @ftlvariable name="tracer" type="org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer" -->
<#-- @ftlvariable name="type" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
<#if !type.isDiscriminatedChildTypeDefinition()>${helper.getSourceDirectory()?replace(".", "/")}/${helper.camelCaseToSnakeCase(type.name)}.c
/*
 * 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.
 */

#include <stdio.h>
#include <plc4c/spi/evaluation_helper.h>
#include <plc4c/driver_${protocolName}_static.h>

#include "${helper.camelCaseToSnakeCase(type.name)}.h"

// 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.parserArguments.orElseThrow()></#if>
<#-- Helper function to get the discriminator for a given enum type constant -->
<#if type.isDiscriminatedParentTypeDefinition()>
// Array of discriminator values that match the enum type constants.
// (The order is identical to the enum constants, so we can use the
// enum constant to directly access a given type's discriminator values)
const ${helper.getCTypeName(type.name)}_discriminator ${helper.getCTypeName(type.name)}_discriminators[] = {
    <#if type.getDiscriminatorCaseToKeyValueMap()?has_content>
        <#list type.getDiscriminatorCaseToKeyValueMap() as name, typeTerms>
  {/* ${helper.getCTypeName(name)} */
<#if true>   </#if><@compress single_line=true>
               <#list typeTerms as key, term>
                   <#if !helper.getDiscriminatorTypes()[key].isNonSimpleTypeReference()>
                .${key} = <#if term??>${helper.toParseExpression(type, null, term, parserArguments)}<#else>-1</#if><#sep>,
                   <#else>
                .${key} = <#if term??>${helper.getLanguageTypeNameForTypeReference(helper.getDiscriminatorTypes()[key])}_${term.asLiteral().orElseThrow().asVariableLiteral().orElseThrow().name}<#else>-1</#if><#sep>,
                   </#if>
               </#list>}<#sep >,
            </@compress>

        </#list>
    </#if>

};

// Function returning the discriminator values for a given type constant.
${helper.getCTypeName(type.name)}_discriminator ${helper.getCTypeName(type.name)}_get_discriminator(${helper.getCTypeName(type.name)}_type type) {
  return ${helper.getCTypeName(type.name)}_discriminators[type];
}

// Create an empty NULL-struct
static const ${helper.getCTypeName(type.name)} ${helper.getCTypeName(type.name)}_null_const;

${helper.getCTypeName(type.name)} ${helper.getCTypeName(type.name)}_null() {
  return ${helper.getCTypeName(type.name)}_null_const;
}

</#if>
<#if helper.getAllConstFields()?has_content>

// Constant values.
    <#list helper.getAllConstFields() as entry>
        <#assign constField=entry.key>
        <#assign parentType=entry.value>
static const ${helper.getLanguageTypeNameForField(constField)} ${helper.getCTypeName(parentType.name)?upper_case}_${helper.camelCaseToSnakeCase(constField.name)?upper_case}_const = ${helper.toParseExpression(type, constField, constField.referenceValue, parserArguments)};
${helper.getLanguageTypeNameForField(constField)} ${helper.getCTypeName(parentType.name)?upper_case}_${helper.camelCaseToSnakeCase(constField.name)?upper_case}() {
  return ${helper.getCTypeName(parentType.name)?upper_case}_${helper.camelCaseToSnakeCase(constField.name)?upper_case}_const;
}
    </#list>
</#if>

// Parse function.
plc4c_return_code ${helper.getCTypeName(type.name)}_parse(plc4c_spi_read_buffer* readBuffer, <#if parserArguments?has_content><#list parserArguments as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type)}<#if parserArgument.type.isComplexTypeReference()>*</#if> ${parserArgument.name}<#sep>, </#list>, </#if>${helper.getCTypeName(type.name)}** _message) {
  uint16_t startPos = plc4c_spi_read_get_pos(readBuffer);
  plc4c_return_code _res = OK;

  // Allocate enough memory to contain this data structure.
  (*_message) = malloc(sizeof(${helper.getCTypeName(type.name)}));
  if(*_message == NULL) {
    return NO_MEMORY;
  }
<#macro fieldName baseType field>${helper.getFieldName(baseType, field)}</#macro>
<#macro fieldParser baseType field indentContent>
    <#-- @ftlvariable name="baseType" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
    <#-- @ftlvariable name="field" type="org.apache.plc4x.plugins.codegenerator.types.fields.Field" -->
    <#-- @ftlvariable name="indentContent" type="java.lang.Boolean" -->
    <#assign parserArguments=baseType.allParserArguments.orElseThrow()>
    <#switch field.typeName>
        <#case "array">
            <#assign arrayField = field.asArrayField().orElseThrow()>
            <#assign elementTypeReference = arrayField.getType().getElementTypeReference()>
            <#-- @ftlvariable name="field" type="org.apache.plc4x.plugins.codegenerator.types.fields.ArrayField" -->

  // Array field (${arrayField.name})
            <#-- In all other cases do we have to work with a list, that is later converted to an array -->
  plc4c_list* ${arrayField.name} = NULL;
  plc4c_utils_list_create(&${arrayField.name});
  if(${arrayField.name} == NULL) {
    return NO_MEMORY;
  }
  {
            <#if arrayField.isCountArrayField()>
    // Count array
    uint16_t itemCount = (uint16_t) ${helper.toParseExpression(baseType, arrayField, arrayField.loopExpression, parserArguments)};
    for(int curItem = 0; curItem < itemCount; curItem++) {
      <#if !elementTypeReference.isSimpleTypeReference()>bool lastItem = curItem == (itemCount - 1);</#if>
      <#-- Inizialize a local variable with the simple type (Intentionally keeping the java-style names so they can be used in expressions) -->
<#if elementTypeReference.isSimpleTypeReference()>
    <#assign simpleTypeReference = elementTypeReference.asSimpleTypeReference().orElseThrow()>
      ${helper.getLanguageTypeNameForTypeReference(elementTypeReference)}* _value = malloc(sizeof(${helper.getLanguageTypeNameForTypeReference(elementTypeReference)}));
      _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "_value", arrayField)};
      if(_res != OK) {
        return _res;
      }
      plc4c_utils_list_insert_head_value(${arrayField.name}, _value);
<#else>
    <#assign nonSimpleTypeReference = elementTypeReference.asNonSimpleTypeReference().orElseThrow()>
      <#-- Inizialize a local variable with the complex type (Intentionally keeping the java-style names so they can be used in expressions) -->
      ${helper.getCTypeName(nonSimpleTypeReference.name)}* _value = NULL;
      _res = ${helper.getCTypeName(nonSimpleTypeReference.name)}_parse(readBuffer<#if elementTypeReference.isNonSimpleTypeReference() && elementTypeReference.asNonSimpleTypeReference().get().getParams().isPresent()>, <#list elementTypeReference.asNonSimpleTypeReference().get().getParams().get() as parserTerm>${helper.toParseExpression(baseType, arrayField, parserTerm, parserArguments)}<#sep>, </#sep></#list></#if>, (void*) &_value);
      if(_res != OK) {
        return _res;
      }
      plc4c_utils_list_insert_head_value(${arrayField.name}, _value);
</#if>
    }
            <#-- For a length array, we read data till the read position of the buffer reaches a given position -->
            <#elseif arrayField.isLengthArrayField()>
    // Length array
    uint8_t _${arrayField.name}Length = ${helper.toParseExpression(baseType, arrayField, arrayField.loopExpression, parserArguments)};
    uint8_t ${arrayField.name}EndPos = plc4c_spi_read_get_pos(readBuffer) + _${arrayField.name}Length;
    while(plc4c_spi_read_get_pos(readBuffer) < ${arrayField.name}EndPos) {
                <#-- Inizialize a local variable with the simple type (Intentionally keeping the java-style names so they can be used in expressions) -->
<#if elementTypeReference.isSimpleTypeReference()>
    <#assign simpleTypeReference = elementTypeReference.asSimpleTypeReference().orElseThrow()>
      ${helper.getLanguageTypeNameForTypeReference(elementTypeReference)} _value = ${helper.getNullValueForTypeReference(elementTypeReference)};
      _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&_value", arrayField)};
      if(_res != OK) {
        return _res;
      }
      plc4c_utils_list_insert_head_value(${arrayField.name}, &_value);
<#else>
    <#assign nonSimpleTypeReference = elementTypeReference.asNonSimpleTypeReference().orElseThrow()>
                <#-- Inizialize a local variable with the complex type (Intentionally keeping the java-style names so they can be used in expressions) -->
      ${helper.getCTypeName(nonSimpleTypeReference.name)}* _value = NULL;
      _res = ${helper.getCTypeName(nonSimpleTypeReference.name)}_parse(readBuffer<#if elementTypeReference.isNonSimpleTypeReference() && elementTypeReference.asNonSimpleTypeReference().get().getParams().isPresent()>, <#list elementTypeReference.asNonSimpleTypeReference().get().getParams().get() as parserTerm>${helper.toParseExpression(baseType, arrayField, parserTerm, parserArguments)}<#sep>, </#sep></#list></#if>, (void*) &_value);
      if(_res != OK) {
        return _res;
      }
      plc4c_utils_list_insert_head_value(${arrayField.name}, _value);
</#if>
                <#-- After parsing, update the current position, but only if it's needed -->
    }
            <#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
            <#elseif arrayField.isTerminatedArrayField()>
    // Terminated array
    while(!((bool) (${helper.toParseExpression(baseType, arrayField, arrayField.loopExpression, parserArguments)}))) {
            <#-- Inizialize a local variable with the simple type (Intentionally keeping the java-style names so they can be used in expressions) -->
<#if elementTypeReference.isSimpleTypeReference()>
    <#assign simpleTypeReference = elementTypeReference.asSimpleTypeReference().orElseThrow()>
      ${helper.getLanguageTypeNameForTypeReference(elementTypeReference)} _value = ${helper.getNullValueForTypeReference(elementTypeReference)};
      _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&_value", arrayField)};
      if(_res != OK) {
        return _res;
      }
      plc4c_utils_list_insert_head_value(${arrayField.name}, &_value);
<#else>
    <#assign nonSimpleTypeReference = elementTypeReference.asNonSimpleTypeReference().orElseThrow()>
                <#-- Inizialize a local variable with the complex type (Intentionally keeping the java-style names so they can be used in expressions) -->
      ${helper.getCTypeName(nonSimpleTypeReference.name)}* _value = NULL;
      _res = ${helper.getCTypeName(nonSimpleTypeReference.name)}_parse(readBuffer<#if elementTypeReference.isNonSimpleTypeReference() && elementTypeReference.asNonSimpleTypeReference().get().getParams().isPresent()>, <#list elementTypeReference.asNonSimpleTypeReference().get().getParams().get() as parserTerm>${helper.toParseExpression(baseType, arrayField, parserTerm, parserArguments)}<#sep>, </#sep></#list></#if>, (void*) &_value);
      if(_res != OK) {
        return _res;
      }
      plc4c_utils_list_insert_head_value(${arrayField.name}, _value);
</#if>
              <#-- After parsing, update the current position, but only if it's needed -->
    }
            </#if>
  }
  (*_message)-><@fieldName baseType=baseType field=arrayField/> = ${arrayField.name};
            <#break>
        <#case "checksum">
            <#assign checksumField = field.asChecksumField().orElseThrow()>
            <#assign simpleTypeReference = checksumField.type.asSimpleTypeReference().orElseThrow()>

  // Checksum Field (${checksumField.name})
  {
    // Create an array of all the bytes read in this message element so far.
    ${helper.getLanguageTypeNameForField(field)} _checksumRef = ${helper.getNullValueForTypeReference(checksumField.type)};
    _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&_checksumRef", checksumField)};
    if(_res != OK) {
      return _res;
    }
    ${helper.getLanguageTypeNameForField(field)} _checksum = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(baseType, checksumField, checksumField.checksumExpression, parserArguments)});
    if(_checksum != _checksumRef) {
      return PARSE_ERROR;
      // throw new ParseException(String.format("Checksum verification failed. Expected %04X but got %04X",_checksumRef & 0xFFFF, _checksum & 0xFFFF));
    }
  }
            <#break>
        <#case "const">
            <#assign constField = field.asConstField().orElseThrow()>
            <#assign simpleTypeReference = constField.type.asSimpleTypeReference().orElseThrow()>

  // Const Field (${constField.name})
  ${helper.getLanguageTypeNameForField(field)} ${constField.name} = ${helper.getNullValueForTypeReference(constField.type)};
  _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&" + constField.name, constField)};
  if(_res != OK) {
    return _res;
  }
  if(${constField.name} != ${helper.getCTypeName(baseType.name)?upper_case}_${helper.camelCaseToSnakeCase(constField.name)?upper_case}()) {
    return PARSE_ERROR;
    // throw new ParseException("Expected constant value " + ${helper.getCTypeName(baseType.name)?upper_case}_${helper.camelCaseToSnakeCase(constField.name)?upper_case} + " but got " + ${constField.name});
  }
            <#break>
        <#case "discriminator">
            <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
  // Discriminator Field (${discriminatorField.name})
        <#if discriminatorField.type.isSimpleTypeReference()>
            <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>

  // Discriminator Field (${discriminatorField.name}) (Used as input to a switch field)
  ${helper.getLanguageTypeNameForField(field)} ${discriminatorField.name} = ${helper.getNullValueForTypeReference(discriminatorField.type)};
  _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&" + discriminatorField.name, discriminatorField)};
        <#elseif discriminatorField.type.isEnumTypeReference()>
            <#assign enumTypeReference = discriminatorField.type.asEnumTypeReference().orElseThrow()>
  ${helper.getLanguageTypeNameForTypeReference(enumTypeReference)} ${discriminatorField.name};
  _res = ${helper.getCTypeName(enumTypeReference.name)}_parse(readBuffer, &${discriminatorField.name});
        </#if>
  if(_res != OK) {
    return _res;
  }
            <#break>
        <#case "enum">
            <#assign enumField = field.asEnumField().orElseThrow()>

  // Enum field (${enumField.name})
  ${helper.getLanguageTypeNameForField(field)} ${enumField.name} = ${helper.getNullValueForTypeReference(enumField.type)};
  {
    ${helper.getLanguageTypeNameForTypeReference(helper.getEnumFieldSimpleTypeReference(enumField.type, enumField.fieldName))} _constantValue = ${helper.getNullValueForTypeReference(helper.getEnumFieldSimpleTypeReference(enumField.type, enumField.fieldName))};
    _res = ${helper.getReadBufferReadMethodCall(helper.getEnumFieldSimpleTypeReference(enumField.type, enumField.fieldName), "&_constantValue", enumField)};
    if(_res != OK) {
      return _res;
    }
    ${enumField.name} = ${helper.getLanguageTypeNameForTypeReference(enumField.type)}_get_first_enum_for_field_${helper.camelCaseToSnakeCase(enumField.fieldName)}(_constantValue);
  }
  (*_message)-><@fieldName baseType=baseType field=enumField/> = ${enumField.name};
            <#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)
  ${helper.getLanguageTypeNameForField(field)} ${implicitField.name} = ${helper.getNullValueForTypeReference(implicitField.type)};
  _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&" + implicitField.name, implicitField)};
  if(_res != OK) {
    return _res;
  }
            <#break>
        <#case "manualArray">
            <#assign manualArrayField = field.asManualArrayField().orElseThrow()>

  <#-- TODO: Implement -->
            <#break>
        <#case "manual">
            <#assign manualField = field.asManualField().orElseThrow()>

  // Manual Field (${manualField.name})<#-- check if complex -->
  ${helper.getLanguageTypeNameForField(field)} ${manualField.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(baseType, manualField, manualField.parseExpression, parserArguments)});
  (*_message)-><@fieldName baseType=baseType field=manualField/> = ${manualField.name};
        <#break>
        <#case "optional">
            <#assign optionalField = field.asOptionalField().orElseThrow()>

  // Optional Field (${optionalField.name}) (Can be skipped, if a given expression evaluates to false)
  ${helper.getLanguageTypeNameForField(field)}* ${optionalField.name} = NULL;
            <#if optionalField.conditionExpression.present>
  if(${helper.toParseExpression(baseType, field, optionalField.conditionExpression.get(), parserArguments)}) {
            </#if>
        <#if optionalField.type.isSimpleTypeReference()>
            <#assign simpleTypeReference = optionalField.type.asSimpleTypeReference().orElseThrow()>
    ${optionalField.name} = malloc(sizeof(${helper.getLanguageTypeNameForField(field)}));
    if(${optionalField.name} == NULL) {
      return NO_MEMORY;
    }
    *${optionalField.name} = ${helper.getNullValueForTypeReference(optionalField.type)};
    _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, optionalField.name, optionalField)};
        <#else>
            <#assign nonSimpleTypeReference = optionalField.type.asNonSimpleTypeReference().orElseThrow()>
    _res = ${helper.getCTypeName(nonSimpleTypeReference.name)}_parse(readBuffer<#if optionalField.type.isNonSimpleTypeReference() && optionalField.type.asNonSimpleTypeReference().get().getParams().isPresent()>, <#list optionalField.type.asNonSimpleTypeReference().get().getParams().get() as parserTerm>${helper.toParseExpression(baseType, optionalField, parserTerm, parserArguments)}<#sep>, </#sep></#list></#if>, &${optionalField.name});
        </#if>
    if(_res != OK) {
      return _res;
    }
    (*_message)-><@fieldName baseType=baseType field=optionalField/> = ${optionalField.name};
            <#if optionalField.conditionExpression.present>
  } else {
    (*_message)-><@fieldName baseType=baseType field=optionalField/> = NULL;
  }
            </#if>
            <#break>
        <#case "padding">
            <#assign paddingField = field.asPaddingField().orElseThrow()>
            <#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>

  // Padding Field (padding)
  {
    int _timesPadding = (int) ((plc4c_spi_read_has_more(readBuffer, ${helper.getNumBits(simpleTypeReference)})) && (${helper.toParseExpression(baseType, paddingField, paddingField.paddingCondition, parserArguments)}));
    while (_timesPadding-- > 0) {
      // Just read the padding data and ignore it
      ${helper.getLanguageTypeNameForField(field)} _paddingValue = ${helper.getNullValueForTypeReference(simpleTypeReference)};
      _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&_paddingValue", paddingField)};
      if(_res != OK) {
        return _res;
      }
    }
  }
            <#break>
        <#case "reserved">
            <#assign reservedField = field.asReservedField().orElseThrow()>
            <#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>

  // Reserved Field (Compartmentalized so the "reserved" variable can't leak)
  {
    ${helper.getLanguageTypeNameForField(field)} _reserved = ${helper.getNullValueForTypeReference(simpleTypeReference)};
    _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&_reserved", reservedField)};
    if(_res != OK) {
      return _res;
    }
    if(_reserved != ${reservedField.referenceValue}) {
      printf("Expected constant value '%d' but got '%d' for reserved field.", ${reservedField.referenceValue}, _reserved);
    }
  }
            <#break>
        <#case "simple">
            <#assign simpleField = field.asSimpleField().orElseThrow()>

  // Simple Field (${simpleField.name})
            <#if simpleField.type.isSimpleTypeReference()>
                <#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>

            <#-- Inizialize a local variable with the simple type (Intentionally keeping the java-style names so they can be used in expressions) -->
  ${helper.getLanguageTypeNameForField(field)} ${simpleField.name} = ${helper.getNullValueForTypeReference(simpleTypeReference)};
  _res = ${helper.getReadBufferReadMethodCall(simpleTypeReference, "&" + simpleField.name, simpleField)};
            <#elseif simpleField.type.isEnumTypeReference()>
                <#assign enumTypeReference = simpleField.type.asEnumTypeReference().orElseThrow()>
            <#-- Inizialize a local variable with the complex type (Intentionally keeping the java-style names so they can be used in expressions) -->
  ${helper.getLanguageTypeNameForField(field)} ${simpleField.name};
  _res = ${helper.getCTypeName(enumTypeReference.name)}_parse(readBuffer, (void*) &${simpleField.name});
            <#else>
                <#assign nonSimpleTypeReference = simpleField.type.asNonSimpleTypeReference().orElseThrow()>
            <#-- Inizialize a local variable with the complex type (Intentionally keeping the java-style names so they can be used in expressions) -->
  ${helper.getLanguageTypeNameForField(field)}* ${simpleField.name};
  _res = ${helper.getCTypeName(nonSimpleTypeReference.name)}_parse(readBuffer<#if simpleField.type.isNonSimpleTypeReference() && simpleField.type.asNonSimpleTypeReference().get().getParams().isPresent()>, <#list simpleField.type.asNonSimpleTypeReference().get().getParams().get() as parserTerm>${helper.toParseExpression(baseType, simpleField, parserTerm, parserArguments)}<#sep>, </#sep></#list></#if>, (void*) &${simpleField.name});
            </#if>
  if(_res != OK) {
    return _res;
  }
  (*_message)-><@fieldName baseType=baseType field=simpleField/> = ${simpleField.name};
            <#break>
        <#case "switch">
            <#assign switchField = field.asSwitchField().orElseThrow()>

    // Switch Field (Depending on the discriminator values, passes the instantiation to a sub-type)
        <#list switchField.cases as case>
            <@compress single_line=true>
                <#if case.discriminatorValueTerms?has_content>
                    if(
                    <#list case.discriminatorValueTerms as discriminatorValueTerm>
                        <#if case.discriminatorValueTerms?size &gt; 1>(</#if>
                        <#assign discriminatorExpression=switchField.discriminatorExpressions[discriminatorValueTerm?index].asLiteral().orElseThrow().asVariableLiteral().orElseThrow()>
                        <#assign discriminatorType=helper.getDiscriminatorTypes()[discriminatorExpression.discriminatorName]>
                        <#if discriminatorType.isNonSimpleTypeReference()><#assign typeDefinition=discriminatorType.asNonSimpleTypeReference().orElseThrow().typeDefinition></#if>
                        ${helper.toVariableParseExpression(baseType, switchField, discriminatorExpression, parserArguments)} ==
                        <#if discriminatorType.isEnumTypeReference()>
                            ${helper.getLanguageTypeNameForTypeReference(discriminatorType)}_${helper.toParseExpression(typeDefinition, switchField, discriminatorValueTerm, parserArguments)?replace("plc4c_spi_evaluation_helper_", "")?upper_case}
                        <#else>
                            ${helper.toParseExpression(typeDefinition, switchField, discriminatorValueTerm, parserArguments)}
                        </#if>
                        <#if case.discriminatorValueTerms?size &gt; 1>)</#if>
                        <#sep> && </#sep>
                    </#list>)
                </#if>
                { /* ${case.name} */
            </@compress>

<#if baseType.isDiscriminatedChildTypeDefinition()>
    <#assign discriminatedChildType = type.asDiscriminatedComplexTypeDefinition().orElseThrow()>
    // Accessors for discriminator values.
    <#list discriminatedChildType.getDiscriminatorMap() as discriminatorName, dicriminatorValue>
        ${discriminatorName}
    </#list>
</#if>
    (*_message)->_type = ${helper.getCTypeName(type.name)}_type_${helper.getCTypeName(case.name)};
                <#list case.fields as caseField>
                    <@fieldParser baseType=case field=caseField indentContent=true/>
                    <#sep >

                </#list>
  }<#sep> else </#sep>
            </#list>
            <#break>
        <#case "virtual">
            <#assign virtualField = field.asVirtualField().orElseThrow()>

  // Virtual field (Just declare a local variable, so we can access it in the parser)
  ${helper.getLanguageTypeNameForField(field)} ${virtualField.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toParseExpression(baseType, virtualField, virtualField.valueExpression, parserArguments)});
            <#break>
    </#switch>
</#macro>
<#list type.fields as field>
<@fieldParser baseType=type field=field indentContent=false/>
</#list>

  return OK;
}

plc4c_return_code ${helper.getCTypeName(type.name)}_serialize(plc4c_spi_write_buffer* writeBuffer, ${helper.getCTypeName(type.name)}* _message<#--if helper.getSerializerArguments(parserArguments)?has_content>, <#list helper.getSerializerArguments(parserArguments) as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type)} ${parserArgument.name}<#sep>, </#sep></#list></#if-->) {
  plc4c_return_code _res = OK;
<#macro fieldSerializer baseType field indentContent>
    <#-- @ftlvariable name="baseType" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
    <#-- @ftlvariable name="field" type="org.apache.plc4x.plugins.codegenerator.types.fields.Field" -->
    <#-- @ftlvariable name="indentContent" type="boolean" -->
    <#-- TODO: the code below implies that parserArguments will be null if not present... not pretty  -->
    <#if baseType.parserArguments.isPresent()><#assign parserArguments=baseType.parserArguments.orElseThrow()></#if>
    <#switch field.typeName>
        <#case "array">
            <#assign arrayField = field.asArrayField().orElseThrow()>
            <#assign elementTypeReference = arrayField.type.elementTypeReference>

  // Array field (${arrayField.name})
  {
    uint8_t itemCount = plc4c_utils_list_size(_message-><@fieldName baseType=baseType field=arrayField/>);
    for(int curItem = 0; curItem < itemCount; curItem++) {
            <#-- When parsing simple types, there is nothing that could require the "lastItem" -->
<#if !elementTypeReference.isSimpleTypeReference()>      bool lastItem = curItem == (itemCount - 1);</#if>
      ${helper.getLanguageTypeNameForTypeReference(elementTypeReference)}* _value = (${helper.getLanguageTypeNameForTypeReference(elementTypeReference)}*) plc4c_utils_list_get_value(_message-><@fieldName baseType=baseType field=arrayField/>, curItem);
            <#if elementTypeReference.isSimpleTypeReference()>
      ${helper.getWriteBufferWriteMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "*_value", arrayField)};
            <#else>
      _res = ${helper.getCTypeName(elementTypeReference.asNonSimpleTypeReference().orElseThrow().name)}_serialize(writeBuffer, (void*) _value<#--#if helper.getSerializerTerms(field.params.orElse(null))?has_content>, <#list helper.getSerializerTerms(field.params.orElse(null)) as serializerTerm>${helper.toSerializationExpression(baseType, field, serializerTerm, parserArguments)}<#sep>, </#sep></#list></#if-->);
      if(_res != OK) {
        return _res;
      }
            </#if>
    }
  }
            <#break>
        <#case "checksum">
            <#assign checksumField = field.asChecksumField().orElseThrow()>
            <#assign simpleTypeReference = checksumField.type.asSimpleTypeReference().orElseThrow()>

  // Checksum Field (${checksumField.name})
  {
    // Create an array of all the bytes read in this message element so far.
    ${helper.getLanguageTypeNameForField(field)} _checksum = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(baseType, checksumField, checksumField.checksumExpression, parserArguments)});
    ${helper.getWriteBufferWriteMethodCall(simpleTypeReference, "_checksum", checksumField)};
  }
            <#break>
        <#case "const">
            <#assign constField = field.asConstField().orElseThrow()>
            <#assign simpleTypeReference = constField.type.asSimpleTypeReference().orElseThrow()>

  // Const Field (${constField.name})
  ${helper.getWriteBufferWriteMethodCall(simpleTypeReference, helper.getCTypeName(baseType.name)?upper_case + "_" + helper.camelCaseToSnakeCase(constField.name)?upper_case + "()", constField)};
            <#break>
        <#case "discriminator">
            <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
        <#if discriminatorField.type.isEnumTypeReference()>
            <#assign enumTypeReference = discriminatorField.type.asEnumTypeReference().orElseThrow()>

  // Enumerated Discriminator Field (${discriminatorField.name})
  ${helper.getLanguageTypeNameForTypeReference(enumTypeReference)} _${discriminatorField.name} = ${helper.getCTypeName(baseType.name) + "_get_discriminator(_message->_type)." + discriminatorField.name};
  _res = ${helper.getCTypeName(enumTypeReference.name)}_serialize(writeBuffer, &_${discriminatorField.name});
  if(_res != OK) {
    return _res;
  }
        <#else>
            <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>

  // Discriminator Field (${discriminatorField.name})
  ${helper.getWriteBufferWriteMethodCall(simpleTypeReference, helper.getCTypeName(baseType.name) + "_get_discriminator(_message->_type)." + discriminatorField.name, discriminatorField)};
        </#if>
            <#break>
        <#case "enum">
            <#assign enumField = field.asEnumField().orElseThrow()>
            <#assign enumTypeReference = enumField.type.asEnumTypeReference().orElseThrow()>

  // Enum field (${enumField.name})
  _res = ${helper.getWriteBufferWriteMethodCall(helper.getEnumFieldSimpleTypeReference(enumTypeReference, enumField.fieldName), helper.getLanguageTypeNameForTypeReference(enumField.type) + "_get_" + helper.camelCaseToSnakeCase(enumField.fieldName) + "(_message->" + helper.getFieldName(baseType, enumField) + ")", enumField)};
  if(_res != OK) {
    return _res;
  }
            <#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)
  _res = ${helper.getWriteBufferWriteMethodCall(simpleTypeReference, helper.toSerializationExpression(baseType, implicitField, implicitField.serializeExpression, parserArguments), implicitField)};
  if(_res != OK) {
    return _res;
  }
            <#break>
        <#case "manualArray">
            <#assign manualArrayField = field>

            <#-- TODO: Implement -->
            <#break>
        <#case "manual">
            <#assign manualField = field.asManualField().orElseThrow()>

  // Manual Field (${manualField.name})  {
  _res = ${helper.toSerializationExpression(baseType, manualField, manualField.serializeExpression, parserArguments)};
  if(_res != OK) {
    return _res;
  }
            <#break>
        <#case "optional">
            <#assign optionalField = field.asOptionalField().orElseThrow()>

  // Optional Field (${optionalField.name})
  if(_message-><@fieldName baseType=baseType field=optionalField/> != NULL) {
            <#if optionalField.type.isSimpleTypeReference()>
    _res = ${helper.getWriteBufferWriteMethodCall(optionalField.type.asSimpleTypeReference().orElseThrow(), "*_message->" + helper.getFieldName(baseType, optionalField), optionalField)};
            <#else>
    _res = ${helper.getCTypeName(optionalField.type.asNonSimpleTypeReference().orElseThrow().name)}_serialize(writeBuffer<#if optionalField.type.isDataIoTypeReference() && optionalField.type.asNonSimpleTypeReference().get().getParams().isPresent()>, <#list optionalField.type.asNonSimpleTypeReference().get().getParams().get() as parserTerm>${helper.toSerializationExpression(baseType, optionalField, parserTerm, parserArguments)}<#sep>, </#sep></#list></#if>, <#if optionalField.type.isEnumTypeReference() || optionalField.type.isDataIoTypeReference()>&</#if>_message-><@fieldName baseType=baseType field=optionalField/>);
            </#if>
    if(_res != OK) {
      return _res;
    }
  }
            <#break>
        <#case "padding">
            <#assign paddingField = field.asPaddingField().orElseThrow()>
            <#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>

  // Padding Field (padding)
  {
    <#-- We're replacing the "lastItem" with 'false' here as the item itself can't know if it is the last -->
    int _timesPadding = (int) (${helper.toSerializationExpression(baseType, paddingField, paddingField.paddingCondition, parserArguments)});
    while (_timesPadding-- > 0) {
      // Just output the default padding data
      _res = ${helper.getWriteBufferWriteMethodCall(simpleTypeReference, helper.toParseExpression(baseType, paddingField, paddingField.paddingValue, parserArguments), paddingField)};
      if(_res != OK) {
        return _res;
      }
    }
  }
            <#break>
        <#case "reserved">
            <#assign reservedField = field.asReservedField().orElseThrow()>
            <#assign simpleTypeReference = reservedField.type.asSimpleTypeReference().orElseThrow()>

  // Reserved Field
  _res = ${helper.getWriteBufferWriteMethodCall(simpleTypeReference, reservedField.referenceValue, reservedField)};
  if(_res != OK) {
    return _res;
  }
            <#break>
        <#case "simple">
            <#assign simpleField = field.asSimpleField().orElseThrow()>

  // Simple Field (${simpleField.name})
            <#if simpleField.type.isSimpleTypeReference()>
                <#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>
  _res = ${helper.getWriteBufferWriteMethodCall(simpleTypeReference, "_message->" + helper.getFieldName(baseType, simpleField), simpleField)};
            <#else>
                <#assign nonSimpleTypeRererence = simpleField.type.asNonSimpleTypeReference().orElseThrow()>
  _res = ${helper.getCTypeName(nonSimpleTypeRererence.name)}_serialize(writeBuffer<#if simpleField.type.isDataIoTypeReference() && simpleField.type.asNonSimpleTypeReference().get().getParams().isPresent()>, <#list simpleField.type.asNonSimpleTypeReference().get().getParams().get() as parserTerm>${helper.toSerializationExpression(baseType, simpleField, parserTerm, parserArguments)}<#sep>, </#sep></#list></#if>, <#if simpleField.type.isEnumTypeReference() || simpleField.type.isDataIoTypeReference()>&</#if>_message-><@fieldName baseType=baseType field=simpleField/>);
            </#if>
  if(_res != OK) {
    return _res;
  }
            <#break>
        <#case "switch">
            <#assign switchField = field.asSwitchField().orElseThrow()>

  // Switch Field (Depending on the current type, serialize the subtype elements)
  switch(_message->_type) {
        <#list switchField.cases as case>
    case ${helper.getCTypeName(type.name)}_type_${helper.getCTypeName(case.name)}: {
            <#list case.fields as caseField>
                <@fieldSerializer baseType=case field=caseField indentContent=true/>
            </#list>

      break;
    }
        </#list>
  }
            <#break>
    </#switch>
</#macro>
<#list type.fields as field>
  <@fieldSerializer baseType=type field=field indentContent=false/>
</#list>

  return OK;
}

uint16_t ${helper.getCTypeName(type.name)}_length_in_bytes(${helper.getCTypeName(type.name)}* _message) {
  return ${helper.getCTypeName(type.name)}_length_in_bits(_message) / 8;
}

uint16_t ${helper.getCTypeName(type.name)}_length_in_bits(${helper.getCTypeName(type.name)}* _message) {
    <#macro fieldSize baseType field indentContent>
        <#-- @ftlvariable name="baseType" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
        <#-- @ftlvariable name="field" type="org.apache.plc4x.plugins.codegenerator.types.fields.Field" -->
        <#-- @ftlvariable name="indentContent" type="boolean" -->
        <#switch field.typeName>
            <#case "array">
                <#assign arrayField = field.asArrayField().orElseThrow()>
                <#assign elementTypeReference = arrayField.type.elementTypeReference>

  // Array field
                <#if elementTypeReference.isSimpleTypeReference()>
                    <#assign simpleTypeReference = elementTypeReference.asSimpleTypeReference().orElseThrow()>
  lengthInBits += ${simpleTypeReference.sizeInBits} * plc4c_utils_list_size(_message-><@fieldName baseType=baseType field=arrayField/>);
                <#else>
  if(_message-><@fieldName baseType=baseType field=arrayField/> != NULL) {
    plc4c_list_element* curElement = _message-><@fieldName baseType=baseType field=arrayField/>->tail;
    while (curElement != NULL) {
      lengthInBits += ${helper.getLengthInBitsFunctionNameForComplexTypedField(arrayField)}((${helper.getLanguageTypeNameForTypeReference(elementTypeReference)}*) curElement->value);
      curElement = curElement->next;
    }
  }
                </#if>
                <#break>
            <#case "checksum">
                <#assign checksumField = field.asChecksumField().orElseThrow()>
                <#assign simpleTypeReference = checksumField.type>

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

  // Const Field (${constField.name})
  lengthInBits += ${simpleTypeReference.sizeInBits};
                <#break>
            <#case "discriminator">
                <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>

  // Discriminator Field (${discriminatorField.name})
        <#if discriminatorField.type.isSimpleTypeReference()>
            <#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>
            <#if simpleTypeReference.isVstringTypeReference()>
                <#assign vstringTypeReference = simpleTypeReference.asVstringTypeReference().orElseThrow()>
  lengthInBits +=  ${helper.toParseExpression(type, discriminatorField, vstringTypeReference.lengthExpression, parserArguments)};
            <#else>
  lengthInBits += ${simpleTypeReference.sizeInBits};
            </#if>
        <#elseif helper.isEnumField(field)>
            <#assign enumTypeReference = discriminatorField.type.asEnumTypeReference().orElseThrow()>
  lengthInBits += ${enumTypeReference.baseTypeReference.orElseThrow().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()>

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

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

  // Optional Field (${optionalField.name})
  if(_message-><@fieldName baseType=baseType field=optionalField/> != NULL) {
                <#if optionalField.type.isSimpleTypeReference()>
                    <#assign simpleTypeReference = optionalField.type.asSimpleTypeReference().orElseThrow()>
    lengthInBits += ${simpleTypeReference.sizeInBits};
                <#else>
    lengthInBits += ${helper.getLengthInBitsFunctionNameForComplexTypedField(optionalField)}(_message-><@fieldName baseType=baseType field=optionalField/><#if optionalField.type.isDataIoTypeReference() && optionalField.type.asNonSimpleTypeReference().get().getParams().isPresent()>, <#list optionalField.type.asNonSimpleTypeReference().get().getParams().get() as parserTerm>${helper.toSerializationExpression(baseType, optionalField, parserTerm, parserArguments)}<#sep>, </#sep></#list></#if>);
                </#if>
  }
                <#break>
            <#case "padding">
                <#assign paddingField = field.asPaddingField().orElseThrow()>
                <#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>

  // Padding Field (padding)
            <#-- We're replacing the "lastItem" with 'false' here as the item itself can't know if it is the last -->
  int _needsPadding = (int) (${helper.toSerializationExpression(type, paddingField, paddingField.paddingCondition, parserArguments)?replace("lastItem", "false")});
  while(_needsPadding-- > 0) {
    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()>
                    <#assign simpleTypeReference = simpleField.type.asSimpleTypeReference().orElseThrow()>
                    <#if simpleTypeReference.isVstringTypeReference()>
                        <#assign vstringTypeReference = simpleTypeReference.asVstringTypeReference().orElseThrow()>
  lengthInBits +=  ${helper.toSerializationExpression(type, simpleField, vstringTypeReference.lengthExpression, parserArguments)};
                    <#else>
  lengthInBits += ${simpleTypeReference.sizeInBits};
                    </#if>
                <#else>
  lengthInBits += ${helper.getLengthInBitsFunctionNameForComplexTypedField(simpleField)}(<#if simpleField.type.isEnumTypeReference()>&</#if>_message-><@fieldName baseType=baseType field=simpleField/>);
                </#if>
                <#break>
            <#case "switch">
                <#assign switchField = field.asSwitchField().orElseThrow()>

  // Depending on the current type, add the length of sub-type elements ...
  switch(_message->_type) {
            <#list switchField.cases as case>
    case ${helper.getCTypeName(type.name)}_type_${helper.getCTypeName(case.name)}: {
                <#list case.fields as caseField>
                    <@fieldSize baseType=case field=caseField indentContent=true/>
                    <#sep >

                </#list>

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

  // A virtual field doesn't have any in- or output.
                <#break>
        </#switch>
    </#macro>
  uint16_t lengthInBits = 0;
<#list type.fields as field>
  <@fieldSize baseType=type field=field indentContent=false/>
</#list>

  return lengthInBits;
}

</#if>
</#outputformat>