// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen

// swiftlint:disable superfluous_disable_command implicit_return
// swiftlint:disable sorted_imports
import CoreData
import Foundation
{% for import in param.extraImports %}
import {{ import }}
{% empty %}
{# If extraImports is a single String instead of an array, `for` considers it empty but we still have to check if there's a single String value #}
{% if param.extraImports %}import {{ param.extraImports }}{% endif %}
{% endfor %}

// swiftlint:disable attributes file_length vertical_whitespace_closing_braces
// swiftlint:disable identifier_name line_length type_body_length
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}

{% for model in models %}
{% for name, entity in model.entities %}
{% set superclass %}{{ model.entities[entity.superEntity].className|default:"NSManagedObject" }}{% endset %}
{% set entityClassName %}{{ entity.className|default:"NSManagedObject" }}{% endset %}
// MARK: - {{ entity.name }}

{% if not entity.shouldGenerateCode %}
// Note: '{{ entity.name }}' has codegen enabled for Xcode, skipping code generation.

{% elif entityClassName|contains:"." %}
// Warning: '{{ entityClassName }}' cannot be a valid type name, skipping code generation.

{% else %}
{% if param.generateObjcName %}
@objc({{ entityClassName }})
{% endif %}
{{ accessModifier }} class {{ entityClassName }}: {{ superclass }} {
  {% set override %}{% if superclass != "NSManagedObject" %}override {% endif %}{% endset %}
  {{ override }}{{ accessModifier }} class var entityName: String {
    return "{{ entity.name }}"
  }

  {{ override }}{{ accessModifier }} class func entity(in managedObjectContext: NSManagedObjectContext) -> NSEntityDescription? {
    return NSEntityDescription.entity(forEntityName: entityName, in: managedObjectContext)
  }

  @available(*, deprecated, renamed: "makeFetchRequest", message: "To avoid collisions with the less concrete method in `NSManagedObject`, please use `makeFetchRequest()` instead.")
  @nonobjc {{ accessModifier }} class func fetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
    return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
  }

  @nonobjc {{ accessModifier }} class func makeFetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
    return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
  }

  // swiftlint:disable discouraged_optional_boolean discouraged_optional_collection
  {% for attribute in entity.attributes %}
  {% if attribute.userInfo.RawType %}
  {% set rawType attribute.userInfo.RawType %}
  {% set unwrapOptional attribute.userInfo.unwrapOptional %}
  {{ accessModifier }} var {{ attribute.name }}: {{ rawType }}{% if not unwrapOptional %}?{% endif %} {
    get {
      let key = "{{ attribute.name }}"
      willAccessValue(forKey: key)
      defer { didAccessValue(forKey: key) }

      {% if unwrapOptional %}
      guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue,
        let result = {{ rawType }}(rawValue: value) else {
        fatalError("Could not convert value for key '\(key)' to type '{{ rawType }}'")
      }
      return result
      {% else %}
      guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue else {
        return nil
      }
      return {{ rawType }}(rawValue: value)
      {% endif %}
    }
    set {
      let key = "{{ attribute.name }}"
      willChangeValue(forKey: key)
      defer { didChangeValue(forKey: key) }

      setPrimitiveValue(newValue{% if not unwrapOptional %}?{% endif %}.rawValue, forKey: key)
    }
  }
  {% elif attribute.usesScalarValueType and attribute.isOptional %}
  {{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}? {
    get {
      let key = "{{ attribute.name }}"
      willAccessValue(forKey: key)
      defer { didAccessValue(forKey: key) }

      return primitiveValue(forKey: key) as? {{ attribute.typeName }}
    }
    set {
      let key = "{{ attribute.name }}"
      willChangeValue(forKey: key)
      defer { didChangeValue(forKey: key) }

      setPrimitiveValue(newValue, forKey: key)
    }
  }
  {% else %}
  @NSManaged {{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}{% if attribute.isOptional %}?{% endif %}
  {% endif %}
  {% endfor %}
  {% for relationship in entity.relationships %}
  {% if relationship.isToMany %}
  @NSManaged {{ accessModifier }} var {{ relationship.name }}: {% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}>{% endif %}{% if relationship.isOptional %}?{% endif %}
  {% else %}
  @NSManaged {{ accessModifier }} var {{ relationship.name }}: {{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% if relationship.isOptional %}?{% endif %}
  {% endif %}
  {% endfor %}
  {% for fetchedProperty in entity.fetchedProperties %}
  @NSManaged {{ accessModifier }} var {{ fetchedProperty.name }}: [{{ fetchedProperty.fetchRequest.entity }}]
  {% endfor %}
  // swiftlint:enable discouraged_optional_boolean discouraged_optional_collection
}

{% for relationship in entity.relationships where relationship.isToMany %}
{% set destinationEntityClassName %}{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% endset %}
{% set collectionClassName %}{% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ destinationEntityClassName }}>{% endif %}{% endset %}
{% set relationshipName %}{{ relationship.name | upperFirstLetter }}{% endset %}
// MARK: Relationship {{ relationshipName }}

extension {{ entityClassName }} {
  {% if relationship.isOrdered %}
  @objc(insertObject:in{{ relationshipName }}AtIndex:)
  @NSManaged public func insertInto{{ relationshipName }}(_ value: {{ destinationEntityClassName }}, at idx: Int)

  @objc(removeObjectFrom{{ relationshipName }}AtIndex:)
  @NSManaged public func removeFrom{{ relationshipName }}(at idx: Int)

  @objc(insert{{ relationshipName }}:atIndexes:)
  @NSManaged public func insertInto{{ relationshipName }}(_ values: [{{ destinationEntityClassName }}], at indexes: NSIndexSet)

  @objc(remove{{ relationshipName }}AtIndexes:)
  @NSManaged public func removeFrom{{ relationshipName }}(at indexes: NSIndexSet)

  @objc(replaceObjectIn{{ relationshipName }}AtIndex:withObject:)
  @NSManaged public func replace{{ relationshipName }}(at idx: Int, with value: {{ destinationEntityClassName }})

  @objc(replace{{ relationshipName }}AtIndexes:with{{ relationshipName }}:)
  @NSManaged public func replace{{ relationshipName }}(at indexes: NSIndexSet, with values: [{{ destinationEntityClassName }}])

  {% endif %}
  @objc(add{{ relationshipName }}Object:)
  @NSManaged public func addTo{{ relationshipName }}(_ value: {{ destinationEntityClassName }})

  @objc(remove{{ relationshipName }}Object:)
  @NSManaged public func removeFrom{{ relationshipName }}(_ value: {{ destinationEntityClassName }})

  @objc(add{{ relationshipName }}:)
  @NSManaged public func addTo{{ relationshipName }}(_ values: {{ collectionClassName }})

  @objc(remove{{ relationshipName }}:)
  @NSManaged public func removeFrom{{ relationshipName }}(_ values: {{ collectionClassName }})
}

{% endfor %}
{% if model.fetchRequests[entity.name].count > 0 %}
// MARK: Fetch Requests

extension {{ entityClassName }} {
  {% for fetchRequest in model.fetchRequests[entity.name] %}
  {% set resultTypeName %}{% filter removeNewlines:"leading" %}
    {% if fetchRequest.resultType == "Object" %}
    {{ entityClassName }}
    {% elif fetchRequest.resultType == "Object ID" %}
    NSManagedObjectID
    {% elif fetchRequest.resultType == "Dictionary" %}
    [String: Any]
    {% endif %}
  {% endfilter %}{% endset %}
  class func fetch{{ fetchRequest.name | upperFirstLetter }}({% filter removeNewlines:"leading" %}
    managedObjectContext: NSManagedObjectContext
    {% for variableName, variableType in fetchRequest.substitutionVariables %}
    , {{ variableName | lowerFirstWord }}: {{ variableType }}
    {% endfor %}
  {% endfilter %}) throws -> [{{ resultTypeName }}] {
    guard let persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator else {
      fatalError("Managed object context has no persistent store coordinator for getting fetch request templates")
    }
    let model = persistentStoreCoordinator.managedObjectModel
    let substitutionVariables: [String: Any] = [
      {% for variableName, variableType in fetchRequest.substitutionVariables %}
      "{{ variableName }}": {{ variableName | lowerFirstWord }}{{ "," if not forloop.last }}
      {% empty %}
      :
      {% endfor %}
    ]

    guard let fetchRequest = model.fetchRequestFromTemplate(withName: "{{ fetchRequest.name }}", substitutionVariables: substitutionVariables) else {
      fatalError("No fetch request template named '{{ fetchRequest.name }}' found.")
    }

    guard let result = try managedObjectContext.fetch(fetchRequest) as? [{{ resultTypeName }}] else {
      fatalError("Unable to cast fetch result to correct result type.")
    }

    return result
  }

  {% endfor %}
}

{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
// swiftlint:enable identifier_name line_length type_body_length
