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

import UIKit

class VoiceUIOverviewSettingsViewController: UITableViewController {
    var deviceID: String?

    // MARK: Private Types
    private struct Segues {
        static let showServiceSettings = "showServiceSettings"
        static let showAVSOnboarding = "showAVSOnboarding"
        static let showDefaultService = "showDefaultService"
    }

    private enum RowType {
        case globalEnabled
        case avsState
        case gvaState
        case cortanaState
        case modularState
        case defaultService
    }

    private struct Row {
        let type: RowType
        let unlocalizedTitle: String
        let unlocalizedSubtitle: String?
    }

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

extension VoiceUIOverviewSettingsViewController {
    override func viewDidLoad() {
        title = NSLocalizedString("VOICE CONTROL", comment: "Voice UI Title")

        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.VoiceUISettingsScreen.masterSettingsList)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        setUpObservers()
        reloadState()
    }

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

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let navVC = segue.destination as? UINavigationController {
            if let top = navVC.topViewController as? AVSOnboardingIntroViewController {
                top.injectDependencies(preferences: preferences, playerController: playerController)
                top.deviceID = deviceID
            }
        } else if let vc = segue.destination as? VoiceUIDefaultServiceViewController {
            vc.injectDependencies(preferences: preferences, playerController: playerController)
            vc.deviceID = deviceID
        } else if let vc = segue.destination as? VoiceUIServiceSettingsViewController {
            vc.injectDependencies(preferences: preferences, playerController: playerController)
            vc.deviceID = deviceID
            vc.service = sender as? IoTVoiceUIClient
        }
    }
}

extension VoiceUIOverviewSettingsViewController {
    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 VoiceUIOverviewSettingsViewController {
    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 }
			self.cancelTimeoutTimer()
            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) else {
                return
        }

        let voiceServices = device.voiceUIClients
        let serviceRows = voiceServices.compactMap { (service) -> Row? in
            let name = VoiceUIHelper.unlocalizedNameForService(service)
            let subtitle = VoiceUIHelper.unlocalizedStateForService(service)
            switch service.type {
            case .AVS:
                return Row(type: .avsState,
                           unlocalizedTitle: name,
                           unlocalizedSubtitle: subtitle)
            case .GVA:
                return Row(type: .gvaState,
                           unlocalizedTitle: name,
                           unlocalizedSubtitle: subtitle)
            case .cortana:
                return Row(type: .cortanaState,
                           unlocalizedTitle: name,
                           unlocalizedSubtitle: subtitle)
            case .modular:
                return Row(type: .modularState,
                           unlocalizedTitle: name,
                           unlocalizedSubtitle: subtitle)
            case .unknown:
                return nil
            @unknown default:
                fatalError()
            }
        }

 /*
// Unsupported in CS1.0
         let enabledRow = Row(type: .globalEnabled,
                             title: NSLocalizedString("Microphone", comment: "Microphone cell"),
                             subtitle: nil)

        rows.append(enabledRow) */
        rows.append(contentsOf: serviceRows)

        if let defaultService = device.defaultVoiceUIClient,
            voiceServices.count > 0 {
            let defaultServiceRow = Row(type: .defaultService,
                                        unlocalizedTitle: Accessibility.VoiceUISettingsScreen.defaultServiceRow,
                                        unlocalizedSubtitle: VoiceUIHelper.unlocalizedNameForService(defaultService))
            rows.append(defaultServiceRow)
        }
    }
}

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

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

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return rows.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let row = rows[indexPath.row]

        switch row.type {
        case .globalEnabled:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }
            cell?.style = .toggle
            if let deviceID = deviceID,
                let device = playerController.device(id: deviceID) {
                cell?.setToggle(isOn: device.voiceUIEnabled)
            }
            cell?.onToggle = { [weak self] newState in
                guard
                    let self = self,
                    let deviceID = self.deviceID,
                    let device = self.playerController.device(id: deviceID) else {
                        return
                }
                cell?.style = .waiting
				self.startTimeoutTimer()
                device.setVoiceUIEnabled(newState,
                                         completion: nil,
                                         completionQueue: nil)

            }
            cell?.accessoryType = .none
            cell?.titleLabel?.text = NSLocalizedString(row.unlocalizedTitle, comment: "Enabled Row")
            return cell!
        case .avsState,
             .gvaState,
             .cortanaState,
             .modularState:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsTwoRowTableViewCell.reuseIdentifier) as? SettingsTwoRowTableViewCell
            if cell == nil {
                cell = SettingsTwoRowTableViewCell()
            }
            cell?.style = .plain
            cell?.accessoryType = row.type == .avsState || row.type == .modularState ? .disclosureIndicator : .none // Others currently not supported.
            cell?.titleLabel?.text = NSLocalizedString(row.unlocalizedTitle, comment: "Cell row")
            cell?.subtitleLabel?.text = NSLocalizedString(row.unlocalizedSubtitle!, comment: "Cell row")
            cell?.makeAccessible(row.unlocalizedTitle, second: row.unlocalizedSubtitle!)
            return cell!
        case .defaultService:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsTwoRowTableViewCell.reuseIdentifier) as? SettingsTwoRowTableViewCell
            if cell == nil {
                cell = SettingsTwoRowTableViewCell()
            }
            cell?.style = .plain
            cell?.accessoryType = .disclosureIndicator
            cell?.titleLabel?.text = NSLocalizedString(row.unlocalizedTitle, comment: "Cell row")
            cell?.subtitleLabel?.text = NSLocalizedString(row.unlocalizedSubtitle!, comment: "Cell row")
            cell?.makeAccessible(row.unlocalizedTitle, second: row.unlocalizedSubtitle!)
            return cell!
        }
    }

    override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if let cell = tableView.cellForRow(at: indexPath) {
            return cell.accessoryType == .disclosureIndicator ? indexPath : nil
        }
        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 device = playerController.device(id: deviceID) else {
            return
        }

        let row = rows[indexPath.row]
        switch row.type {
        case .avsState:
            if let avsService = device.voiceUIClients.first(where: { return $0.type == .AVS }) {
                if avsService.onboarded {
                    performSegue(withIdentifier: Segues.showServiceSettings, sender: avsService)
                } else {
					performSegue(withIdentifier: Segues.showAVSOnboarding, sender: self)
                }
            }
        case .modularState:
            if let modularService = device.voiceUIClients.first(where: { return $0.type == .modular }) {
            	performSegue(withIdentifier: Segues.showServiceSettings, sender: modularService)
            }
        case .defaultService:
            performSegue(withIdentifier: Segues.showDefaultService, sender: self)
        default:
            break
        }
    }
}
