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

import UIKit
import CocoaLumberjack

class SpeakerSetUpChooseSpeakerCell: UITableViewCell {
	@IBOutlet weak var label: UILabel?
}

// MARK: -
protocol SpeakerSetUpSuccessOrFailure: class {
	var bailOutViewController: UIViewController? { get set }
	var soundbarPlayerID: String? { get set }
    var soundbarDeviceID: String? { get set }
	var setUpInfo: SpeakerSetUpInfo? { get set }
}

// MARK: -
class SpeakerSetUpChooseSpeakerViewController: UIViewController {
	// MARK: Public Properties

    let addSpeakerTimeout = 60.0
	var soundbarPlayerID: String?
    var soundbarDeviceID: String?
	var setUpInfo: SpeakerSetUpInfo?
	var bailOutViewController: UIViewController?
	
	// MARK: Private Properties/Types
	private struct Segues {
		static let showSuccess = "showSuccess"
		static let showError = "showError"
	}
	
	private let cellIdentifier = "SpeakerSetUpChooseSpeakerCell"
	private let tableViewHeaderHeight: CGFloat = 38.0
	private var speakers = [IoTPlayer]()
	private var observers = [ObserverToken]()
	private var playerInfoObserver: ObserverToken?
	private weak var playerInfoTimer: Timer?
	
	// MARK: Outlets
	@IBOutlet private weak var tableView: UITableView?
	@IBOutlet private weak var helpLabel: UILabel?
}

// MARK: - Overridden Methods
extension SpeakerSetUpChooseSpeakerViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
		
		navigationItem.setHidesBackButton(true, animated: false)
		// No title for back button to centre next title
		let bbi = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
		navigationItem.backBarButtonItem = bbi
		
		let nib = UINib(nibName: SettingsTableViewHeaderView.reuseIdentifier, bundle: nil)
		tableView?.register(nib, forHeaderFooterViewReuseIdentifier: SettingsTableViewHeaderView.reuseIdentifier)

		// Accessibility
		tableView?.accessibilityLabel = "Speaker List"

        if let setUpInfo = setUpInfo {
            switch setUpInfo.currentBaseChannel {
            case .subwoofer:
                helpLabel?.text = NSLocalizedString("Choose your subwoofer:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD SUBWOOFER", comment: "Choose speaker text")
            case .leftSurround:
                helpLabel?.text = NSLocalizedString("Choose your left surround:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD LEFT SURROUND", comment: "Choose speaker text")
            case .rightSurround:
                helpLabel?.text = NSLocalizedString("Choose your right surround:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD RIGHT SURROUND", comment: "Choose speaker text")
            case .leftRearSurround:
                helpLabel?.text = NSLocalizedString("Choose your left rear surround:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD LEFT REAR SURROUND", comment: "Choose speaker text")
            case .rightRearSurround:
                helpLabel?.text = NSLocalizedString("Choose your right rear surround:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD RIGHT REAR SURROUND", comment: "Choose speaker text")
            case .leftUpfiringSurround:
                helpLabel?.text = NSLocalizedString("Choose your left upfiring surround:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD LEFT UPFIRING SURROUND", comment: "Choose speaker text")
            case .rightUpfiringSurround:
                helpLabel?.text = NSLocalizedString("Choose your right upfiring surround:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD RIGHT UPFIRING SURROUND", comment: "Choose speaker text")
            case .leftRearUpfiringSurround:
                helpLabel?.text = NSLocalizedString("Choose your left rear upfiring surround:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD LEFT REAR UPFIRING SURROUND", comment: "Choose speaker text")
            case .rightRearUpfiringSurround:
                helpLabel?.text = NSLocalizedString("Choose your right rear upfiring surround:", comment: "Choose speaker text")
                title = NSLocalizedString("ADD RIGHT REAR UPFIRING SURROUND", comment: "Choose speaker text")
            default:
                break
            }
        }
    }
	
	override func viewWillAppear(_ animated: Bool) {
		super.viewWillAppear(animated)
		refresh()
		setUpObservers()
	}
	
	override func viewWillDisappear(_ animated: Bool) {
		super.viewWillDisappear(animated)
		playerInfoObserver = nil
		observers.forEach { NotificationCenter.default.removeObserver($0) }
		observers = []
	}
	
	override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
		segue.destination.injectDependencies(preferences: preferences, playerController: playerController)
		if let dest = segue.destination as? SpeakerSetUpSuccessOrFailure {
			dest.bailOutViewController = bailOutViewController
			dest.soundbarPlayerID = soundbarPlayerID
            dest.soundbarDeviceID = soundbarDeviceID
			dest.setUpInfo = setUpInfo
		}
	}
}

// MARK: - Actions
extension SpeakerSetUpChooseSpeakerViewController {
    @IBAction private func notNowTapped (_ sender: Any?) {
        if let currentSpeaker = setUpInfo?.currentBaseChannel {
            if currentSpeaker == .rightSurround ||
                currentSpeaker == .rightRearSurround ||
                currentSpeaker == .rightUpfiringSurround ||
                currentSpeaker == .rightRearUpfiringSurround {
                // We must remove the previously set left surround as they must be set up in pairs.
                performSegue(withIdentifier: Segues.showError, sender: self)
            } else {
                navigationController?.presentingViewController?.dismiss(animated: true, completion: nil)
            }
        }
    }
}

// MARK: - Private Methods
extension SpeakerSetUpChooseSpeakerViewController {
	private func refresh() {
		getSpeakers()
		tableView?.reloadData()
	}

	private func setUpObservers() {
		let deviceHandler: (DeviceNotification) -> () = { [weak self] notification in
			guard let self = self else {
					return
			}
			
			if notification.reason == .removed {
				if notification.device.deviceID == self.soundbarDeviceID {
					// Our soundbar has gone away...
				} else {
					self.refresh()
				}
			} else if notification.reason == .added {
				self.refresh()
			}
		}
		
		let deviceObserver = NotificationCenter.default.addObserver(forType: DeviceNotification.self,
																	object: playerController,
																	queue: OperationQueue.main,
																	using: deviceHandler)

		observers = [deviceObserver]
	}

	private func getSpeakers() {
		// We get the players that have home theatre channel support enabled and a corresponding device object
		let allPlayers = playerController.players

        guard
            let soundbarPlayerID = soundbarPlayerID,
            let soundbar = playerController.player(id: soundbarPlayerID) else {
            	speakers = []
            	return
        }

        let mlanSpeakers = soundbar.allHomeTheaterPlayerIDs()

		speakers = allPlayers.filter {
            if let _ = playerController.device(player: $0),
            	$0.homeTheaterSupported,
                $0.playerID != soundbarPlayerID,
                !mlanSpeakers.contains($0.playerID) {
                return true
            } else {
                return false
            }
        }
    }

    private func addSpeaker(_ speaker: IoTPlayer) {
        guard
            let soundbar = playerController.player(id: soundbarPlayerID!),
        	let setUpInfo = setUpInfo else {
                return
        }
		
        waitForPlayerInfo()

        var channels = [NSNumber(integerLiteral: setUpInfo.currentBaseChannel.rawValue)]
        if let upfiringChannel = setUpInfo.currentUpfiringChannel,
            setUpInfo.upfiringRequested {
            channels.append(NSNumber(integerLiteral: upfiringChannel.rawValue))
        }

        soundbar.addHomeTheaterChannels(channels,
                                        player: speaker,
                                        completion: { [weak self](success, e) in
                                            guard
                                                let self = self,
                                                let timer = self.playerInfoTimer else {
                                                return
                                            }
                                            if !success, let error = e {
                                                DDLogError("Couldn't add \(speaker.displayName) as \(setUpInfo.currentBaseChannel). Reason: \(error)")
                                                self.playerInfoTimerExpired(timer)
                                            }
            },completionQueue: DispatchQueue.main)
	}
	
	private func waitForPlayerInfo() {
		setUpPlayerInfoObserver()
		let timer = Timer(timeInterval: addSpeakerTimeout, // This can take quite a while to happen
						  target: self,
						  selector: #selector(playerInfoTimerExpired(_:)),
						  userInfo: nil,
						  repeats: false)
		RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
		playerInfoTimer = timer
		appSpinnerPresenter()?.presentWaitSpinner()
	}
	
	@objc private func playerInfoTimerExpired(_ timer: Timer) {
		appSpinnerPresenter()?.dismissWaitSpinner()
		playerInfoObserver = nil
        playerInfoTimer?.invalidate()
		playerInfoTimer = nil
		performSegue(withIdentifier: Segues.showError, sender: self)
	}
	
	private func gotPlayerInfo() {
		appSpinnerPresenter()?.dismissWaitSpinner()
		playerInfoObserver = nil
		playerInfoTimer?.invalidate()
		playerInfoTimer = nil
		showSuccess()
	}
	
	private func setUpPlayerInfoObserver() {
		let playerInfoHandler: (HomeTheaterNotification) -> () = { [weak self] notification in
			guard let self = self else {
				return
			}
			
            if notification.reason == .availabilityChanged &&
                notification.player.playerID == self.soundbarPlayerID &&
                notification.payload.channel == self.setUpInfo?.currentBaseChannel {
					// Got it
				self.gotPlayerInfo()
				
			}
		}
		
		playerInfoObserver = NotificationCenter.default.addObserver(forType: HomeTheaterNotification.self,
																	object: playerController,
																	queue: OperationQueue.main,
																	using: playerInfoHandler)
		
	}
	
	private func showSuccess() {
		performSegue(withIdentifier: Segues.showSuccess, sender: self)
	}
}

// MARK: - Tableview Datasource/Delegate
extension SpeakerSetUpChooseSpeakerViewController: UITableViewDelegate, UITableViewDataSource {
	func numberOfSections(in tableView: UITableView) -> Int {
		// #warning Incomplete implementation, return the number of sections
		return 1
	}
	
	func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
		// #warning Incomplete implementation, return the number of rows
		return speakers.count
	}
	
	func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
		let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! SpeakerSetUpChooseSpeakerCell
		
		let speaker = speakers[indexPath.row]
		cell.label?.text = speaker.displayName
		cell.accessibilityLabel = speaker.displayName + " Speaker"
		return cell
	}
	
	func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
		return tableViewHeaderHeight
	}
	
	func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
		return CGFloat.leastNormalMagnitude // if we return zero it gets ignored.
	}
	
	func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
		guard let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: SettingsTableViewHeaderView.reuseIdentifier) as? SettingsTableViewHeaderView else {
			return nil
		}
		
		header.titleLabel?.text = NSLocalizedString("SPEAKERS ON YOUR NETWORK", comment: "Tableview header")
		return header
	}
	
	func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
		tableView.deselectRow(at: indexPath, animated: true)
		addSpeaker(speakers[indexPath.row])
	}
}
