<#--
  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" -->
<#-- Declare the name and type of variables declared locally inside the template -->
<#-- @ftlvariable name="field" type="org.apache.plc4x.plugins.codegenerator.types.fields.Field" -->
<#if !type.isDiscriminatedChildTypeDefinition()>${helper.getIncludesDirectory()?replace(".", "/")}/${helper.camelCaseToSnakeCase(type.name)}.h
/*
 * 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.
 */

#ifndef ${helper.getCTypeName(type.name)?upper_case}_H_
#define ${helper.getCTypeName(type.name)?upper_case}_H_

#include <stdbool.h>
#include <stdint.h>
#include <plc4c/spi/read_buffer.h>
#include <plc4c/spi/write_buffer.h>
#include <plc4c/utils/list.h>
<#--
    Add any import statements for partent-types, complex types used in properties or parser arguments.
-->
<#if helper.getTypeNamesForImportStatements()?has_content>
    <#list helper.getTypeNamesForImportStatements() as typeReference>
#include "${helper.camelCaseToSnakeCase(typeReference)}.h"
    </#list>
</#if>
<#if helper.getDiscriminatorTypes()?has_content>
    <#list helper.getDiscriminatorTypes()?values as discriminatorType>
        <#if discriminatorType.isNonSimpleTypeReference()>
#include "${helper.camelCaseToSnakeCase(discriminatorType.asNonSimpleTypeReference().orElseThrow().name)}.h"
        </#if>
    </#list>
</#if>

// 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>
<#--#ifdef __cplusplus
extern "C" {
#endif-->

<#--
    Abstract types only occur as parents in the case of discriminated types.
    As C doesn't have any form of polymorph type-system, we need to save the type information in the
    data-structure. So with this enum we're doing exactly this. It will be used in the serializers and
    parsers and stored in discriminated types.
-->
<#if type.isDiscriminatedParentTypeDefinition()>

// Structure used to contain the discriminator values for discriminated types using this as a parent
struct ${helper.getCTypeName(type.name)}_discriminator {
<#if helper.getDiscriminatorTypes()?has_content>
    <#list helper.getDiscriminatorTypes()?keys as discriminatorName>
        <#if !helper.getDiscriminatorTypes()[discriminatorName].isNonSimpleTypeReference()>
  ${helper.getLanguageTypeNameForTypeReference(helper.getDiscriminatorTypes()[discriminatorName])} ${discriminatorName};
        <#else>
  enum ${helper.getLanguageTypeNameForTypeReference(helper.getDiscriminatorTypes()[discriminatorName])} ${discriminatorName};
        </#if>
    </#list>
</#if>
};
typedef struct ${helper.getCTypeName(type.name)}_discriminator ${helper.getCTypeName(type.name)}_discriminator;

// Enum assigning each subtype an individual id.
enum ${helper.getCTypeName(type.name)}_type {
    <#list type.getSubTypeDefinitions() as subtype>
  ${helper.getCTypeName(type.name)}_type_${helper.getCTypeName(subtype.name)} = ${subtype?index}<#sep>,
    </#list>
};
typedef enum ${helper.getCTypeName(type.name)}_type ${helper.getCTypeName(type.name)}_type;

// Function to get the discriminator values for a given type.
${helper.getCTypeName(type.name)}_discriminator ${helper.getCTypeName(type.name)}_get_discriminator(${helper.getCTypeName(type.name)}_type type);
</#if>
<#--
    When using const fields, output the constant reference values
    as global const values so we can use them elsewhere.
-->
<#if helper.getAllConstFields()?has_content>

// Constant values.
    <#list helper.getAllConstFields() as entry>
        <#assign constField=entry.key>
        <#assign parentType=entry.value>
${helper.getLanguageTypeNameForField(constField)} ${helper.getCTypeName(parentType.name)?upper_case}_${helper.camelCaseToSnakeCase(constField.name)?upper_case}();
    </#list>
</#if>

<#--
    Create the general data-structure for this type
-->
struct ${helper.getCTypeName(type.name)} {
<#if type.isDiscriminatedParentTypeDefinition()>
  /* This is an abstract type so this property saves the type of this typed union */
  ${helper.getCTypeName(type.name)}_type _type;
</#if>
  /* Properties */
<#list type.getPropertyAndSwitchFields() as field>
    <#if field.isSwitchField()>
        <#assign switchField=field.asSwitchField().orElseThrow()>
  union {
        <#list switchField.cases as case>
            <#if case.propertyFields?has_content>
    struct { /* ${case.name} */
                <#list case.propertyFields as caseField>
                    <#if (caseField.type.isSimpleTypeReference() || helper.isEnumField(caseField)) && !caseField.isOptionalField() && !caseField.isArrayField() && !caseField.isManualArrayField()>
      ${helper.getLanguageTypeNameForField(caseField)} ${helper.camelCaseToSnakeCase(case.name)}_${helper.camelCaseToSnakeCase(caseField.name)}${helper.getTypeSizeForField(caseField)}<#if field.loopType??>${helper.getLoopExpressionSuffix(caseField)}</#if>;
                    <#elseif caseField.isArrayField() || caseField.isManualArrayField()>
      plc4c_list* ${helper.camelCaseToSnakeCase(case.name)}_${helper.camelCaseToSnakeCase(caseField.name)};
                    <#else>
      ${helper.getLanguageTypeNameForField(caseField)}* ${helper.camelCaseToSnakeCase(case.name)}_${helper.camelCaseToSnakeCase(caseField.name)};
                    </#if>
                </#list>
    };
            </#if>
        </#list>
  };
    <#elseif (field.isTypedField() && field.asTypedField().orElseThrow().type.isSimpleTypeReference() || helper.isEnumField(field)) && !field.isOptionalField() && !field.isArrayField() && !field.isManualArrayField()>
        <#assign namedField = field.asNamedField().orElseThrow()>
  ${helper.getLanguageTypeNameForField(field)} ${helper.camelCaseToSnakeCase(namedField.name)}${helper.getTypeSizeForField(field.asTypedField().orElseThrow())}<#if field.loopType??>${helper.getLoopExpressionSuffix(field.asTypedField().orElseThrow())}</#if>;
    <#elseif field.isArrayField() || field.isManualArrayField()>
        <#assign namedField = field.asNamedField().orElseThrow()>
  plc4c_list* ${helper.camelCaseToSnakeCase(namedField.name)};
    <#else>
        <#assign namedField = field.asNamedField().orElseThrow()>
  ${helper.getLanguageTypeNameForField(field)}* ${helper.camelCaseToSnakeCase(namedField.name)};
    </#if>
</#list>
};
typedef struct ${helper.getCTypeName(type.name)} ${helper.getCTypeName(type.name)};

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

<#--
    Define the parse-method for elements of this tpye
-->
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);

<#--
    Define the serialize-method for elements of this tpye
-->
plc4c_return_code ${helper.getCTypeName(type.name)}_serialize(plc4c_spi_write_buffer* writeBuffer, ${helper.getCTypeName(type.name)}* message);

uint16_t ${helper.getCTypeName(type.name)}_length_in_bytes(${helper.getCTypeName(type.name)}* message);

uint16_t ${helper.getCTypeName(type.name)}_length_in_bits(${helper.getCTypeName(type.name)}* message);

<#--#ifdef __cplusplus
}
#endif-->
#endif  // ${helper.getCTypeName(type.name)?upper_case}_H_
</#if>
</#outputformat>