//
//  KMPLexicalModel.swift
//  KeymanEngine
//
//  Created by Randy Boring on 3/12/19.
//  Copyright © 2019 SIL International. All rights reserved.
//

import Foundation

class KMPLexicalModel: Codable, KMPResource {
  public var name: String
  public var lexicalModelId: String
  public var packageId: String?
  public var version: String?
  public var isRTL: Bool = false
  public var languages: [KMPLanguage]

  enum CodingKeys: String, CodingKey {
    case name
    case lexicalModelId = "id"
    case version
    case isRTL = "rtl"
    case languages
  }

  internal required init?(from lexicalModel: InstallableLexicalModel) {

    self.name = lexicalModel.name
    self.lexicalModelId = lexicalModel.id
    self.version = lexicalModel.version
    self.packageId = lexicalModel.packageID ?? lexicalModel.id

    // InstallableLexicalModel doesn't store the language name, so we use the id as a fill-in.
    // The 'name' part isn't used for matching, anyway.
    self.languages = [KMPLanguage(name: lexicalModel.languageID, languageId: lexicalModel.languageID)]
  }

  required public init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)

    name = try values.decode(String.self, forKey: .name)
    lexicalModelId = try values.decode(String.self, forKey: .lexicalModelId)
    // Our schema says that this field is optional for lexical models.
    // In these cases, the version should probably be set to the package's version...
    // but this constructor lacks access.  We'll let the owning Package instance handle this.
    version = try values.decodeIfPresent(String.self, forKey: .version)
    isRTL = try values.decodeIfPresent(Bool.self, forKey: .isRTL) ?? false
    languages = try values.decode([KMPLanguage].self, forKey: .languages)
  }

  /** This function will set the value for version only when a lexical model doesn't have a value for it specified directly in its definition.
   */
  public func setNilVersion(to version: String) {
    self.version = self.version ?? version
  }

  func hasMatchingMetadata(for resource: InstallableLexicalModel, ignoreLanguage: Bool = false, ignoreVersion: Bool = true) -> Bool {
    if id != resource.id {
      return false
    } else if !ignoreVersion, version != resource.version {
      return false
    }

    if !ignoreLanguage {
      let resourceMetadata = KMPLexicalModel(from: resource)!
      return languages.contains(where: { language in
        return language.languageId == resourceMetadata.languages[0].languageId
      })
    }

    return true
  }

  internal var installableLexicalModels: [InstallableLexicalModel] {
    var installableLexicalModels : [InstallableLexicalModel] = []

    for language in self.languages {
      let model = InstallableLexicalModel(from: self, packageID: packageId!, lgCode: language.languageId)!
      installableLexicalModels.append( model )
    }

    return installableLexicalModels
  }

  // Needed to properly support AnyKMPResource.installableResources
  // because of weird, weird Swift rules.
  public var typedInstallableResources: [InstallableLexicalModel] {
    return installableLexicalModels
  }

  // Provides our class's method of the same signature, but with
  // the local type signature we know is available.
  public var installableResources: [InstallableLexicalModel] {
    return installableLexicalModels
  }

  public var isValid: Bool {
    // Ensures that our 'optional' members are properly instantiated.
    // Any file-based checks will be performed by the object's owner,
    // which knows the containing package's root folder.
    return installableLexicalModels.count > 0
      && self.languages.count > 0
  }

  public var id: String {
    return lexicalModelId
  }
}

