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

import UIKit

class BluetoothOnboardingNewDevicesViewController: ModalViewController {
    var deviceID: String?
    var discoveredDevices = [IoTBluetoothDevice] ()
    
    private var filteredDevices = [IoTBluetoothDevice] ()
    private var observers = [ObserverToken] ()
    private var pairingDevice: IoTBluetoothDevice?
    private weak var pairingTimer: Timer?

    @IBOutlet private weak var tableView: UITableView!
}

extension BluetoothOnboardingNewDevicesViewController {
    override func viewDidLoad() {
        title = NSLocalizedString("DEVICES FOUND", comment: "Bluetooth Title")

        super.viewDidLoad()

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

        // Accessibility
        tableView?.makeAccessible(Accessibility.BluetoothDiscoveredDevicesScreen.listTitle)
    }

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

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

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

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

            if notification.device.deviceID == self.deviceID! &&
                notification.reason == .removed {
                self.presentingViewController?.dismiss(animated: true, completion: nil)
            }
        }

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


        let bluetoothHandler: (BluetoothNotification) -> () = { [weak self] notification in
            guard let self = self else { return }

            if notification.device.deviceID == self.deviceID! {
                switch notification.reason {
                case .adapterStateChanged:
                    // Having the adapter disable would be bad.
                    self.presentingViewController?.dismiss(animated: true, completion: nil)
                case .pairedDevicesChanged:
					self.handledDevicePaired()
                default:
                    break
                }
            }
        }

        let bluetoothObserver = NotificationCenter.default.addObserver(forType: BluetoothNotification.self,
                                                                       object: playerController,
                                                                       queue: OperationQueue.main,
                                                                       using: bluetoothHandler)

        observers = [deviceObserver, bluetoothObserver]
    }

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

    private func setUpRows() {
        guard
            let id = deviceID,
            let device = playerController.device(id: id) else {
                filteredDevices.removeAll()
                return
        }
        let pairedDevices = device.pairedBluetoothDevices
        filteredDevices = discoveredDevices.filter( { return !pairedDevices.contains($0) } )
    }

    private func handledDevicePaired() {
        appSpinnerPresenter()?.dismissWaitSpinner()
        reloadState()
        
        if let deviceID = deviceID,
            let device = playerController.device(id: deviceID) {
            // Chance that device not one we asked for? In any case stop pairing animation if we're waiting
            if let timer = pairingTimer {
                timer.invalidate()
                pairingTimer = nil
            }
            if let pairing = pairingDevice {
                if device.pairedBluetoothDevices.contains(pairing) {
                    let alert = UIAlertController(title: NSLocalizedString("Pairing Successful", comment: "BT Pairing success title"),
                                                  message: NSLocalizedString("The device has now been successfully paired.", comment: "BT Pairing success message"),
                                                  preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "OK"),
                                                  style: .default,
                                                  handler: { [weak self] _ in
                                                    guard let self = self else {
                                                        return
                                                    }
                                                    self.reloadState()
                    }))
                    present(alert, animated: true)
                }
            }
        }
    }

    @objc private func pairingTimerExpired(_ timer: Timer) {
        pairingTimer = nil
        pairingDevice = nil
        appSpinnerPresenter()?.dismissWaitSpinner()
        let alert = UIAlertController(title: NSLocalizedString("Pairing Failed", comment: "BT Pairing fail title"),
                                      message: NSLocalizedString("The device could not be paired.", comment: "BT Pairing  fail message"),
                                      preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "OK"),
                                      style: .default,
                                      handler: { [weak self] _ in
                                        guard let self = self else {
                                            return
                                        }
                                        self.reloadState()
        }))
        present(alert, animated: true)
    }
}

extension BluetoothOnboardingNewDevicesViewController: UITableViewDelegate, UITableViewDataSource {
    // MARK: - Table view data source

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

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

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

        var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier) as? SettingsOneRowTableViewCell
        if cell == nil {
            cell = SettingsOneRowTableViewCell()
        }

        cell?.style = .plain
        cell?.accessoryType = .none
        let itemString = btDevice.name.count > 0 ? btDevice.name : btDevice.address
        cell?.titleLabel?.text = itemString
        cell?.makeAccessible(itemString, doNotLocalizeForHuman: true)
        return cell!
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return CGFloat.leastNormalMagnitude
    }

    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 btDevice = filteredDevices[indexPath.row]
		pairingDevice = btDevice
		device.startPairBtDevice(btDevice,
                                 completion: nil,
                                 completionQueue: nil)

        let timer = Timer(timeInterval: 30,
                          target: self,
                          selector: #selector(pairingTimerExpired(_:)),
                          userInfo: nil,
                          repeats: false)
        RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
        pairingTimer = timer
        appSpinnerPresenter()?.presentWaitSpinner()
    }
}
