//
// © 2018-2020 Qualcomm Technologies, Inc. and/or its subsidiaries. All rights reserved.
//

import UIKit

class VoiceUIServiceSettingsViewController: UITableViewController {
    var deviceID: String?
    var service: IoTVoiceUIClient?

    // MARK: Private Types
    private struct Segues {
        static let showAVSCountry = "showAVSCountry"
        static let showModularAlertSound = "showModularAlertSound"
        static let showModularLanguage = "showModularLanguage"
    }

    private enum RowType {
        case hotwordEnabled
        case avsCountry
        case avsSignOut
        case modularAlertSound
        case modularLanguage
    }

    private struct Row {
        let type: RowType
        let title: String
        let subtitle: String?
    }

    // MARK: Private Properties

    private weak var timeoutTimer: Timer?
    private var observers = [ObserverToken] ()
    private let tableViewHeaderHeight: CGFloat = 38.0
    private var rows = [Row] ()
}

extension VoiceUIServiceSettingsViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let nib1 = UINib(nibName: SettingsOneRowTableViewCell.reuseIdentifier, bundle: nil)
        tableView?.register(nib1, forCellReuseIdentifier: SettingsOneRowTableViewCell.reuseIdentifier)

        let nib2 = UINib(nibName: SettingsTwoRowTableViewCell.reuseIdentifier, bundle: nil)
        tableView?.register(nib2, forCellReuseIdentifier: SettingsTwoRowTableViewCell.reuseIdentifier)

        let nib = UINib(nibName: SettingsTableViewHeaderView.reuseIdentifier, bundle: nil)
        tableView?.register(nib, forHeaderFooterViewReuseIdentifier: SettingsTableViewHeaderView.reuseIdentifier)

        // Accessibility
        tableView.makeAccessible(Accessibility.VoiceUIServiceSettingsScreen.serviceSettingsList)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        title = NSLocalizedString(VoiceUIHelper.unlocalizedNameForService(service!), comment: "Service Name").uppercased()
        setUpObservers()
        reloadState()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        removeObservers()
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        segue.destination.injectDependencies(preferences: preferences, playerController: playerController)
        if let vc = segue.destination as? AVSCountrySelectionViewController {
            vc.deviceID = self.deviceID
        } else if let vc = segue.destination as? ModularLanguageSelectionViewController {
            vc.deviceID = self.deviceID
        } else if let vc = segue.destination as? ModularAlertSoundSelectionViewController {
            vc.deviceID = self.deviceID
        }
    }
}

extension VoiceUIServiceSettingsViewController {
    private func cancelTimeoutTimer() {
        if let t = timeoutTimer {
            t.invalidate()
            timeoutTimer = nil
        }
    }

    private func startTimeoutTimer() {
        cancelTimeoutTimer()
        let t = Timer(timeInterval: 10,
                      target: self,
                      selector: #selector(timeoutTimerFired(_:)),
                      userInfo: nil,
                      repeats: false)
        RunLoop.current.add(t, forMode: .common)
        timeoutTimer = t
    }

    @objc private func timeoutTimerFired(_: Any?) {
        timeoutTimer = nil
        reloadState()
    }
}

extension VoiceUIServiceSettingsViewController {
    private func reloadState() {
        setUpRows()
        tableView.reloadData()
    }

    private func setUpObservers() {
        let deviceHandler: (DeviceNotification) -> () = { [weak self] notification in
            guard let self = self else { return }

            if notification.reason == .removed {
                self.reloadState()
            }
        }

        let deviceObserver = NotificationCenter.default.addObserver(forType: DeviceNotification.self,
                                                                    object: playerController,
                                                                    queue: OperationQueue.main,
                                                                    using: deviceHandler)
        // ************


        let voiceUIHandler: (VoiceUINotification) -> () = { [weak self] notification in
            guard let self = self else { return }
            if let deviceID = self.deviceID,
                let device = self.playerController.device(id: deviceID) {
                let services = device.voiceUIClients
                self.service = services.first(where: { return $0 == self.service })
            } else {
                self.service = nil
            }
            self.reloadState()
        }

        let voiceUIObserver = NotificationCenter.default.addObserver(forType: VoiceUINotification.self,
                                                                     object: playerController,
                                                                     queue: OperationQueue.main,
                                                                     using: voiceUIHandler)

        observers = [deviceObserver, voiceUIObserver]
    }

    private func removeObservers () {
        observers.forEach {
            NotificationCenter.default.removeObserver($0)
        }
        observers.removeAll()
    }

    private func setUpRows() {
        rows = [Row] ()

        guard
            let deviceID = deviceID,
            let device = playerController.device(id: deviceID),
            let service = service else {
                return
        }

        let hotWordTitle = NSString.localizedStringWithFormat("\"%@\" hotword detection", VoiceUIHelper.unlocalizedWakeWordForService(service)) as String
        let hotwordRow = Row(type: .hotwordEnabled,
                             title: hotWordTitle,
                             subtitle: nil)
        rows.append(hotwordRow)

        switch service.type {
        case .AVS:
            let countryRow = Row(type: .avsCountry,
                                 title: NSLocalizedString("Country", comment: "Country Cell"),
                                 subtitle: device.selectedAVSLocale)
            rows.append(countryRow)

            if service.onboarded {
                let title = NSLocalizedString(Accessibility.VoiceUIServiceSettingsScreen.signOut,
                                              comment: "Sign Out Cell")
                let signOutRow = Row(type: .avsSignOut,
                                     title: title,
                                     subtitle: nil)
                rows.append(signOutRow)
            }
        case .modular:
            /*                   let alertSoundRow = Row(type: .modularAlertSound,
             title: NSLocalizedString("Alert Sound", comment: "Alert Sound Cell"),
             subtitle: device.modularClientAlertFile)
             rows.append(alertSoundRow) */
            if device.modularClientLanguages.count > 0 {
                let languageRow = Row(type: .modularLanguage,
                                      title: NSLocalizedString("Language", comment: "Language Cell"),
                                      subtitle: device.selectedModularClientLanguage)
                rows.append(languageRow)
            }
        case .GVA, .cortana, .unknown:
            break
        @unknown default:
            fatalError()
        }
    }
}


extension VoiceUIServiceSettingsViewController {
    // MARK: - Table view data source

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

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return rows.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard
            let deviceID = deviceID,
            let device = playerController.device(id: deviceID),
            let service = service
        else {
            return UITableViewCell()
        }

        let row = rows[indexPath.row]

        switch row.type {
        case .hotwordEnabled:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }
            cell?.style = .toggle
            cell?.setToggle(isOn: service.wakeWordEnabled)
            cell?.onToggle = { [weak self] newState in
                guard
                    let self = self,
                    let deviceID = self.deviceID,
                    let device = self.playerController.device(id: deviceID),
                    let service = self.service else {
                        return
                }
                cell?.style = .waiting
                self.startTimeoutTimer()
                device.setVoiceUIClient(service,
                                        wakeWordEnabled: newState,
                                        completion: nil,
                                        completionQueue: nil)
            }
            cell?.accessoryType = .none
            cell?.titleLabel?.text = row.title

            let enabledStr = NSLocalizedString(service.wakeWordEnabled ?
                Accessibility.VoiceUIServiceSettingsScreen.serviceEnabledSuffix :
                Accessibility.VoiceUIServiceSettingsScreen.serviceDisabledSuffix, comment: "state suffix")
            let wakeword = VoiceUIHelper.unlocalizedWakeWordForService(service)
            cell?.makeAccessible(wakeword, localizableSuffix: enabledStr)
            return cell!

        case .avsCountry,
             .modularAlertSound,
             .modularLanguage:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsTwoRowTableViewCell.reuseIdentifier) as? SettingsTwoRowTableViewCell
            if cell == nil {
                cell = SettingsTwoRowTableViewCell()
            }
            cell?.style = .plain
            cell?.accessoryType = .disclosureIndicator
            cell?.titleLabel?.text = row.title
            cell?.subtitleLabel?.text = row.subtitle

            if row.type == .avsCountry {
                cell?.makeAccessible(localizablePrefix: Accessibility.VoiceUIServiceSettingsScreen.countryPrefix, value: device.selectedAVSLocale)
            } else if row.type == .modularLanguage {
                cell?.makeAccessible(localizablePrefix: Accessibility.VoiceUIServiceSettingsScreen.languagePrefix, value: device.selectedModularClientLanguage ?? "")
            }
            return cell!
        case .avsSignOut:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsTwoRowTableViewCell.reuseIdentifier) as? SettingsTwoRowTableViewCell
            if cell == nil {
                cell = SettingsTwoRowTableViewCell()
            }
            cell?.style = .plain
            cell?.accessoryType = .disclosureIndicator
            cell?.titleLabel?.text = row.title
            cell?.subtitleLabel?.text = row.subtitle
			cell?.makeAccessible(Accessibility.VoiceUIServiceSettingsScreen.signOut)
            return cell!
        }
    }

    override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if let cell = tableView.cellForRow(at: indexPath) {
            if cell.accessoryType == .disclosureIndicator {
                return indexPath
            }
    		let row = rows[indexPath.row]
            if row.type == .avsSignOut {
                return indexPath
            }
        }
        return nil
    }

    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return CGFloat.leastNormalMagnitude // if we return zero it gets ignored.
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        guard
            let deviceID = deviceID,
            let _ = playerController.device(id: deviceID),
            let _ = service else {
                return
        }

        let row = rows[indexPath.row]
        switch row.type {
        case .modularLanguage:
            performSegue(withIdentifier: Segues.showModularLanguage, sender: self)
            break
        case .modularAlertSound:
            performSegue(withIdentifier: Segues.showModularAlertSound, sender: self)
            break
        case .avsCountry:
            performSegue(withIdentifier: Segues.showAVSCountry, sender: self)
        case .avsSignOut:
            confirmAVSSignOut()
        default:
            break
        }
    }

    private func confirmAVSSignOut() {
        let confirmatoryAlert = UIAlertController(title: NSLocalizedString("Sign Out?", comment: "AVS Sign Out"),
                                                  message: NSLocalizedString("Are you sure you want to remove your AVS login information from this device?", comment: "AVS Sign Out Message"),
                                                  preferredStyle: .actionSheet)
        confirmatoryAlert.addAction(UIAlertAction(title: NSLocalizedString("Sign Out", comment: "Sign out option"),
                                                  style: .destructive,
                                                  handler: { [weak self] _ in
                                                    guard let self = self else { return }
                                                    self.doAVSSignOut()
        } ))

        confirmatoryAlert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Sign out option"),
                                                  style: .cancel,
                                                  handler: nil))
        present(confirmatoryAlert,
                animated: true,
                completion: nil)
    }

    private func doAVSSignOut() {
        guard
            let deviceID = deviceID,
            let device = playerController.device(id: deviceID)
        else {
            return
        }

        device.removeAVSCredential(completion: { (success, error) in
            if success {
                let completedAlert = UIAlertController(title: NSLocalizedString("Reboot Required", comment: "AVS Sign Out"),
                                                       message: NSLocalizedString("Please reboot your device to complete the sign out process.", comment: "AVS Sign Out Message"),
                                                       preferredStyle: .actionSheet)
                completedAlert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Sign out option"),
                                                       style: .default,
                                                       handler: nil))
            }
        }, completionQueue: DispatchQueue.main)
    }
}
