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

import UIKit
import CocoaLumberjack

class SpeakerSettingsViewController: UITableViewController {
	var deviceID: String?
	
	// MARK: Private Types
	private struct Segues {
		static let showSpeakerNameEntry = "showSpeakerNameEntry"
        static let showVoiceUISettings = "showVoiceUISettings"
        static let showBluetoothSettings = "showBluetoothSettings"
        static let showZigbeeSettings = "showZigbeeSettings"
        static let showAudioSettings = "showAudioSettings"
	}
	
	private enum SectionType {
		case speaker
		case network
		case restartReset
	}
	
	private enum RowType {
		case speakerName
        case voiceControl
        case audioAdjustment
        case bluetooth
        case iotDevices

		case battery
		case wiredNetwork
        case wirelessNetworkSSID
		case signalStrength
		case ipAddress
		case macAddress
		case restart
		case reset
	}
	
	private struct Section {
		let type: SectionType
		let title: String
		let rows: [Row]
	}
	
	private struct Row {
		let type: RowType
		let title: String
        let accessibilityTitle: String
		let subtitle: String?
	}
	
	// MARK: Private Properties
	private var observers = [ObserverToken] ()
	private let tableViewHeaderHeight: CGFloat = 38.0
	private var sections = [Section] ()
    private var networkRefreshTimer: Timer?
}

// MARK: - Overridden Methods
extension SpeakerSettingsViewController {
	override func viewDidLoad() {
		super.viewDidLoad()
        if let deviceID = deviceID,
            let _ = playerController.device(id: deviceID) {
			title = NSLocalizedString("SETTINGS", comment: "Present Device")
        } else {
            title = NSLocalizedString("UNAVAILABLE", comment: "Missing Device")
        }
		
		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 nib3 = UINib(nibName: SettingsSignalStrengthTableViewCell.reuseIdentifier, bundle: nil)
		tableView?.register(nib3, forCellReuseIdentifier: SettingsSignalStrengthTableViewCell.reuseIdentifier)
		
		let nib = UINib(nibName: SettingsTableViewHeaderView.reuseIdentifier, bundle: nil)
		tableView?.register(nib, forHeaderFooterViewReuseIdentifier: SettingsTableViewHeaderView.reuseIdentifier)

		// Accessibility
		tableView?.makeAccessible(Accessibility.MainSettingsScreen.settingsList)
    }
		
	override func viewWillAppear(_ animated: Bool) {
		super.viewWillAppear(animated)
		// No title for back button to centre next title
		let bbi = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
		navigationItem.backBarButtonItem = bbi
		
		setUpObservers()
		
		reloadState()
	}
	
	override func viewDidDisappear(_ animated: Bool) {
		super.viewDidDisappear(animated)
		
		observers.forEach { NotificationCenter.default.removeObserver($0) }
		observers = []

        networkRefreshTimer?.invalidate()
	}
	
	override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
		segue.destination.injectDependencies(preferences: preferences,
                                             playerController: playerController)

        if let speakerNamingVC = segue.destination as? SpeakerNameViewController {
            speakerNamingVC.deviceID = deviceID
            speakerNamingVC.title = NSLocalizedString("DEVICE NAME", comment: "View title")
        }

        if let voiceUIVC = segue.destination as? VoiceUIOverviewSettingsViewController {
            voiceUIVC.deviceID = deviceID
        }

        if let btVC = segue.destination as? BluetoothOverviewSettingsViewController {
            btVC.deviceID = deviceID
        }

        if let audioVC = segue.destination as? AudioSettingsTableViewController {
            let viewModel = AudioSettingsIntroViewModel()
            if
                let deviceID = deviceID,
                let device = playerController.device(id: deviceID),
            	let player = playerController.player(device: device) {
				viewModel.playerID = player.playerID
            }

            viewModel.playerController = playerController
            viewModel.preferences = preferences
            audioVC.viewModel = viewModel
            viewModel.viewController = audioVC
        }

        if let zbVC = segue.destination as? ZigbeeOverviewSettingsViewController {
            zbVC.deviceID = deviceID
        }
	}
}

// MARK: - Overridden UITableViewViewController Methods
extension SpeakerSettingsViewController {
	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 {
		let row = sections[indexPath.section].rows[indexPath.row]
		if row.subtitle != nil {
			// Two Row
			var cell = tableView.dequeueReusableCell(withIdentifier: SettingsTwoRowTableViewCell.reuseIdentifier, for: indexPath) as? SettingsTwoRowTableViewCell
			if cell == nil {
				cell = SettingsTwoRowTableViewCell()
			}
			
			cell!.titleLabel?.text = row.title
            cell!.subtitleLabel?.text = row.subtitle
            if row.type == .wirelessNetworkSSID {
                cell?.makeAccessible(localizablePrefix: Accessibility.MainSettingsScreen.wirelessNetworkSSIDPrefix,
                                     value: row.subtitle!)
            } else if row.type == .ipAddress {
				cell?.makeAccessible(localizablePrefix: Accessibility.MainSettingsScreen.ipAddressPrefix,
                                     value: row.subtitle!)
            } else if row.type == .macAddress {
                cell?.makeAccessible(localizablePrefix: Accessibility.MainSettingsScreen.macAddressPrefix,
                                     value: row.subtitle!)
            } else {
            	cell?.makeAccessible(row.accessibilityTitle)
            }
			
            if row.type == .speakerName  ||
                row.type == .bluetooth ||
                row.type == .iotDevices {
				cell!.selectionStyle = .gray
				cell?.accessoryType = .disclosureIndicator
			} else {
				cell!.selectionStyle = .none
				cell?.accessoryType = .none
			}
			return cell!
		} else if row.type == .signalStrength {
			var cell = tableView.dequeueReusableCell(withIdentifier: SettingsSignalStrengthTableViewCell.reuseIdentifier, for: indexPath) as? SettingsSignalStrengthTableViewCell
			if cell == nil {
				cell = SettingsSignalStrengthTableViewCell()
			}
			
			cell!.titleLabel?.text = row.title
            if let deviceID = deviceID,
                let device = playerController.device(id: deviceID) {
                let wifiQuality = device.wifiQuality(UInt32(cell!.numberOfSignalLevelIcons))
                cell?.signalStrength = UInt(wifiQuality)
                cell?.makeAccessible(localizablePrefix: Accessibility.MainSettingsScreen.wifiSignalStrengthPrefix, value: "\(wifiQuality)")
            }
			cell?.selectionStyle = .none
			return cell!
		} else {
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier, for: indexPath) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }

            cell!.titleLabel?.text = row.title
            cell?.makeAccessible(row.accessibilityTitle)
            cell?.selectionStyle = .gray

            if row.type == .voiceControl ||
                row.type == .audioAdjustment ||
                row.type == .bluetooth ||
                row.type == .iotDevices {
                cell?.accessoryType = .disclosureIndicator
            }


            cell!.style = .plain
            return cell!
        }
    }

	override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
		tableView.deselectRow(at: indexPath, animated: false)
		let row = sections[indexPath.section].rows[indexPath.row]
		
		switch row.type {
		case .speakerName:
			editSpeakerName()
//		case .checkNow:
//			checkForUpdatesNow()
		case .restart:
			requestedRestart()
		case .reset:
			requestedReset()
		case .audioAdjustment:
			showAdjustAudio()
        case .voiceControl:
            showVoiceControl()
        case .bluetooth:
            showBluetooth()
        case .iotDevices:
            showIoTDevices()
        default:
            break
        }
	}
	
	override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
		let cell = tableView.cellForRow(at: indexPath)
        return cell?.selectionStyle == UITableViewCell.SelectionStyle.none ? nil : indexPath
	}
	
	override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
		return tableViewHeaderHeight
	}
	
	override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
		return CGFloat.leastNormalMagnitude // if we return zero it gets ignored.
	}
	
	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, canEditRowAt indexPath: IndexPath) -> Bool {
		// Return false if you do not want the specified item to be editable.
		return false
	}
}

// MARK: - Private Methods
extension SpeakerSettingsViewController {
	private func setUpObservers() {
		let deviceHandler: (DeviceNotification) -> () = { [weak self] notification in
			guard
                let self = self,
                let deviceID = self.deviceID,
                let _ = self.playerController.device(id: deviceID),
                deviceID == notification.device.deviceID
            else {
            	return
            }

            if notification.reason == .accessPointStateChanged {
                if let networkSectionIndex = self.sections.firstIndex(where: { return $0.type == .network }) {
					let networkSection = self.sections[networkSectionIndex]
                    var rows = [IndexPath] ()
                    if let ssidRow = networkSection.rows.firstIndex(where: { return $0.type == .wirelessNetworkSSID }) {
                        rows.append(IndexPath(row: ssidRow, section: networkSectionIndex))
                    }

                    if let strengthRow = networkSection.rows.firstIndex(where: { return $0.type == .signalStrength }) {
                        rows.append(IndexPath(row: strengthRow, section: networkSectionIndex))
                    }

                    if rows.count > 0 {
                    	self.tableView.reloadRows(at: rows, with: .none)
                    }
                }
            } else {
				self.reloadState()
            }
		}
		
		let deviceObserver = NotificationCenter.default.addObserver(forType: DeviceNotification.self,
																	object: playerController,
																	queue: OperationQueue.main,
																	using: deviceHandler)

		observers = [deviceObserver]
	}
	
	// MARK: TableView Setup
	private func reloadState() {
		guard
            let deviceID = deviceID,
            let dev = playerController.device(id: deviceID) else {
			title = NSLocalizedString("UNAVAILABLE", comment: "Missing Device")
			sections = []
			tableView.reloadData()
			return
		}
		
		title = NSLocalizedString("SETTINGS", comment: "Present Device")
		
		var newSections = [Section] ()
		newSections.append(speakerSection(device: dev))
        if dev.networkSupported && dev.networkInterface != .none {
			newSections.append(networkSection(device: dev))
        }
        
//		newSections.append(restartResetSection(device: dev))
		
		sections = newSections
		
		tableView.reloadData()
	}
	
	private func speakerSection(device: IoTDevice) -> Section {
		var speakerRows = [Row]()
        var name = device.displayName
        if name.isEmpty {
            if let p = playerController.player(device: device) {
                DDLogVerbose("Using device name from allplay!!!")
                name = p.displayName
            }
        }
		let nameRow = Row(type: .speakerName,
                          title: NSLocalizedString("Speaker Name", comment: "Name TV Row"),
                          accessibilityTitle: Accessibility.MainSettingsScreen.speakerName,
                          subtitle: name)
        speakerRows.append(nameRow)

        if device.voiceUISupported {
            let voiceControlRow = Row(type: .voiceControl,
                                      title: NSLocalizedString("Voice Control", comment: "Voice Control cell"),
                                      accessibilityTitle: Accessibility.MainSettingsScreen.voiceControl,
                                      subtitle: nil)
            speakerRows.append(voiceControlRow)
        }

        let adjustAudioRow = Row(type: .audioAdjustment,
                                 title: NSLocalizedString("Audio Settings", comment: "Audio Settings cell"),
                                 accessibilityTitle: Accessibility.MainSettingsScreen.audioSettings,
                                 subtitle: nil)
        speakerRows.append(adjustAudioRow)

        if device.bluetoothSupported {
            var subtitle: String? = nil
            if device.bluetoothEnabled == .enabled {
                subtitle = device.pairedBluetoothDevices.count > 0 ?
                    NSString.localizedStringWithFormat("%ld Paired Devices", device.pairedBluetoothDevices.count) as String :
                    NSLocalizedString("No Paired Devices", comment: "No Devices Connected")
            }

            let bluetoothRow = Row(type: .bluetooth,
                                   title: NSLocalizedString("Bluetooth Devices", comment: "Bluetooth Devices cell"),
                                   accessibilityTitle: Accessibility.MainSettingsScreen.bluetooth,
                                   subtitle: subtitle)
            speakerRows.append(bluetoothRow)
        }

        if device.zigbeeSupported {
            var subtitle: String? = nil
            if device.zigbeeEnabled == .enabled {
                subtitle = device.zigbeeDevices.count > 0 ?
                    NSString.localizedStringWithFormat("%ld Devices Connected", device.zigbeeDevices.count) as String :
                    NSLocalizedString("No Devices Connected", comment: "No Devices Connected")
            }
            
            let zigbeeRow = Row(type: .iotDevices,
                                title: NSLocalizedString("Smarthome Devices", comment: "Smarthome Devices cell"),
                                accessibilityTitle: Accessibility.MainSettingsScreen.zigbee,
                                subtitle: subtitle)
            speakerRows.append(zigbeeRow)
        }

        if device.isBatteryPowerSupported {
            var batteryText = ""
            var accessibilityText = ""
			if device.onBatteryPower {
				let chargeLevel = device.chargeLevel
                batteryText = String.localizedStringWithFormat("Charge Level: %d%%", chargeLevel)
                accessibilityText = Accessibility.MainSettingsScreen.poweredByBattery
			} else {
				batteryText = NSLocalizedString("Using AC Power", comment: "Battery TV row subtitle")
                accessibilityText = Accessibility.MainSettingsScreen.poweredByAC
			}
			let batteryRow = Row(type: .battery,
								 title: NSLocalizedString("Battery", comment: "Battery TV Row"),
                                 accessibilityTitle: accessibilityText,
								 subtitle: batteryText)
			speakerRows.append(batteryRow)
		}
		return Section(type: .speaker,
					   title: NSLocalizedString("SPEAKER SETTINGS", comment: "Section header"),
					   rows: speakerRows)
	}

    @objc private func sendNetworkRefresh(_ : Timer) {
        guard
            let deviceID = deviceID,
            let dev = playerController.device(id: deviceID)
        else {
            return
        }
		dev.updateNetworkInfo(completion: nil, completionQueue: nil) // Should trigger notification.
    }
	
	private func networkSection(device: IoTDevice) -> Section {
		var networkRows = [Row]()
		let interface = device.networkInterface
		var ipAddress: String
		var macAddress: String
		
		if interface == .wiFi {
			let typeRow = Row(type: .wirelessNetworkSSID,
							  title: NSLocalizedString("Wireless Network", comment: "Network TV Row"),
                              accessibilityTitle: "",
							  subtitle: device.wifiSSID)
			networkRows.append(typeRow)
			
			ipAddress = device.wifiIPAddress
			macAddress = device.wifiMacAddress
			let signalRow = Row(type: .signalStrength,
								title: NSLocalizedString("Signal Strength", comment: "Signal TV Row"),
                                accessibilityTitle: "", // Determined later.
								subtitle: nil)
			networkRows.append(signalRow)

            if !(networkRefreshTimer?.isValid ?? false) {
                let t = Timer(timeInterval: 5,
                              target: self,
                              selector: #selector(sendNetworkRefresh(_ :)),
                              userInfo: nil,
                              repeats: true)
                RunLoop.current.add(t, forMode: .common)
                networkRefreshTimer = t;
            }
		} else {
            networkRefreshTimer?.invalidate()
			let typeRow = Row(type: .wiredNetwork,
							  title: NSLocalizedString("Wired Network", comment: "Network TV Row"),
                              accessibilityTitle: Accessibility.MainSettingsScreen.wiredNetwork,
							  subtitle: NSLocalizedString("Ethernet", comment: "Network TV Row"))
			networkRows.append(typeRow)
			
			ipAddress = device.ethernetIPAddress
			macAddress = device.ethernetMacAddress
		}
		
		let ipAddressRow = Row(type: .ipAddress,
							   title: NSLocalizedString("IP Address", comment: "IP Address TV Row"),
                               accessibilityTitle: "", // Determined later
							   subtitle: ipAddress)
		networkRows.append(ipAddressRow)
		
		let macAddressRow = Row(type: .macAddress,
								title: NSLocalizedString("MAC Address", comment: "MAC Address TV Row"),
                                accessibilityTitle: "", // Determined later// Determined later
								subtitle: macAddress)
		networkRows.append(macAddressRow)
		
		return Section(type: .network,
					   title: NSLocalizedString("NETWORK", comment: "Section header"),
					   rows: networkRows)
	}

	private func restartResetSection(device: IoTDevice) -> Section {
		var rrRows = [Row]()
		
		let restartRow = Row(type: .restart,
							 title: NSLocalizedString("Restart this Speaker", comment: "Restart TV Row"),
                             accessibilityTitle: Accessibility.MainSettingsScreen.restart,
							 subtitle: nil)
		rrRows.append(restartRow)
		
		let resetRow = Row(type: .reset,
						   title: NSLocalizedString("Factory Reset this Speaker", comment: "Reset TV Row"),
                           accessibilityTitle: Accessibility.MainSettingsScreen.factoryReset,
						   subtitle: nil)
		rrRows.append(resetRow)
		
		return Section(type: .restartReset,
					   title: NSLocalizedString("RESTART/RESET", comment: "Section header"),
					   rows: rrRows)
	}
	
	// MARK: Speaker Name
	private func editSpeakerName() {
        performSegue(withIdentifier: Segues.showSpeakerNameEntry, sender: self)
    }

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

        device.setDisplayName(newName,
                              completion: { (success, e) in
                                if !success, let error = e {
                                    DDLogError("Error setting name: \(error)")
                                }
        }, completionQueue: DispatchQueue.main)
    }

    // MARK: Device Restart
    private func requestedRestart() {
        guard
            let deviceID = deviceID,
            let device = playerController.device(id: deviceID) else {
                return
        }
        let title = NSString.localizedStringWithFormat("Restart \"%@\"?", device.displayName) as String
        let alertController = UIAlertController(title: title,
                                                message: nil,
                                                preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Cancel button"),
                                                style: .cancel,
                                                handler: nil))
        alertController.addAction(UIAlertAction(title: NSLocalizedString("Restart", comment: "Restart button"),
                                                style: .default,
                                                handler: { [weak self] _ in
                                                    guard
                                                        let self = self,
                                                        let deviceID = self.deviceID,
                                                        let device = self.playerController.device(id: deviceID) else {
                                                        return
                                                    }

                                                    device.reboot(completion: { (success, e) in
                                                        if !success, let error = e {
                                                            DDLogError("Error doing reboot: \(error)")
                                                        }
                                                    }, completionQueue: DispatchQueue.main)
        }))
        present(alertController, animated: true, completion: nil)
    }
	
	// MARK: Device Reset
	private func requestedReset() {
		guard
            let deviceID = deviceID,
            let device = playerController.device(id: deviceID) else {
				return
		}
		let title = NSString.localizedStringWithFormat("Reset \"%@\" to factory default settings?\n\n", device.displayName) as String
        let message = NSString.localizedStringWithFormat("You will have to set up \"%@\" as a new speaker before Jukebox will recognize it", device.displayName) as String
        let alertController = UIAlertController(title: title,
                                                message: message,
                                                preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Cancel button"),
                                                style: .cancel,
                                                handler: nil))
        alertController.addAction(UIAlertAction(title: NSLocalizedString("Reset", comment: "Reset button"),
                                                style: .destructive,
                                                handler: { [weak self] _ in
                                                    guard
                                                        let self = self,
                                                        let deviceID = self.deviceID,
                                                        let device = self.playerController.device(id: deviceID) else {
                                                        	return
                                                    }
                                                    device.factoryReset(completion: { (success, e) in
                                                        if !success, let error = e {
                                                            DDLogError("Error doing factory reset: \(error)")
                                                        }
                                                    }, completionQueue: DispatchQueue.main)

        }))
        present(alertController, animated: true, completion: nil)
    }

    private func showAdjustAudio() {
		performSegue(withIdentifier: Segues.showAudioSettings, sender: self)
    }

    private func showVoiceControl() {
		performSegue(withIdentifier: Segues.showVoiceUISettings, sender: self)
    }

    private func showBluetooth() {
		performSegue(withIdentifier: Segues.showBluetoothSettings, sender: self)
    }

    private func showIoTDevices() {
        performSegue(withIdentifier: Segues.showZigbeeSettings, sender: self)
    }
}


