<#--
  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.DataIoTypeDefinition" -->
${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.getRequiredImportsForDataIo()?has_content>
		<#list helper.getRequiredImportsForDataIo() as import>
	${import}
		</#list>
	</#if>
)

	${sectionContent}
</#macro>

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

<@importSectionWithContentBelow><@emitImport import="github.com/apache/plc4x/plc4go/spi/utils" /><@emitImport import="github.com/apache/plc4x/plc4go/spi/values" /><@emitImportWithAlias alias="api" import="github.com/apache/plc4x/plc4go/pkg/api/values" />
// 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>
func ${type.name}Parse(readBuffer utils.ReadBuffer<#if parserArguments?has_content>, <#list parserArguments as parserArgument>${parserArgument.name} <#if parserArgument.type.isNonSimpleTypeReference() && !parserArgument.type.isEnumTypeReference()>I</#if>${helper.getLanguageTypeNameForTypeReference(parserArgument.type)}<#sep>, </#sep></#list></#if>) (api.PlcValue, error) {
	readBuffer.PullContext("${type.name}")
	switch {
	<#list type.switchField.orElseThrow().cases as case>
		<@compress single_line=true>
        <#if case.discriminatorValueTerms?has_content>
			case
			<#list case.discriminatorValueTerms as discriminatorValueTerm>
				${helper.toParseExpression(null, null, type.switchField.orElseThrow().discriminatorExpressions[discriminatorValueTerm?index], parserArguments)} ==
				<#if parserArguments[discriminatorValueTerm?index].type.isNonSimpleTypeReference()>
					<#if parserArguments[discriminatorValueTerm?index].type.isEnumTypeReference()>
                        <#assign enumType=helper.getLanguageTypeNameForTypeReference(parserArguments[discriminatorValueTerm?index].type)>
                        <#assign enumTypeWithoutTraces=tracer.removeTraces(enumType)>
                        <#assign enumTypeTraces=tracer.extractTraces(enumType)>
                        <#assign enumValue=helper.toParseExpression(null, parserArguments[discriminatorValueTerm?index].type, discriminatorValueTerm, parserArguments)>
                        <#assign enumValueWithoutTraces=tracer.removeTraces(enumValue)>
                        <#assign enumValueTraces=tracer.extractTraces(enumValue)>
                        ${enumTypeTraces}${enumValueTraces}${enumTypeWithoutTraces}_${enumValueWithoutTraces?replace("Get","")?replace("()","")}
					<#else>
						${helper.toParseExpression(null, parserArguments[discriminatorValueTerm?index].type, discriminatorValueTerm, parserArguments)}
					</#if>
				<#else>
					${helper.toParseExpression(null, parserArguments[discriminatorValueTerm?index].type, discriminatorValueTerm, parserArguments)}
				</#if>
				<#sep> && </#sep>
			</#list>
		<#else>
			default
		</#if>: // ${case.name}
		</@compress>
		<#assign valueDefined=false>
		<#if case.name == "Struct">

			// Struct
			_map := map[string]api.PlcValue{}
			<#assign valueDefined=true>
		</#if>
		<#list case.fields as field>
			<#switch field.typeName>
				<#case "array">
					<#assign arrayField = field.asArrayField().orElseThrow()>
                    <#assign arrayElementType = arrayField.type.elementTypeReference>

			// Array Field (${arrayField.name})
			var ${arrayField.name} []api.PlcValue
			for i := 0; i < int(${helper.toParseExpression(null, null, arrayField.loopExpression, parserArguments)}); i++ {
				_item, _itemErr := <#if arrayElementType.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(arrayField.name, arrayElementType.asSimpleTypeReference().orElseThrow(), arrayField)}<#else>Complex type array in data-io parsing currently not implemented</#if>
				if _itemErr != nil {
					return nil, errors.Wrap(_itemErr, "Error parsing 'value' field")<@emitImport import="github.com/pkg/errors" />
				}
				${arrayField.name} = append(${arrayField.name}, ${helper.getPlcValueTypeForTypeReference(arrayElementType)}(_item))
			}
					<#if arrayField.name == "value">
						<#assign valueDefined=true>
					</#if>
					<#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")<@emitImport import="github.com/pkg/errors" />
			}
					<#if manualField.name == "value">
						<#assign valueDefined=true>
					</#if>
					<#break>
				<#case "reserved">
					<#assign reservedField = field.asReservedField().orElseThrow()>

			// Reserved Field (Just skip the bytes)
			if _, _err := ${helper.getReadBufferReadMethodCall("reserved", reservedField.type.asSimpleTypeReference().orElseThrow(), reservedField)}; _err != nil {
				return nil, errors.Wrap(_err, "Error parsing reserved field")<@emitImport import="github.com/pkg/errors" />
			}
					<#break>
				<#case "simple">
					<#assign simpleField = field.asSimpleField().orElseThrow()>

			// Simple Field (${simpleField.name})
			<#if case.name == "Struct" || ((case.name == "DATE_AND_TIME") && ((simpleField.name == "year") || (simpleField.name == "month") || (simpleField.name == "day") || (simpleField.name == "hour") || (simpleField.name == "minutes") || (simpleField.name == "seconds") || (simpleField.name == "secondsSinceEpoch"))) || ((case.name == "DATE") && ((simpleField.name == "year") || (simpleField.name == "month") || (simpleField.name == "day"))) || ((case.name == "TIME_OF_DAY") && ((simpleField.name == "hour") || (simpleField.name == "minutes") || (simpleField.name == "seconds"))) || simpleField.name == "value">${simpleField.name}<#else>_</#if>, _${simpleField.name}Err := <#if simpleField.type.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(simpleField.name, simpleField.type.asSimpleTypeReference().orElseThrow(), simpleField)}<#else>${simpleField.type.asComplexTypeReference().orElseThrow().name}Parse(io<#if simpleField.type.asComplexTypeReference().orElseThrow().params.isPresent()>, <#list simpleField.type.asComplexTypeReference().orElseThrow().params.orElseThrow() as parserArgument>(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(simpleField.type, parserArgument?index))}) (${helper.toParseExpression(simpleField, helper.getArgumentType(simpleField.type, parserArgument?index), parserArgument, parserArguments)})<#sep>, </#sep></#list></#if>)</#if>
			if _${simpleField.name}Err != nil {
				return nil, errors.Wrap(_${simpleField.name}Err, "Error parsing '${simpleField.name}' field")<@emitImport import="github.com/pkg/errors" />
			}
					<#if case.name == "Struct">
			_map["${case.name}"] = ${helper.getPlcValueTypeForTypeReference(simpleField.type)}(${simpleField.name})
					</#if>
					<#assign valueDefined=true>
					<#break>
			</#switch>
		</#list>
		<#if valueDefined>
			<#switch case.name>
				<#case "TIME_OF_DAY">
					<#if helper.hasFieldsWithNames(case.fields, "hour", "minutes", "seconds")>
			value := time.Date(0,0,0, int(hour), int(minutes), int(seconds), 0, nil)<@emitImport import="time" />
					</#if>
			readBuffer.CloseContext("${type.name}")
			return values.NewPlcTIME_OF_DAY(value), nil
					<#break>
				<#case "DATE">
					<#if helper.hasFieldsWithNames(case.fields, "year", "month", "day")>
			value := time.Date(int(year), time.Month(month), int(day), 0, 0, 0, 0, nil)<@emitImport import="time" />
					</#if>
			readBuffer.CloseContext("${type.name}")
			return values.NewPlcDATE(value), nil
					<#break>
				<#case "DATE_AND_TIME">
					<#if helper.hasFieldsWithNames(case.fields, "year", "month", "day", "hour", "minutes", "seconds")>
			value := time.Date(int(year), time.Month(month), int(day), int(hour), int(minutes), int(seconds), 0, nil)<@emitImport import="time" />
                    <#elseif helper.hasFieldsWithNames(case.fields, "secondsSinceEpoch")>
            value := time.Unix(int64(secondsSinceEpoch), 0)<@emitImport import="time" />
					</#if>
			readBuffer.CloseContext("${type.name}")
			return values.NewPlcDATE_AND_TIME(value), nil
					<#break>
			readBuffer.CloseContext("${type.name}")
				<#case "Struct">
			readBuffer.CloseContext("${type.name}")
			return values.NewPlcStruct(_map), nil
					<#break>
				<#case "List">
			readBuffer.CloseContext("${type.name}")
			return values.NewPlcList(value), nil
					<#break>
				<#default>
			readBuffer.CloseContext("${type.name}")
			return values.NewPlc${case.name}(value), nil
			</#switch>
		</#if>
	</#list>
	}
    // TODO: add more info which type it is actually
	return nil, errors.New("unsupported type")<@emitImport import="github.com/pkg/errors" />
}

func ${type.name}Serialize(writeBuffer utils.WriteBuffer, value api.PlcValue<#if parserArguments?has_content>, <#list parserArguments as parserArgument>${parserArgument.name} <#if parserArgument.type.isNonSimpleTypeReference() && !parserArgument.type.isEnumTypeReference()>I</#if>${helper.getLanguageTypeNameForTypeReference(parserArgument.type)}<#sep>, </#sep></#list></#if>) error {
	<#if parserArguments?has_content>
	m := struct {
    	<#list parserArguments as parserArgument>
			${parserArgument.name?cap_first} <#if parserArgument.type.isNonSimpleTypeReference() && !parserArgument.type.isEnumTypeReference()>I</#if>${helper.getLanguageTypeNameForTypeReference(parserArgument.type)}
		</#list>
	}{
		<#list parserArguments as parserArgument>
			${parserArgument.name?cap_first}: ${parserArgument.name},
		</#list>
	}
	_ = m
	</#if>
	writeBuffer.PushContext("${type.name}")
	switch {
	<#assign defaultSet=false>
	<#list type.switchField.orElseThrow().cases as case>
		<@compress single_line=true>
        <#if case.discriminatorValueTerms?has_content>
			case
			<#list case.discriminatorValueTerms as discriminatorValueTerm>
				${helper.toParseExpression(null, null, type.switchField.orElseThrow().discriminatorExpressions[discriminatorValueTerm?index], parserArguments)} ==
				<#assign typeRef=parserArguments[discriminatorValueTerm?index].type>
				<#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, discriminatorValueTerm, parserArguments)>
                        <#assign enumValueWithoutTraces=tracer.removeTraces(enumValue)>
                        <#assign enumValueTraces=tracer.extractTraces(enumValue)>
                        ${enumTypeTraces}${enumValueTraces}${enumTypeWithoutTraces}_${enumValueWithoutTraces?replace("Get","")?replace("()","")}
					<#else>
						${helper.toParseExpression(null, typeRef, discriminatorValueTerm, parserArguments)}
					</#if>
				<#else>
					${helper.toParseExpression(null, typeRef, discriminatorValueTerm, parserArguments)}
				</#if>
				<#sep> && </#sep>
			</#list>
		<#else>
			default<#assign defaultSet=true>
		</#if>: // ${case.name}
		</@compress>
		<#list case.fields as field>
			<#switch field.typeName>
				<#case "array">
					<#assign arrayField = field.asArrayField().orElseThrow()>
                    <#assign arrayElementType = arrayField.type.elementTypeReference>

			// Array Field (${arrayField.name})
			for i := uint32(0); i < uint32(${helper.toSerializationExpression(null, null, arrayField.loopExpression, parserArguments)}); i++ {
					<#if case.name = "Struct">
				${arrayField.name} := value.GetValue("${arrayField.name}")
					</#if>
				_itemErr := <#if arrayElementType.isSimpleTypeReference()>${helper.getWriteBufferWriteMethodCall("", arrayElementType.asSimpleTypeReference().orElseThrow(), arrayField.name + ".GetIndex(i).Get" + helper.getLanguageTypeNameForTypeReference(arrayElementType)?cap_first + "()", arrayField)}<#else>Complex type array in data-io serialization currently not implemented</#if>
				if _itemErr != nil {
					return errors.Wrap(_itemErr, "Error serializing 'value' field")<@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 "reserved">
					<#assign reservedField = field.asReservedField().orElseThrow()>

			// Reserved Field (Just skip the bytes)
			if _err := ${helper.getWriteBufferWriteMethodCall("reserved", reservedField.type.asSimpleTypeReference().orElseThrow(), helper.getReservedValue(reservedField), reservedField)}; _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 _err := <#if simpleField.type.isSimpleTypeReference()>${helper.getWriteBufferWriteMethodCall(simpleField.name, simpleField.type.asSimpleTypeReference().orElseThrow(), "value.Get" + helper.getLanguageTypeNameForTypeReference(simpleField.type)?cap_first + "()", simpleField)}<#else>${simpleField.type.asComplexTypeReference().orElseThrow().name}Serialize(io, <#if simpleField.type.asComplexTypeReference().orElseThrow().params.isPresent()>, <#list simpleField.type.asComplexTypeReference().orElseThrow().params.orElseThrow() as parserArgument>(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(simpleField.type, parserArgument?index))}) (${helper.toParseExpression(simpleField, helper.getArgumentType(simpleField.type, parserArgument?index), parserArgument, parserArguments)})<#sep>, </#sep></#list></#if>)</#if>; _err != nil {
				return errors.Wrap(_err, "Error serializing '${simpleField.name}' field")<@emitImport import="github.com/pkg/errors" />
			}
					<#break>
			</#switch>
		</#list>
	</#list>
	<#if !defaultSet>
		default:
            // TODO: add more info which type it is actually
			return errors.New("unsupported type")<@emitImport import="github.com/pkg/errors" />
	</#if>
	}
	writeBuffer.PopContext("${type.name}")
	return nil
}
</@importSectionWithContentBelow>

</#outputformat>
