<#macro addNullCheck fieldType indent>
	<#switch fieldType.flag>
		<#case 'MANDATORY'>
			<#lt/>${indent}.map(java.util.Objects::requireNonNull)
			<@addNullCheck fieldType=fieldType.nested indent=indent/>
		<#break/>
		<#case 'LIST'>
			<#lt/>${indent}.filter(java.util.Objects::nonNull)
			<#lt/>${indent}.flatMap(java.util.Collection::stream)
			<@addNullCheck fieldType=fieldType.nested indent=indent/>
		<#break/>
		<#default>
			<#lt/>${indent}.forEach($ -> {});
		<#break/>
	</#switch>
</#macro>

<#function getFieldType field>
	<#if field.targetTypeName?has_content>
		<#return field.type.getCustom(field.targetTypeName) />
	<#else>
		<#return field.type.full />
	</#if>
</#function>

<#function getFieldName field>
	<#if field.alias?has_content>
		<#return field.alias />
	<#else>
		<#return field.name />
	</#if>
</#function>

<#macro addField field indent>
	<#if jsonProperty?has_content>
		<#lt/>${indent}@${jsonProperty}("${getFieldName(field)}")
	</#if>
	<#lt/>${indent}private ${getFieldType(field)} ${propertyPrefix!}${getFieldName(field)}${propertySuffix!} = null;
</#macro>

<#macro addGetter field indent>
	<@addJavadoc javadoc=field.javadoc indent=indent/>
	<#lt/>${indent}public ${getFieldType(field)} get${getFieldName(field)?cap_first}() {
	<#lt/>${indent}	return this.${propertyPrefix!}${getFieldName(field)}${propertySuffix!};
	<#lt/>${indent}}
</#macro>

<#macro addSetter className field indent>
	<@addJavadoc javadoc=field.javadoc indent=indent/>
	<#lt/>${indent}public ${dtoMethodChaining?then(className,"void")} set${getFieldName(field)?cap_first}(${getFieldType(field)} v) {
	<#lt/>${indent}	java.util.stream.Stream.of(v)
	<@addNullCheck fieldType=field.type indent=indent+'\t\t'/>
	<#lt/>${indent}	this.${propertyPrefix!}${getFieldName(field)}${propertySuffix!} = v;
	<#if dtoMethodChaining>
		<#lt/>${indent}	return this;
	</#if>
	<#lt/>${indent}}
</#macro>

<#macro addFields className fields indent>
	<#list fields as field>
		<@addField field indent/>
		<@addGetter field indent/>
		<#if dtoSetters>
			<@addSetter className field indent/>
		</#if>
	</#list>
</#macro>

<#macro addToBuilderMethod builderClassName fields indent>
	<#lt/>${indent}public ${builderClassName} toBuilder() {
	<#lt/>${indent}	${builderClassName} builder = new ${builderClassName}();
	<#list fields as field>
		<#lt/>${indent}	builder.${propertyPrefix!}${getFieldName(field)}${propertySuffix!} = this.${propertyPrefix!}${getFieldName(field)}${propertySuffix!};
	</#list>
	<#lt/>${indent}	return builder;
	<#lt/>${indent}}
</#macro>

<#macro addBuilderFields className fields indent>
	<#list fields as field>
		<@addField field indent/>
		<@addSetter className field indent/>
	</#list>
</#macro>

<#macro asLiteralArgument fieldType indent>
	<#switch fieldType.flag>
		<#case 'MANDATORY'>
			<#lt/>${indent}.map(java.util.Objects::requireNonNull)
			<@asLiteralArgument fieldType=fieldType.nested indent=indent/>
		<#break/>
		<#case 'LIST'>
			<#lt/>${indent}.flatMap(${operationsPackage}.GraphQlAppender.arrayAppender(documentBuilder))
			<@asLiteralArgument fieldType=fieldType.nested indent=indent/>
		<#break/>
		<#default>
			<#lt/>${indent}.forEach(${operationsPackage}.GraphQlAppender.valueAppender(documentBuilder));
		<#break/>
	</#switch>
</#macro>

<#macro addSelectionField className field indent>
	<@addJavadoc javadoc=field.javadoc indent=indent/>
	<#lt/>${indent}public ${className} get${field.name?cap_first}(<#if field.arguments?has_content
	>java.util.function.Consumer<${field.title?cap_first}Arguments> arguments<#if field.targetTypeName?has_content>, </#if></#if><#if field.targetTypeName?has_content
	>java.util.function.Consumer<${field.targetTypeName}Selector> selector</#if>) {
	<#if field.alias?has_content>
		<#lt/>${indent}	documentBuilder.append(" ${field.alias}:");
	</#if>
	<#lt/>${indent}	documentBuilder.append(" ${field.name}");
	<#if field.arguments?has_content>
		<#lt/>${indent}	if (arguments != null) {
		<#lt/>${indent}		documentBuilder.append(" (");
		<#lt/>${indent}		arguments.accept(new ${field.title?cap_first}Arguments());
		<#lt/>${indent}		documentBuilder.append(" )");
		<#lt/>${indent}	}
	</#if>
	<#if field.targetTypeName?has_content>
		<#lt/>${indent}	if (selector != null) {
		<#lt/>${indent}		documentBuilder.append(" {");
		<#lt/>${indent}		selector.accept(new ${field.targetTypeName}Selector(documentBuilder));
		<#lt/>${indent}		documentBuilder.append(" }");
		<#lt/>${indent}	}
	</#if>
	<#lt/>${indent}	return this;
	<#lt/>${indent}}
</#macro>

<#macro addSelectionFields className fields indent>
	<#list fields as field>
		<#if field.arguments?has_content>
			<#lt/>${indent}public class ${field.title?cap_first}Arguments {
			<#list field.arguments as argument>
				<@addJavadoc javadoc=argument.javadoc indent=indent+'\t'/>
				<#lt/>${indent}	public ${field.title?cap_first}Arguments set${argument.name?cap_first}(${argument.type.full} value) {
				<#lt/>${indent}		documentBuilder.append(" ${argument.name}:");
				<#lt/>${indent}		java.util.stream.Stream.of(value)
				<@asLiteralArgument fieldType=argument.type indent=indent+'\t\t\t'/>
				<#lt/>${indent}		return this;
				<#lt/>${indent}	}
			</#list>
			<#lt/>${indent}}
		</#if>
		<@addSelectionField className field indent/>
	</#list>
</#macro>

<#macro addVariables className variables indent>
	<#list variables as field>
		<#lt/>${indent}public ${className} set${field.name?cap_first}(${field.type.full} value) {
		<#lt/>${indent}	java.util.stream.Stream.of(value)
		<@addNullCheck fieldType=field.type indent=indent+'\t\t'/>
		<#lt/>${indent}	map.put("${field.name}", value);
		<#lt/>${indent}	return this;
		<#lt/>${indent}}
	</#list>
</#macro>

<#macro addEquals className fields indent>
	<#lt/>${indent}@Override
	<#lt/>${indent}public boolean equals(Object o) {
	<#lt/>${indent}	if (this == o) return true;
	<#lt/>${indent}	if (!(o instanceof ${className})) return false;
	<#list fields>
		<#lt/>${indent}	${className} other = (${className}) o;
		<#lt/>${indent}	return
		<#items as field>
			<#lt/>${indent}		java.util.Objects.equals(this.get${getFieldName(field)?cap_first}(), other.get${getFieldName(field)?cap_first}())<#sep> &&</#sep>
		</#items>
		<#lt/>${indent}	;
	<#else>
		<#lt/>${indent}	return true;
	</#list>
	<#lt/>${indent}}
</#macro>

<#macro addHashCode fields indent>
	<#lt/>${indent}@Override
	<#lt/>${indent}public int hashCode() {
	<#lt/>${indent}	return java.util.Objects.hash(
	<#list fields as field>
		<#lt/>${indent}		get${getFieldName(field)?cap_first}()<#sep>,</#sep>
	</#list>
	<#lt/>${indent}	);
	<#lt/>${indent}}
</#macro>

<#macro addToString fields indent>
	<#lt/>${indent}@Override
	<#lt/>${indent}public String toString() {
	<#lt/>${indent}	return new StringBuilder().append("{")
	<#list fields as field>
		<#lt/>${indent}		.append(" ${getFieldName(field)} = ").append(get${getFieldName(field)?cap_first}())<#sep>.append(",")</#sep>
	</#list>
	<#lt/>${indent}		.append(" }")
	<#lt/>${indent}		.toString();
	<#lt/>${indent}}
</#macro>

<#macro asLiteral fieldType indent>
	<#switch fieldType.flag>
		<#case 'MANDATORY'>
			<@asLiteral fieldType=fieldType.nested indent=indent/>
		<#break/>
		<#case 'LIST'>
			<#lt/>${indent}.flatMap(${operationsPackage}.GraphQlAppender.arrayAppender(documentBuilder))
			<@asLiteral fieldType=fieldType.nested indent=indent/>
		<#break/>
		<#default>
			<#lt/>${indent}.forEach(${operationsPackage}.GraphQlAppender.valueAppender(documentBuilder));
		<#break/>
	</#switch>
</#macro>

<#macro addToGraphQlString fields indent>
	<#lt/>${indent}@Override
	<#lt/>${indent}public String toString() {
	<#lt/>${indent}	StringBuilder documentBuilder = new StringBuilder().append(" {");
	<#list fields as field>
		<#lt/>${indent}	documentBuilder.append(" ${getFieldName(field)}:");
		<#lt/>${indent}	java.util.stream.Stream.of(get${getFieldName(field)?cap_first}())
		<@asLiteral fieldType=field.type indent=indent+'\t\t'/>
	</#list>
	<#lt/>${indent}	return documentBuilder.append(" }").toString();
	<#lt/>${indent}}
</#macro>

<#macro addConstructor className fields indent>
	<#lt/>${indent}@java.beans.ConstructorProperties({
	<#list fields as field>
		<#lt/>${indent}	"${getFieldName(field)}"<#sep>,</#sep>
	</#list>
	<#lt/>${indent}})
	<#lt/>${indent}${dtoBuilder?then("private","public")} ${className}(
	<#list fields as field>
		<#lt/>${indent}	${getFieldType(field)} ${propertyPrefix!}${getFieldName(field)}${propertySuffix!}<#sep>,</#sep>
	</#list>
	<#lt/>${indent}) {
	<#list fields as field>
		<#lt/>${indent}	this.${propertyPrefix!}${getFieldName(field)}${propertySuffix!} = ${propertyPrefix!}${getFieldName(field)}${propertySuffix!};
	</#list>
	<#lt/>${indent}}
</#macro>

<#macro addBuilderMethod builderClassName indent>
	<#lt/>${indent}public static ${builderClassName} builder() {
	<#lt/>${indent}	return new ${builderClassName}();
	<#lt/>${indent}}
</#macro>

<#macro addBuildMethod resultClassName fields indent>
	<#lt/>${indent}public ${resultClassName} build() {
	<#lt/>${indent}	return new ${resultClassName}(
	<#list fields as field>
		<#lt/>${indent}		${propertyPrefix!}${getFieldName(field)}${propertySuffix!}<#sep>,</#sep>
	</#list>
	<#lt/>${indent}	);
	<#lt/>${indent}}
</#macro>

<#macro addRootClassAnnotations>
	<#if generatedAnnotation?has_content>
		<#lt/>${generatedAnnotation}
	</#if>
	<#lt/>@SuppressWarnings({"all", "PMD"})
</#macro>

<#macro addJavadoc javadoc indent>
	<#if javadoc?has_content>
		<#lt/>${indent}/**
		<#list javadoc as line>
			<#lt/>${indent} * ${line}
		</#list>
		<#lt/>${indent} */
	</#if>
</#macro>
