{# If you add a single import in the args you get a String, but multiple import are Arrays, then given that you don't have a lot of possibilities on stencil, the best way is check the class description (you can use == only on Numbers, Bools and Strings) #}
{% if argument.import.class.description == "Swift._NSContiguousString" %}
import {{ argument.import }}
{% else %}
{% for importName in argument.import %}
import {{ importName }}
{% endfor %}
{% endif %}
import TestSpy
@testable import {{ argument.module }}

// swiftlint:disable force_cast

{% macro parameterType typeName typeToReplace replaceType %}{% if typeName.description == typeToReplace %}{{ genericReplaceType }}{% else %}{{ typeName }}{% endif %}{% endmacro %}

{% macro methodCaseName method skipParameter typeToReplace replaceType %}{{ method.callName }}{% if method.parameters.count > 0 %}({% for param in method.parameters where param.name != skipParameter|default:"" and param.typeName.isClosure == 0 %}{{ param.name }}: {% call parameterType param.typeName genericType genericReplaceType %}{% if not forloop.last%}, {% endif %}{% endfor %}){% endif %}{% endmacro %}

{% macro mockOptionalVariable variable %}
    public var {% call mockedVariableName variable %}: {{ variable.typeName }}
{% endmacro %}

{% macro mockNonOptionalArrayOrDictionaryVariable variable %}
    public var {% call mockedVariableName variable %}: {{ variable.typeName }} = {% if variable.isArray %}[]{% elif variable.isDictionary %}[:]{% endif %}
{% endmacro %}

{% macro mockNonOptionalVariable variable %}
    public var {% call mockedVariableName variable %}: {{ variable.typeName }} {
        get { return {% call underlyingMockedVariableName variable %} }
        set(value) { {% call underlyingMockedVariableName variable %} = value }
    }
    public var {% call underlyingMockedVariableName variable %}: {{ variable.typeName }}!
{% endmacro %}

{% macro underlyingMockedVariableName variable %}underlying{{ variable.name|upperFirstLetter }}{% endmacro %}
{% macro mockedVariableName variable %}{{ variable.name }}{% endmacro %}

{% macro spyCase method skipParameter genericType genericReplaceType %}
    {% if not method.isInitializer %}
        case {% call methodCaseName method skipParameter genericType genericReplaceType %}
    {% endif %}
{% endmacro %}

{% macro addDefaultInitialiser initialisers %}
{% if initialisers == "[]" %}
    public init() {}
{% endif %}
{% endmacro %}

{% for type in types.protocols where type.annotations.autoSpy %}
// Spy for {{ type.name }}
public class Spy{{ type.name }}: {{ type.name }}, TestSpy {
	public enum Method: Equatable {
	{% for method in type.allMethods|!definedInExtension %}
		{% call spyCase method type.annotations.skipParameter type.annotations.typeToReplace type.annotations.replaceType %}
	{% endfor %}
	}

	{% for variable in type.allVariables|!definedInExtension %}
    {% if variable.isOptional %}{% call mockOptionalVariable variable %}{% elif variable.isArray or variable.isDictionary %}{% call mockNonOptionalArrayOrDictionaryVariable variable %}{% else %}{% call mockNonOptionalVariable variable %}{% endif %}
	{% endfor %}

	public var callstack = CallstackContainer<Method>()

    {% set initialisers %}{{ type.allMethods|initializer }}{% endset %}
    {% call addDefaultInitialiser initialisers %}

	{% for method in type.allMethods|!definedInExtension %}
	{% if method.isInitializer %}
    public required {{method.name}} {}
    {% else %}
    {% if not method.returnTypeName.isVoid %}
    public var {{ method.shortName }}Result: {{ method.returnTypeName }}!
    {% endif %}
    public func {{ method.name }}{% if method.throws %} throws{% endif %}{% if not method.returnTypeName.isVoid %} -> {{ method.returnTypeName }}{% endif %} {
        callstack.record(.{{ method.callName }}{% if method.parameters.count > 0 %}({% for param in method.parameters where param.name != type.annotations.skipParameter|default:"" and param.typeName.isClosure == 0 %}{{ param.name }}: {{param.name}} {% if param.typeName.description == type.annotations.typeToReplace %} as! {{ type.annotations.replaceType }} {% endif %}{% if not forloop.last%}, {% endif %}{% endfor %}){% endif %})
        {% if not method.returnTypeName.isVoid %}
        return {{ method.shortName }}Result
        {% endif %}
    }
	{% endif %}
	{% endfor %}
}
{% endfor %}