//
//  KMPKeyboard.swift
//  KeymanEngine
//
//  Created by Jacob Bullock on 2/16/18.
//  Copyright © 2018 SIL International. All rights reserved.
//

import Foundation

class KMPKeyboard: Codable, KMPResource {
  public var name: String
  public var keyboardId: String
  public var packageId: String?
  public var version: String
  public var osk: String?
  public var font: String?
  public var isRTL: Bool = false
  // Technically, the schema says that this doesn't have to be specified.
  // However, it's a requirement for KeymanEngine for iOS.
  public var languages: [KMPLanguage]

  enum CodingKeys: String, CodingKey {
    case name
    case keyboardId = "id"
    case version
    case osk = "oskFont"
    case font = "displayFont"
    case isRTL = "rtl"
    case languages
  }

  internal required init?(from keyboard: InstallableKeyboard) {
    self.name = keyboard.name
    self.keyboardId = keyboard.id
    self.packageId = keyboard.packageID ?? keyboard.id
    self.version = keyboard.version
    self.isRTL = keyboard.isRTL

    self.font = keyboard.font?.source[0]
    self.osk = keyboard.oskFont?.source[0]

    self.languages = [KMPLanguage(name: keyboard.languageName, languageId: keyboard.languageID)]
  }
  
  required public init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)

    name = try values.decode(String.self, forKey: .name)
    keyboardId = try values.decode(String.self, forKey: .keyboardId)
    version = try values.decode(String.self, forKey: .version)
    osk = try values.decodeIfPresent(String.self, forKey: .osk)
    font = try values.decodeIfPresent(String.self, forKey: .font)
    isRTL = try values.decodeIfPresent(Bool.self, forKey: .isRTL) ?? false
    languages = try values.decode([KMPLanguage].self, forKey: .languages)
  }

  func hasMatchingMetadata(for resource: InstallableKeyboard, 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 = KMPKeyboard(from: resource)!
      return languages.contains(where: { language in
        return language.languageId == resourceMetadata.languages[0].languageId
      })
    }

    return true
  }

  internal var installableKeyboards: [InstallableKeyboard] {
    var installableKeyboards : [InstallableKeyboard] = []

    for language in self.languages {
      let keyboard = InstallableKeyboard(from: self, packageID: packageId!, lgCode: language.languageId)!
      installableKeyboards.append( keyboard )
    }

    return installableKeyboards
  }

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

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

  public var displayFont: Font? {
    if let f = font {
      return Font(filename: f)
    }
    return nil
  }
  
  public var oskFont: Font? {
    if let f = osk {
      return Font(filename: f)
    }
    return nil
  }
  
  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 installableKeyboards.count > 0 && self.languages.count > 0
  }

  public var id: String {
    return keyboardId
  }
}
