// -*- mode: Swift -*-

// AUTOGENERATED BY glean_parser v{{ parser_version }}. DO NOT EDIT. DO NOT COMMIT.
{# The rendered markdown is autogenerated, but this
Jinja2 template is not. Please file bugs! #}

#if canImport(Foundation)
    import Foundation
#endif

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
{%- macro obj_declaration(obj, suffix='', access='') %}
{{ access }}static let {{ obj.name|camelize|variable_name }}{{ suffix }} = {{ obj|type_name }}( // generated from {{ obj.identifier() }}
        CommonMetricData(
            {% for arg_name in common_metric_args if obj[arg_name] is defined %}
            {{ arg_name|camelize }}: {{ obj[arg_name]|swift }}{{ "," if not loop.last }}
            {% endfor %}
        )
        {% for arg_name in extra_metric_args if obj[arg_name] is defined %}
        , {{ obj[arg_name]|swift }}
        {% endfor %}
    )
{% endmacro %}

{%- macro struct_decl(obj, name, suffix) %}
struct {{ obj.name|Camelize }}{{ suffix }}: EventExtras {
        {% for item, typ in obj|attr(name) %}
        var {{ item|camelize|variable_name }}: {{typ|extra_type_name}}?
        {% endfor %}

        func toExtraRecord() -> [String: String] {
            var record = [String: String]()

            {% for item in obj|attr(name) %}
            if let {{ item[0]|camelize }} = self.{{item[0]|camelize}} {
                record["{{item[0]}}"] = String({{ item[0]|camelize }})
            }
            {% endfor %}

            return record
        }
    }
{% endmacro %}

{%- macro generate_structure(name, struct, toplevel) %}
{%- if struct.type == "oneof" -%}
    enum {{ name }}: Codable, Equatable {
        {% for ty in struct.subtypes %}
          case {{ty}}({{ty|structure_type_name}})
        {% endfor %}

        func encode(to __encoder: Encoder) throws {
            var __container = __encoder.singleValueContainer()
            switch self {
            {% for ty in struct.subtypes %}
              case .{{ty}}(let __val):
                try __container.encode(__val)
            {% endfor %}
            }
        }
    }

{%- elif struct.type == "array" -%}
    typealias {{ name }} = [{{ name }}Item]

    {{ generate_structure(name ~ "Item", struct["items"]) }}

{%- elif struct.type == "object" -%}
    struct {{ name }}: Codable, Equatable {% if toplevel %}, ObjectSerialize{% endif %} {
        {% for itemname, val in struct.properties.items() %}
          {%- set varname = itemname|camelize|variable_name -%}
          {% if val.type == "array" %}
          var {{varname}}: {{ name ~ itemname|Camelize }} = []
          {% elif val.type == "object" %}
          var {{varname}}: {{ name ~ "Item" ~ itemname|Camelize ~ "Object" }}?
          {% elif val.type == "oneof" %}
          var {{varname}}: {{ name ~ itemname|Camelize ~ "Enum" }}?
          {% else %}
          var {{varname}}: {{val.type|structure_type_name}}?
          {% endif %}
        {% endfor %}

        enum CodingKeys: String, CodingKey {
        {% for itemname, val in struct.properties.items() %}
          {%- set varname = itemname|camelize|variable_name -%}
          case {{varname}} = "{{ itemname }}"
        {% endfor %}
        }

        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            {% for itemname, val in struct.properties.items() %}
              {%- set varname = itemname|camelize|variable_name -%}
              {% if val.type == "array" %}
              if {{varname}}.count > 0 {
                let {{varname}} = self.{{varname}}
              {% else %}
              if let {{varname}} = self.{{varname}} {
              {% endif %}
              try container.encode({{varname}}, forKey: .{{varname}})
              }
            {% endfor %}
    }

        {% if toplevel %}
        func intoSerializedObject() -> String {
            let jsonEncoder = JSONEncoder()
            let jsonData = try! jsonEncoder.encode(self)
            let json = String(data: jsonData, encoding: String.Encoding.utf8)!
            return json
        }
        {% endif %}
    }

    {% for itemname, val in struct.properties.items() %}
        {% if val.type == "array" %}
        {% set nested_name = name ~ itemname|Camelize %}
        {{ generate_structure(nested_name, val) }}
        {% elif val.type == "object" %}
        {% set nested_name = name ~ "Item" ~ itemname|Camelize ~ "Object" %}
        {{ generate_structure(nested_name, val) }}
        {% elif val.type == "oneof" %}
        {% set nested_name = name ~ itemname|Camelize ~ "Enum" %}
        {{ generate_structure(nested_name, val) }}
        {% endif %}
    {% endfor %}

{%- else -%}

    typealias {{ name }} = {{ struct.type|structure_type_name }}

{% endif -%}

{% endmacro %}

{% if not allow_reserved %}
import {{ glean_namespace }}

{% endif %}
// swiftlint:disable superfluous_disable_command
// swiftlint:disable nesting
// swiftlint:disable line_length
// swiftlint:disable identifier_name
// swiftlint:disable force_try

extension {{ namespace }} {
    {% if build_info %}
    class GleanBuild {
        private init() {
            // Intentionally left private, no external user can instantiate a new global object.
        }

        public static let info = BuildInfo(buildDate: {{ build_info.build_date }})
    }
    {% endif %}

    {% for category in categories %}
    {% if category.contains_pings %}
    final class {{ category.name|Camelize }}: Sendable {
        public static let shared = {{ category.name|Camelize }}()
        private init() {
            // Intentionally left private, no external user can instantiate a new global object.
        }

        {% for obj in category.objs.values() %}
        {% if obj|attr("_generate_enums") %}
        {% for name, suffix in obj["_generate_enums"] %}
        {% if obj|attr(name)|length %}
        enum {{ obj.name|Camelize }}{{ suffix }}: Int, ReasonCodes, Sendable {
            {% for key in obj|attr(name) %}
            case {{ key|camelize|variable_name }} = {{ loop.index-1 }}
            {% endfor %}

            public func index() -> Int {
                return self.rawValue
            }
        }

        {% endif %}
        {% endfor %}
        {% endif %}
        /// {{ obj.description|wordwrap() | replace('\n', '\n        /// ') }}
        let {{ obj.name|camelize|variable_name }} = {{obj|type_name}}(
            name: {{ obj.name|swift }},
            includeClientId: {{obj.include_client_id|swift}},
            sendIfEmpty: {{obj.send_if_empty|swift}},
            preciseTimestamps: {{obj.precise_timestamps|swift}},
            includeInfoSections: {{obj.include_info_sections|swift}},
            enabled: {{obj.enabled|swift}},
            schedulesPings: {{obj.schedules_pings|swift}},
            reasonCodes: {{obj.reason_codes|swift}},
            followsCollectionEnabled: {{obj.follows_collection_enabled|swift}},
            uploaderCapabilities: {{obj.uploader_capabilities|swift}}
        )

      {% endfor %}
    }

    {% else %}
    enum {{ category.name|Camelize }} {
    {% for obj in category.objs.values() %}
        {% if obj|attr("_generate_structure") %}
        {{ generate_structure(obj.name|Camelize ~ "Object", obj._generate_structure, true) }}
        {%- endif %}
        {% if obj|attr("_generate_enums") %}
        {% for name, suffix in obj["_generate_enums"] %}
        {% if obj|attr(name)|length %}
        {{ struct_decl(obj, name, suffix)|indent }}
        {% endif %}
        {% endfor %}
        {% endif %}
    {% endfor %}
    {% for obj in category.objs.values() %}
        {% if obj.labeled %}
        {{ obj_declaration(obj, 'Label', 'private ') | indent }}
        /// {{ obj.description|wordwrap() | replace('\n', '\n        /// ') }}
        static let {{ obj.name|camelize|variable_name }} = try! LabeledMetricType<{{ obj|type_name }}>( // generated from {{ obj.identifier() }}
            category: {{ obj.category|swift }},
            name: {{ obj.name|swift }},
            sendInPings: {{ obj.send_in_pings|swift }},
            lifetime: {{ obj.lifetime|swift }},
            disabled: {{ obj.is_disabled()|swift }},
            subMetric: {{ obj.name|camelize }}Label,
            labels: {{ obj.labels|swift }}
        )

        {% else %}
        /// {{ obj.description|wordwrap() | replace('\n', '\n        /// ') }}
        {{ obj_declaration(obj) | indent }}
        {% endif %}
    {% endfor %}
    }

    {% endif %}
    {% endfor %}
}
