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

import UIKit
import Sentry

private let toolbarButtonTag = 100

class LanguageSettingsViewController: UITableViewController {
  let language: Language
  private var settingsArray = [[String: String]]()

  private var doPredictionsSwitch: UISwitch?
  private var doCorrectionsSwitch: UISwitch?
  private var doCorrectionsLabel: UILabel?
  private var correctionsCell: UITableViewCell?

  public init(_ inLanguage: Language) {
    language = inLanguage
    super.init(nibName: nil, bundle: nil)
  }
  
  required public init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  open override func loadView() {
    super.loadView()
    tableView?.delegate = self
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    let titleFormat = NSLocalizedString("menu-langsettings-title", bundle: engineBundle, comment: "")
    title = String.localizedStringWithFormat(titleFormat, language.name)
    log.info("viewDidLoad: LanguageSettingsViewController title: \(title ?? "<empty>")")

    if Manager.shared.canAddNewKeyboards {
      let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self,
                                      action: #selector(self.addClicked))
      navigationItem.rightBarButtonItem = addButton
    }
  }
  
  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    log.info("didAppear: LanguageSettingsViewController")
  }
  
  
  // MARK: - Table view data source UITableViewDataSource

  override func numberOfSections(in tableView: UITableView) -> Int {
      return 2
  }

  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if 0 == section {
      // so far as I know, there is always at least one keyboard or else we won't show
      let kbct = language.keyboards!.count
      return kbct
    } else {
      return 3  // 4 in future when we can manage a user dictionary
    }
  }
  
  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
  }
  
  @objc
  func predictionSwitchValueChanged(source: UISwitch) {
    let value = source.isOn;
    let userDefaults = Storage.active.userDefaults
    userDefaults.set(predictSetting: value, forLanguageID: self.language.id)

    // Reactively set the corrections switch interactivity state.
    self.doCorrectionsSwitch?.isHidden = !value
    self.doCorrectionsLabel?.isEnabled = value
    self.correctionsCell?.isUserInteractionEnabled = value
    
    if let lm = Manager.shared.preferredLexicalModel(userDefaults, forLanguage: self.language.id) {
      if Manager.shared.currentKeyboardID?.languageID == self.language.id {
        // re-register the model - that'll enact the settings.
        _ = Manager.shared.registerLexicalModel(lm)
      }
      // Based on how Manager chooses the model in setKeyboard.
    } else if let lm = userDefaults.userLexicalModels?.first(where: { $0.languageID == self.language.id }) {
      if Manager.shared.currentKeyboardID?.languageID == self.language.id {
        _ = Manager.shared.registerLexicalModel(lm)
      }
    }
  }
  
  @objc
  func correctionSwitchValueChanged(source: UISwitch) {
    let value = source.isOn;
    let userDefaults = Storage.active.userDefaults
    userDefaults.set(correctSetting: value, forLanguageID: self.language.id)
    
    if let lm = Manager.shared.preferredLexicalModel(userDefaults, forLanguage: self.language.id) {
      if Manager.shared.currentKeyboardID?.languageID == self.language.id {
        // re-register the model - that'll enact the settings.
        _ = Manager.shared.registerLexicalModel(lm)
      }
      // Based on how Manager chooses the model in setKeyboard.
    } else if let lm = userDefaults.userLexicalModels?.first(where: { $0.languageID == self.language.id }) {
      if Manager.shared.currentKeyboardID?.languageID == self.language.id {
        _ = Manager.shared.registerLexicalModel(lm)
      }
    }
  }

  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let userDefaults = Storage.active.userDefaults
    
    let cellIdentifier = 0 == indexPath.section ?  "KeyboardInLanguageSettingsCell" : "LanguageSettingsCell"
    
    let reusableCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
    if let cell = reusableCell {
      return cell
    }
    
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)
    if 1 == indexPath.section {
        if 0 == indexPath.row {
          cell.accessoryType = .none
          doPredictionsSwitch = UISwitch()
          doPredictionsSwitch!.translatesAutoresizingMaskIntoConstraints = false
          
          let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doPredictionsSwitch!.frame.size)
          doPredictionsSwitch!.frame = switchFrame
          
          doPredictionsSwitch!.isOn = userDefaults.predictSettingForLanguage(languageID: self.language.id)
          doPredictionsSwitch!.addTarget(self, action: #selector(self.predictionSwitchValueChanged), for: .valueChanged)
          cell.addSubview(doPredictionsSwitch!)
          cell.contentView.isUserInteractionEnabled = false

          doPredictionsSwitch!.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
          doPredictionsSwitch!.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true
        } else if 1 == indexPath.row {
          correctionsCell = cell
          cell.accessoryType = .none
          doCorrectionsSwitch = UISwitch()
          doCorrectionsSwitch!.translatesAutoresizingMaskIntoConstraints = false
          
          let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doCorrectionsSwitch!.frame.size)
          doCorrectionsSwitch!.frame = switchFrame
          
          doCorrectionsSwitch!.isOn = userDefaults.correctSettingForLanguage(languageID: self.language.id)
          doCorrectionsSwitch!.addTarget(self, action: #selector(self.correctionSwitchValueChanged), for: .valueChanged)
          cell.addSubview(doCorrectionsSwitch!)
          cell.contentView.isUserInteractionEnabled = false

          doCorrectionsSwitch!.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
          doCorrectionsSwitch!.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true

          // Disable interactivity if the prediction toggle is set to 'off'.
          doCorrectionsSwitch!.isHidden = !userDefaults.predictSettingForLanguage(languageID: self.language.id)
          cell.isUserInteractionEnabled = userDefaults.predictSettingForLanguage(languageID: self.language.id)
        } else { // rows 3 and 4
          cell.accessoryType = .disclosureIndicator
      }
    }
    let selectionColor = UIView()
    selectionColor.backgroundColor = Colors.selectionPrimary
    cell.selectedBackgroundView = selectionColor
    cell.textLabel?.font = cell.textLabel?.font?.withSize(16.0)
    return cell
  }
  
  // MARK: - UITableViewDelegate

  // fixed font style. use custom view (UILabel) if you want something different
  override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    var title: String
    switch (section) {
    case 0:
      // We do NOT use a general-use "keyboards" entry because different languages have different
      // pluralization rules, and it's a major assumption to assume which is preferred for general use.
      title = NSLocalizedString("menu-langsettings-section-keyboards", bundle: engineBundle, comment: "")
    case 1:
      title = NSLocalizedString("menu-langsettings-section-settings", bundle: engineBundle, comment: "")
    default:
      title = "unknown header"
    }
    return title
  }

  override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
  {
    if (section == 0 || section == 1)
    {
      return 32.0;
    }
    else
    {
      return CGFloat.leastNormalMagnitude;
    }
  }
  
  override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return CGFloat.leastNonzeroMagnitude
  }

  override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    
    cell.isUserInteractionEnabled = true
    cell.textLabel?.isEnabled = true
    cell.detailTextLabel?.isEnabled = true
    if 0 == indexPath.section { // keyboard list
      guard let keyboard = language.keyboards?[safe: indexPath.row] else {
        return
      }
      cell.textLabel?.text = keyboard.name
      cell.detailTextLabel?.text = keyboard.version + " " + keyboard.id

      cell.accessoryType = .disclosureIndicator
    } else { // language settings
      cell.accessoryType = .none
      switch indexPath.row {
        case 0:
          cell.textLabel?.text = NSLocalizedString("menu-langsettings-toggle-predict", bundle: engineBundle, comment: "")
        case 1:
          doCorrectionsLabel = cell.textLabel
          cell.textLabel?.text = NSLocalizedString("menu-langsettings-toggle-correct", bundle: engineBundle, comment: "")
          cell.textLabel?.isEnabled = !(doCorrectionsSwitch?.isHidden ?? false)
        case 2:
          cell.textLabel?.text = NSLocalizedString("menu-langsettings-label-lexical-models", bundle: engineBundle, comment: "")
          cell.accessoryType = .disclosureIndicator
          let modelCt = language.lexicalModels?.count ?? 0
          if modelCt != 1 {
            let formatString = NSLocalizedString("menu-langsettings-lexical-model-count", bundle: engineBundle, comment: "")
            cell.detailTextLabel?.text = String.localizedStringWithFormat(formatString, modelCt)
          } else {
            cell.detailTextLabel?.text = "\(language.lexicalModels![0].name)"
          }

        default:
          cell.textLabel?.text = NSLocalizedString("alert-error-title", bundle: engineBundle, comment: "")
      }
    }
  }
  
  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)
  }
  
  override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    if !Manager.shared.canRemoveKeyboards {
      return false
    }

    // No deletion of the settings toggles!
    if indexPath.section != 0 {
      return false
    }

    // Will we have at least one keyboard left somewhere if this one is deleted?  (Even if not same language)
    if Storage.active.userDefaults.userKeyboards?.count ?? 0 > 1 {
      return true
    }

    return false
  }
  
  override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle,
                          forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
      if let globalIndex = getKeyboardIndex(kb: (language.keyboards?[safe: indexPath.row])!) {
        if Manager.shared.removeKeyboard(at: globalIndex) {
          // For now, a pop-back will be sufficient.
          navigationController?.popToRootViewController(animated: true)
        }
      }
    }

    // Do nothing for now.
  }
  
  @objc func addClicked(_ sender: Any) {
    let keyboardSearchVC = KeyboardSearchViewController(languageCode: self.language.id,
                                                        keyboardSelectionBlock: KeyboardSearchViewController.defaultDownloadClosure() { result in
      switch result {
        case .cancelled:
          break
        case .error(let error):
          if let error = error {
            // Note: Errors may result from network issues.
            log.error(String(describing: error))
          }
        case .success(let package, let fullID):
          ResourceFileManager.shared.doInstallPrompt(for: package as! KeyboardKeymanPackage,
                                                     defaultLanguageCode: fullID.languageID,
                                                     in: self.navigationController!,
                                                     withAssociators: [.lexicalModels])
      }
    })

    navigationController!.pushViewController(keyboardSearchVC, animated: true)
  }

  
  private func performAction(for indexPath: IndexPath) {
    switch indexPath.section {
    case 0:
      showKeyboardInfoView(kb: (language.keyboards?[safe: indexPath.row])!)
    case 1:
      switch indexPath.row  {
        // case 0, 1:  the toggles - but a general 'click' not on the toggle itself.
        case 2:
          showLexicalModelsView()
        default:
          break
      }
    default:
      break
    }
  }
  
  func getKeyboardIndex(kb: Keyboard) -> Int? {
    let matchingFullID = FullKeyboardID(keyboardID: kb.id, languageID: language.id)
    let userData = Storage.active.userDefaults

    // If user defaults for keyboards list does not exist, do nothing.
    guard let globalUserKeyboards = userData.userKeyboards else {
      SentryManager.captureAndLog("no keyboards in the global keyboards list!")
      return nil
    }

    if let index = globalUserKeyboards.firstIndex(where: { $0.fullID == matchingFullID }) {
      guard index < globalUserKeyboards.count else {
        return nil
      }
      return index
    } else {
      let event = Sentry.Event(level: .debug)
      event.message = SentryMessage(formatted: "Keyboard index requested for uninstalled keyboard")
      event.extra = ["id": matchingFullID]
      SentrySDK.capture(event: event)

      log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!")
      return nil
    }
  }
  
  func showKeyboardInfoView(kb: Keyboard) {
    let matchingFullID = FullKeyboardID(keyboardID: kb.id, languageID: language.id)

    let userData = Storage.active.userDefaults

    // If user defaults for keyboards list does not exist, do nothing.
    guard let globalUserKeyboards = userData.userKeyboards else {
      SentryManager.captureAndLog("no keyboards in the global keyboards list!")
      return
    }

    if let index = getKeyboardIndex(kb: kb) {
      guard index < globalUserKeyboards.count else {
        return
      }
      let kbIndex:Int = index
      let thisKb = globalUserKeyboards[kbIndex]
      let mayDelete = mayDeleteKeyboard(keyboardIndex: index, keyboardCount: globalUserKeyboards.count)
      let infoView = ResourceInfoViewController(for: thisKb, mayDelete: mayDelete)
      navigationController?.pushViewController(infoView, animated: true)
    } else {
      let event = Sentry.Event(level: .debug)
      event.message = SentryMessage(formatted: "Keyboard index requested for uninstalled keyboard")
      event.extra = ["id": matchingFullID]
      SentrySDK.capture(event: event)

      log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!")
      return
    }
  }

  private func mayDeleteKeyboard(keyboardIndex: Int, keyboardCount: Int) -> Bool {
    if !Manager.shared.canRemoveKeyboards {
      return false
    }

    if !Manager.shared.canRemoveDefaultKeyboard {
      return keyboardIndex != 0
    }

    if keyboardIndex > 0 {
      return true
    }
    return keyboardCount > 1
  }

  func showLexicalModelsView() {
    //LanguageLexicalModelPickerViewController? (should show just the models for this language)
    let lmListView = LexicalModelPickerViewController(self.language)
    lmListView.language = self.language
    navigationController?.pushViewController(lmListView, animated: true)
  }
  
    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */
    
}
