//
//  SettingsViewController.swift
//  KeymanEngine
//
//  Created by Randy Boring on 5/13/19.
//  Copyright © 2019 SIL International. All rights reserved.
//

import UIKit

open class SettingsViewController: UITableViewController {
  private var itemsArray = [[String: String]]()
  private var userLanguages: [String: Language] = [:]

  override open func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    loadUserLanguages()
    log.info("willAppear: SettingsViewController")
 }
  
  override open func viewDidLoad() {
    super.viewDidLoad()
    
    title = NSLocalizedString("menu-settings-title", bundle: engineBundle, comment: "")
    let doneButton = UIBarButtonItem(title: NSLocalizedString("Done", bundle: engineBundle, comment: ""), style: .plain, target: self,
                                     action: #selector(self.doneClicked))
    navigationItem.leftBarButtonItem = doneButton

    navigationController?.toolbar?.barTintColor = Colors.statusToolbar
  }
  
  @objc func doneClicked(_ sender: Any) {
    // While the called method might should be renamed, it does the job well enough.
    // This resets KMW so that any new and/or updated resources can be properly loaded.
    Manager.shared.dismissKeyboardPicker(self)
  }
  
  open func launchSettings(launchingVC: UIViewController, sender: Any?) -> Void {
    let sb : UIStoryboard = UIStoryboard(name: "Settings", bundle: nil)
    if let vc = sb.instantiateInitialViewController() {
      launchingVC.present(vc, animated: true, completion: {
        log.info("presented settings")
      })
    }
  }
//
//  convenience init() {
//    self.init(style: UITableViewStyle.grouped)
//  }
  
  public init(/*storage: Storage*/) {
//    self.storage = storage
    super.init(nibName: nil, bundle: nil)
    
    itemsArray = [[String: String]]()
    itemsArray.append([
      "title": NSLocalizedString("menu-installed-languages-title", bundle: engineBundle, comment: ""),
      "subtitle": "0", //count of installed languages as string
      "reuseid" : "languages"
      ])
    
    itemsArray.append([
      "title": NSLocalizedString("menu-settings-show-banner", bundle: engineBundle, comment: ""),
      "subtitle": "",
      "reuseid" : "showbanner"
      ])
    
    itemsArray.append([
      "title": NSLocalizedString("menu-settings-startup-get-started", bundle: engineBundle, comment: ""),
      "subtitle": "",
      "reuseid" : "showgetstarted"
      ])

    itemsArray.append([
      "title": NSLocalizedString("menu-settings-error-report", bundle: engineBundle, comment: ""),
      "subtitle": NSLocalizedString("menu-settings-error-report-description", bundle: engineBundle, comment: ""),
      "reuseid": "enablecrashreporting"
      ])

    itemsArray.append([
      "title": NSLocalizedString("menu-settings-spacebar-text", bundle: engineBundle, comment: ""),
      "subtitle": "",
      "reuseid": "spacebartext"
      ])
    
    if let _ = URL(string: UIApplication.openSettingsURLString) {
      itemsArray.append([
        "title": NSLocalizedString("menu-settings-system-keyboard-menu", bundle: engineBundle, comment: ""),
        "subtitle": "",
        "reuseid": "systemkeyboardsettings"
        ])
    }

    itemsArray.append([
      "title": NSLocalizedString("menu-settings-install-from-file", bundle: engineBundle, comment: ""),
      "subtitle": NSLocalizedString("menu-settings-install-from-file-description", bundle: engineBundle, comment: ""),
      "reuseid" : "installfile"
      ])

    #if DEBUG && !NO_SENTRY
            itemsArray.append([
      "title": "Force a crash",
      "subtitle": "Test Sentry error-reporting integration",
      "reuseid" : "forcederror"
      ])
    #endif

    _ = view
  }

  required public init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

    // MARK: - Table view data source

  override open func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

  override open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return itemsArray.count
    }
  
  public func frameAtRightOfCell(cell cellFrame: CGRect, controlSize: CGSize) -> CGRect {
    let rightOffset = cellFrame.size.width
    let switchWidth: CGFloat = 20
    let switchX = rightOffset - switchWidth
    let switchHeight = controlSize.height
    let cellSwitchHeightDiff = cellFrame.size.height - switchHeight
    let switchY = cellFrame.origin.y + 0.5 * cellSwitchHeightDiff
    
    let switchFrame = CGRect(x: switchX,
                             y: switchY,
                             width: switchWidth,
                             height: cellFrame.size.height)
    return switchFrame
  }

  override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellIdentifier = itemsArray[indexPath.row]["reuseid"]
    if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier!) {
      return cell
    }
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)
    cell.selectionStyle = .none
    
    switch(cellIdentifier) {
      case "languages":
        break
      case "showbanner":
        let showBannerSwitch = UISwitch()
        showBannerSwitch.translatesAutoresizingMaskIntoConstraints = false
        
        let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: showBannerSwitch.frame.size)
        showBannerSwitch.frame = switchFrame
        
        showBannerSwitch.isOn = showBanner
        showBannerSwitch.addTarget(self, action: #selector(self.bannerSwitchValueChanged),
                                      for: .valueChanged)
        cell.addSubview(showBannerSwitch)
        cell.contentView.isUserInteractionEnabled = false

        showBannerSwitch.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
        showBannerSwitch.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true
      case "showgetstarted":
        let showAgainSwitch = UISwitch()
        showAgainSwitch.translatesAutoresizingMaskIntoConstraints = false
        
        let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: showAgainSwitch.frame.size)
        showAgainSwitch.frame = switchFrame
        
        showAgainSwitch.isOn = showGetStarted
        showAgainSwitch.addTarget(self, action: #selector(self.showGetStartedSwitchValueChanged),
                                      for: .valueChanged)
        cell.addSubview(showAgainSwitch)
        cell.contentView.isUserInteractionEnabled = false

        showAgainSwitch.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
        showAgainSwitch.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true
      case "enablecrashreporting":
        let enableReportingSwitch = UISwitch()
        enableReportingSwitch.translatesAutoresizingMaskIntoConstraints = false

        let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: enableReportingSwitch.frame.size)
        enableReportingSwitch.frame = switchFrame

        enableReportingSwitch.isOn = SentryManager.enabled
        enableReportingSwitch.addTarget(self, action: #selector(self.reportingSwitchValueChanged),
                                      for: .valueChanged)
        cell.addSubview(enableReportingSwitch)
        cell.contentView.isUserInteractionEnabled = false

        enableReportingSwitch.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
        enableReportingSwitch.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true
        
      case "systemkeyboardsettings", "installfile", "forcederror", "spacebartext":
        break
      default:
        SentryManager.captureAndLog("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")")
        cell.accessoryType = .none
    }
    
    return cell
  }

  @objc func reportingSwitchValueChanged(_ sender: Any) {
    if let toggle = sender as? UISwitch {
      // Propagate the effects
      SentryManager.enabled = toggle.isOn
    }
  }
  
  @objc func bannerSwitchValueChanged(_ sender: Any) {
    let userData = Storage.active.userDefaults
    if let toggle = sender as? UISwitch {
      // actually this should call into KMW, which controls the banner
      userData.set(toggle.isOn, forKey: Key.optShouldShowBanner)
      userData.synchronize()
    }

    // Necessary for the keyboard to visually update to match
    // the new setting.
    Manager.shared.inputViewController.setShouldReload()
  }
  
  @objc func showGetStartedSwitchValueChanged(_ sender: Any) {
    let userData = Storage.active.userDefaults
    if let toggle = sender as? UISwitch {
      userData.set(toggle.isOn, forKey: Key.optShouldShowGetStarted)
      userData.synchronize()
    }
  }

  private var showBanner: Bool {
    let userData = Storage.active.userDefaults
    return userData.bool(forKey: Key.optShouldShowBanner)
  }
  
  private var showGetStarted: Bool {
    let userData = Storage.active.userDefaults
    return userData.bool(forKey: Key.optShouldShowGetStarted)
  }

  override open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cell.accessoryType = .none

    // Remember, UITableViewCells may be reused, so we should always reset relevant properties.
    cell.textLabel?.text = itemsArray[indexPath.row]["title"]
    cell.detailTextLabel?.text = itemsArray[indexPath.row]["subtitle"]
    cell.textLabel?.isEnabled = true

    cell.tag = indexPath.row
    cell.isUserInteractionEnabled = true

    let cellIdentifier = itemsArray[indexPath.row]["reuseid"]

    switch (cellIdentifier) {
      case "languages", "installfile", "systemkeyboardsettings", "forcederror":
        cell.accessoryType = .disclosureIndicator
        cell.detailTextLabel?.isEnabled = true
      case "enablecrashreporting":
        cell.detailTextLabel?.isEnabled = true
        break
      case "spacebartext":
        cell.accessoryType = .disclosureIndicator
        cell.detailTextLabel?.text = NSLocalizedString("menu-settings-spacebar-hint-"+Manager.shared.spacebarText.rawValue, bundle: engineBundle, comment: "")
        cell.detailTextLabel?.isEnabled = true
        break
      case "showbanner", "showgetstarted":
        cell.detailTextLabel?.isEnabled = false
      default:
        SentryManager.captureAndLog("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")")
    }
  }
  
  // In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath:
  override open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.cellForRow(at: indexPath)?.isSelected = false
    performAction(for: indexPath)
  }
  
  override open func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
    performAction(for: indexPath)
  }
  
  private func performAction(for indexPath: IndexPath) {
    switch indexPath.section {
    case 0:
      let cellIdentifier = itemsArray[indexPath.row]["reuseid"]
      switch cellIdentifier {
        case "languages":
          showLanguages()
        case "systemkeyboardsettings":
          guard let appSettings = URL(string: UIApplication.openSettingsURLString) else {
            // It is an error if the option is displayed but unusable.  That's bad UI.
            SentryManager.captureAndLog("Could not launch keyboard settings menu")
            return
          }
          UniversalLinks.externalLinkLauncher?(appSettings)
        case "installfile":
          if let block = Manager.shared.fileBrowserLauncher {
            block(navigationController!)
          } else {
            SentryManager.captureAndLog("Listener for framework signal to launch file browser is missing")
          }
        case "forcederror":
          SentryManager.forceError()
        case "spacebartext":
          showSpacebarText()
        default:
          break
      }
    default:
      break
    }
  }

    /*
    // Override to support conditional editing of the table view.
    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return true
    }
    */

    /*
    // Override to support editing the table view.
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            // Delete the row from the data source
            tableView.deleteRows(at: [indexPath], with: .fade)
        } else if editingStyle == .insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }    
    }
    */

    /*
    // Override to support rearranging the table view.
    override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {

    }
    */

    /*
    // Override to support conditional rearranging of the table view.
    override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        // Return false if you do not want the item to be re-orderable.
        return true
    }
    */

    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
  override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      log.info("prepare for segue")
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
  }
  
  // MARK: - language access -
  
  /** returns an array of LexicalModel created from an array of InstallableLexicalModel
   *  @param installedList: The InstallableLexicalModel are probably already installed
   *  The returned LexicalModels are not complete (usually we go the other way round)
   *    but sufficient for future API calls
   */
  public static func installed2API(_ installedList: [InstallableLexicalModel]) -> [LexicalModel] {
    var returnList = [LexicalModel]()
    for ilm in installedList {
      returnList.append(LexicalModel(id: ilm.id, name: ilm.name, license: "", version: ilm.version, languages: [], authorName: "", fileSize: 0, filename: "no filename", sourcePath: nil, authorEmail: nil, description: nil, packageFileSize: 0, packageFilename: "", packageIncludes: nil, isDefault: false, lastModified: nil, minKeymanVersion: nil))
    }
    return returnList
  }
  
  // MARK: - language access -
  private func loadUserLanguages() {
    //iterate the list of installed languages and save their names
    // Get keyboards list if it exists in user defaults, otherwise create a new one
    let userDefaults = Storage.active.userDefaults
    let userKeyboards = userDefaults.userKeyboards ?? []

    var keyboardLanguages = [String: Language]()
    for k in userKeyboards {
      let l = k.languageID
      var kbds: [Keyboard]
      if let existingLanguage = keyboardLanguages[l] {
        kbds = existingLanguage.keyboards ?? []
        kbds.append(Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil))
      } else {
        kbds = [Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil)]
      }
      let userDefaults : UserDefaults = Storage.active.userDefaults
      let lmListInstalled: [InstallableLexicalModel] = userDefaults.userLexicalModelsForLanguage(languageID: l) ?? []
      let lmList = SettingsViewController.installed2API(lmListInstalled)
      keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: kbds, lexicalModels: lmList, font: nil, oskFont: nil)
    }
    // there shouldn't be any lexical models for languages that don't have a keyboard installed
    //  but check
    let userLexicalModels = userDefaults.userLexicalModels ?? []
    for lm in userLexicalModels {
      let l = lm.languageID
      if let langName = keyboardLanguages[l]?.name {
        log.info("keyboard language \(l) \(langName) has lexical model")
      } else {
        // Legacy behavior:  we automatically install all MTNT language codes, even without
        // a matching keyboard for the more specific variant(s).
        SentryManager.breadcrumbAndLog("lexical model language \(l) has no keyboard installed!")
      }
    }

    userLanguages = keyboardLanguages
    let formatString = NSLocalizedString("settings-languages-installed-count", bundle: engineBundle, comment: "")
    itemsArray[0]["subtitle"] = String.localizedStringWithFormat(formatString, userLanguages.count)
    tableView.reloadData()
  }
  
  public func setIsDoneButtonEnabled(_ nc: UINavigationController, _ value: Bool) {
    let doneOrCancel = value
    if doneOrCancel {
      let doneButton = UIBarButtonItem(title: NSLocalizedString("command-done", bundle: engineBundle, comment: ""), style: .plain, target: self,
                                       action: nil /* #selector(self.doneClicked) */ )
      nc.navigationItem.leftBarButtonItem = doneButton
    } else {
      let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self,
                                         action: nil /* #selector(self.cancelClicked) */ )
      nc.navigationItem.leftBarButtonItem = cancelButton
    }
  }

  func showLanguages() {
    let vc = InstalledLanguagesViewController(userLanguages)
    if let nc = navigationController {
      nc.pushViewController(vc, animated: true)
      setIsDoneButtonEnabled(nc, true)
    } else {
      SentryManager.captureAndLog("no navigation controller for showing languages???")
    }
  }
  
  func showSpacebarText() {
    let vc = SpacebarTextViewController()
    if let nc = navigationController {
      nc.pushViewController(vc, animated: true)
      setIsDoneButtonEnabled(nc, true)
    } else {
      SentryManager.captureAndLog("no navigation controller for showing spacebarText options")
    }
  }
  
}
