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

import UIKit

class ZigbeeOverviewSettingsViewController: UITableViewController {
    var deviceID: String?

    // MARK: Private Types
    private struct Segues {
        static let showZBJoining = "showZBJoining"
        static let showZBDeviceNaming = "showZBDeviceNaming"
    }

    private enum RowType {
        case enabled
        case coordinator
        case joinedDevice
        case startJoining
    }

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

    private struct Section {
        var title: String?
        var rows: [Row]
    }

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

    private var zbJoinedDevices = [IoTZigbeeDevice]()
}

extension ZigbeeOverviewSettingsViewController {
    override func viewDidLoad() {
        title = NSLocalizedString("SMARTHOME DEVICES", comment: "SMARTHOME 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.ZigbeeSettingsScreen.listTitle)
    }

    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? ZigbeeOnboardingViewController {
                top.injectDependencies(preferences: preferences, playerController: playerController)
                top.deviceID = deviceID
            }
        }

        if let speakerNamingVC = segue.destination as? SpeakerNameViewController,
            let zbDevice = sender as? IoTZigbeeDevice {
            speakerNamingVC.injectDependencies(preferences: preferences, playerController: playerController)
            speakerNamingVC.deviceID = deviceID
            speakerNamingVC.zigbeeDeviceID = zbDevice.identifier
        	speakerNamingVC.isRenamingZigbee = true
            speakerNamingVC.title = NSLocalizedString("DEVICE NAME", comment: "View title")
        }
    }
}

extension ZigbeeOverviewSettingsViewController {
    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 ZigbeeOverviewSettingsViewController {
    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 zigbeeHandler: (ZigbeeNotification) -> () = { [weak self] notification in
            guard let self = self else { return }
			self.cancelTimeoutTimer()
            self.reloadState()
        }

        let zigbeeObserver = NotificationCenter.default.addObserver(forType: ZigbeeNotification.self,
                                                                    object: playerController,
                                                                    queue: OperationQueue.main,
                                                                    using: zigbeeHandler)

        observers = [deviceObserver, zigbeeObserver]
    }

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

    private func setUpRows() {
        sections = [Section] ()

        guard
            let deviceID = deviceID,
            let device = playerController.device(id: deviceID) else {
                return
        }
        var generalRows = [Row] ()
        let enabledRow = Row(type: .enabled,
                             title: NSLocalizedString("Zigbee", comment: "Bluetooth enabled cell"),
                             subtitle: nil)
        generalRows.append(enabledRow)

        if device.zigbeeEnabled == .enabled {

            let coordinatorRow = Row(type: .coordinator,
                                     title: NSLocalizedString("Coordinator", comment: "ZB Coordinator cell"),
                                     subtitle: nil)
            generalRows.append(coordinatorRow)

        }
        let generalSection = Section(title: nil,
                                     rows: generalRows)

        sections.append(generalSection)

        if device.zigbeeEnabled == .enabled {
            zbJoinedDevices = device.zigbeeDevices
            let deviceRows = zbJoinedDevices.map { (zbDevice) -> Row in
                return Row(type: .joinedDevice,
                           title: zbDevice.name,
                           subtitle: nil)
            }

            if deviceRows.count > 0 {
                let deviceSection = Section(title: NSLocalizedString("PAIRED DEVICES (Tap to rename)", comment: "Section header"),
                                            rows: deviceRows)
                sections.append(deviceSection)
            }

            let startPairingRow = Row(type: .startJoining,
                                      title: NSLocalizedString("ADD ZIGBEE DEVICES", comment: "Pair ZB cell"),
                                      subtitle: nil)
            let startPairingSection = Section(title: NSLocalizedString("", comment: ""),
                                              rows: [startPairingRow])
            sections.append(startPairingSection)
        }
    }
}

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

    override func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }

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

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

        let section = sections[indexPath.section]
        let row = section.rows[indexPath.row]

        switch row.type {
        case .enabled:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }
            cell?.style = .toggle
            cell?.setToggle(isOn: device.zigbeeEnabled == .enabled)
            cell?.makeAccessible(device.zigbeeEnabled == .enabled ?
                Accessibility.ZigbeeSettingsScreen.zigbeeEnabled :
                Accessibility.ZigbeeSettingsScreen.zigbeeDisabled)
            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.setZbAdapterEnabled(newState,
                                           completion: nil,
                                           completionQueue: nil)
            }
            cell?.accessoryType = .none
            cell?.titleLabel?.textColor = ColorCompat.label
            cell?.titleLabel?.text = row.title
            return cell!

        case .coordinator:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }
            cell?.style = .plain
            cell?.accessoryType = device.zigbeeCoordinatorState == .nominated ? .checkmark : .none
            cell?.titleLabel?.text = row.title
            cell?.makeAccessible(device.zigbeeCoordinatorState == .nominated ?
                Accessibility.ZigbeeSettingsScreen.coordinatorSelected :
                Accessibility.ZigbeeSettingsScreen.coordinatorNotSelected)
            return cell!
        case .joinedDevice:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }
            cell?.style = .plain
            cell?.accessoryType = .disclosureIndicator
            cell?.titleLabel?.text = row.title
            cell?.titleLabel?.textColor = ColorCompat.label
            cell?.makeAccessible(row.title, doNotLocalizeForHuman: true)
            return cell!
        case .startJoining:
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }
            cell?.style = .plain
            cell?.accessoryType = .none
            cell?.titleLabel?.text = row.title
            cell?.titleLabel?.textColor = UIColor.blue
            cell?.makeAccessible(Accessibility.ZigbeeSettingsScreen.addDeviceButton)
            return cell!
        }
    }

    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        let section = sections[section]
        return section.title == nil ? CGFloat.leastNormalMagnitude : tableViewHeaderHeight
    }

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

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        guard let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: SettingsTableViewHeaderView.reuseIdentifier) as? SettingsTableViewHeaderView else {
            return nil
        }

        header.titleLabel?.text = sections[section].title
        return header
    }

    override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        return indexPath.section > 0 ? indexPath : nil
    }

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

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

        let section = sections[indexPath.section]
        let row = section.rows[indexPath.row]

        switch row.type {
        case .startJoining:
            doStartJoiningChecks()
        case .joinedDevice:
            startDeviceNaming(zbJoinedDevices[indexPath.row])
        default:
            break
        }
    }
}

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

        if device.zigbeeCoordinatorState == .nominated {
            startJoining()
            return
        }

        if playerController.zigbeeCoordinatorExists() {
            // Not allowed
			let controller = UIAlertController(title: NSLocalizedString("Zigbee Coordinator Exists", comment: "ZB Coordinator error"),
                                               message: NSLocalizedString("You may only have one coordinator per network. You will need to reset the other coordinator before nominating another.",
                                                                          comment:"ZB Coordinator error message"),
                                               preferredStyle: .alert)
            let action = UIAlertAction(title: NSLocalizedString("OK", comment: "OK"),
                                       style: .default,
                                       handler: nil)
            controller.addAction(action)
            present(controller, animated: true)
        } else {
            let controller = UIAlertController(title: NSLocalizedString("Make Smarthome Coordinator?", comment: "ZB Coordinator nomination"),
                                               message: NSLocalizedString("This device will serve as the central hub that connects to all your smarthome devices.\nYou may only have one Zigbee coordinator per network.",
                                                                          comment:"ZB Coordinator nomination message"),
                                               preferredStyle: .alert)
            let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: "Cancel"),
                                             style: .cancel,
                                             handler: nil)
            controller.addAction(cancelAction)
            let okAction = UIAlertAction(title: NSLocalizedString("OK", comment: "OK"),
                                       style: .default,
                                       handler: { [weak self] (action) in
                                        guard let self = self else { return }
                                        self.startJoining()
            })
            controller.addAction(okAction)
            present(controller, animated: true)
        }
    }

    private func startJoining() {
        performSegue(withIdentifier: Segues.showZBJoining, sender: self)
    }

    private func startDeviceNaming(_ device: IoTZigbeeDevice) {
		performSegue(withIdentifier: Segues.showZBDeviceNaming, sender: device)
    }
}
