{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst | overrideIdentifier}}>{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}{% endif %}{% else %}{{property | typeName}}{% endif %}{% if not property.required %}?{% endif %}{% endmacro %}
{% macro property_name(definition, property) %}{{ property.name | caseUcfirst | removeDollarSign | escapeKeyword }}{% endmacro %}

using System;
using System.Linq;
using System.Collections.Generic;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace {{ spec.title | caseUcfirst }}.Models
{
    public class {{ definition.name | caseUcfirst | overrideIdentifier }}
    {
        {%~ for property in definition.properties %}
        [JsonProperty("{{ property.name }}")]
        public {{ _self.sub_schema(property) }} {{ _self.property_name(definition, property) | overrideProperty(definition.name) }} { get; private set; }

        {%~ endfor %}
        {%~ if definition.additionalProperties %}
        public Dictionary<string, object> Data { get; private set; }

        {%~ endif %}
        public {{ definition.name | caseUcfirst | overrideIdentifier }}(
            {%~ for property in definition.properties %}
            {{ _self.sub_schema(property) }} {{ property.name | caseCamel | escapeKeyword }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %}

            {%~ endfor %}
            {%~ if definition.additionalProperties %}
            Dictionary<string, object> data
            {%~ endif %}
        ) {
            {%~ for property in definition.properties %}
            {{ _self.property_name(definition, property) | overrideProperty(definition.name) }} = {{ property.name | caseCamel | escapeKeyword }};
            {%~ endfor %}
            {%~ if definition.additionalProperties %}
            Data = data;
            {%~ endif %}
        }

        public static {{ definition.name | caseUcfirst | overrideIdentifier }} From(Dictionary<string, object> map) => new {{ definition.name | caseUcfirst | overrideIdentifier }}(
            {%~ for property in definition.properties %}
            {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: 
                {%- if property.sub_schema %} 
                    {%~ if property.type == 'array' %}
                        ((JArray)map["{{ property.name }}"])
                            .ToObject<List<Dictionary<string, object>>>()
                            .Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it))
                            .ToList()
                    {%- else %}
                        {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(
                            map: ((JObject)map["{{ property.name }}"])
                                .ToObject<Dictionary<string, object>>()!
                        )
                    {%- endif %}
                {%~ else %}
                    {%~ if property.type == 'array' %}
                        ((JArray)map["{{ property.name }}"]).ToObject<{{ property | typeName }}>()
                    {%~ else %}
                        {%~ if property.type == "integer" or property.type == "number" %}
                            {%~ if not property.required %}map["{{ property.name }}"] == null ? null : {% endif %} Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"])
                        {%~ else %}
                            {%~ if property.type == "boolean" %}
                                ({{ property | typeName }}{% if not property.required %}?{% endif %})map["{{ property.name }}"]
                            {%~ else %}
                                {%~ if not property.required %}map.TryGetValue("{{ property.name }}", out var {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}) ? {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}?.ToString() : null
                                {%~ else %} map["{{ property.name }}"].ToString(){% endif %}
                            {%- endif %}
                        {%~ endif %}
                    {%~ endif %}
                {%~ endif %}
                {%- if not loop.last or (loop.last and definition.additionalProperties) %},
                {%~ endif %}
            {%~ endfor %}
            {%- if definition.additionalProperties %}
                , data: map
            {%- endif ~%}
        );

        public Dictionary<string, object?> ToMap() => new Dictionary<string, object?>()
        {
            {%~ for property in definition.properties %}
            { "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ _self.property_name(definition, property) | overrideProperty(definition.name) }}.Select(it => it.ToMap()){% else %}{{ _self.property_name(definition, property) | overrideProperty(definition.name) }}.ToMap(){% endif %}{% else %}{{ _self.property_name(definition, property) | overrideProperty(definition.name) }}{% endif %}{{ ' }' }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %}

            {%~ endfor %}
            {%~ if definition.additionalProperties %}
            { "data", Data }
            {%~ endif %}
        };
        {%~ if definition.additionalProperties %}

        public T ConvertTo<T>(Func<Dictionary<string, object>, T> fromJson) =>
            fromJson.Invoke(Data);
        {%~ endif %}
        {%~ for property in definition.properties %}
        {%~ if property.sub_schema %}
        {%~ for def in spec.definitions %}
        {%~ if def.name == property.sub_schema and def.additionalProperties and property.type == 'array' %}

        public T ConvertTo<T>(Func<Dictionary<string, object>, T> fromJson) =>
            (T){{ property.name | caseUcfirst | escapeKeyword }}.Select(it => it.ConvertTo(fromJson));

        {%~ endif %}
        {%~ endfor %}
        {%~ endif %}
        {%~ endfor %}
    }
}