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

import UIKit

class SpeakerSetUpMenuViewController: MiniPlayerEmbeddingViewController { //UITableViewController {
	@IBOutlet private weak var tableView: UITableView!

    var deviceID: String?

    // MARK: Private Types
    private struct Segues {
        static let showAdjustAudio = "showAdjustAudio"
        static let showSetUpWizard = "showSetUpWizard"
    }

    private enum SectionType {
        case basic
        case advanced
        case volumes
    }

    private enum RowType {
        case addSubwoofer
		case removeSubwoofer
		case unremovableSubwoofer
        case addSurrounds
        case removeSurrounds
        case unremovableSurrounds

        case additionalSurroundsCollapsed
        case additionalSurroundsExpanded
        case addRearSurrounds
        case removeRearSurrounds
        case unremovableRearSurrounds
        case addUpfiringSurrounds
        case removeUpfiringSurrounds
        case unremovableUpfiringSurrounds
        case addRearUpfiringSurrounds
        case removeRearUpfiringSurrounds
        case unremovableRearUpfiringSurrounds

        case adjustVolumes
    }

    private struct Section {
        let type: SectionType
        let title: String
        let rows: [Row]
    }

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

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

    private var expandAdvancedOptions = false
    private var speakerInfoForWizard: SpeakerSetUpInfo?
}

extension SpeakerSetUpMenuViewController {
    override func viewDidLoad() {
        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?.accessibilityLabel = "Speaker Set Up List"
    	title = NSLocalizedString("SURROUND SYSTEM", comment: "SURROUND SYSTEM title")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        setUpObservers()
        setUpTableView()
        tableView.reloadData()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        observers.forEach { NotificationCenter.default.removeObserver($0) }
        observers = []
    }
}

// MARK: - TableView
extension SpeakerSetUpMenuViewController: UITableViewDataSource, UITableViewDelegate {
    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let row = sections[indexPath.section].rows[indexPath.row]
        if row.subtitle != nil {

            // Two Row - Add Additional Surrounds
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsTwoRowTableViewCell.reuseIdentifier, for: indexPath) as? SettingsTwoRowTableViewCell
            if cell == nil {
                cell = SettingsTwoRowTableViewCell()
            }

            cell!.subtitleLabel?.text = row.subtitle
            cell!.titleLabel?.text = row.title
            cell?.accessibilityLabel = row.title  + " Cell"
            cell!.selectionStyle = .gray
            cell?.accessoryType = .none
            cell?.style = .add

            return cell!
        } else if row.type == .addSubwoofer ||
            row.type == .addSurrounds {
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier, for: indexPath) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }

            cell!.titleLabel?.text = row.title
            cell?.accessibilityLabel = row.title + " Cell"

            cell!.selectionStyle = .gray
            cell?.accessoryType = .none
            cell?.style = .add

            return cell!
        } else if row.type == .additionalSurroundsCollapsed ||
            row.type == .additionalSurroundsExpanded {
            var cell = tableView.dequeueReusableCell(withIdentifier: SettingsOneRowTableViewCell.reuseIdentifier, for: indexPath) as? SettingsOneRowTableViewCell
            if cell == nil {
                cell = SettingsOneRowTableViewCell()
            }
            cell!.titleLabel?.text = row.title
            cell?.accessibilityLabel = row.title + " Cell"
            cell!.selectionStyle = .none
            cell?.expanded = expandAdvancedOptions ? .expanded : .collapsed
            cell?.accessoryType = .none
            cell?.style = .plain

            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?.accessibilityLabel = row.title + " Cell"
            cell?.accessoryType = .none
            cell?.style = .plain

            if row.type == .unremovableSubwoofer ||
                row.type == .unremovableSurrounds ||
                row.type == .unremovableRearSurrounds ||
                row.type == .unremovableUpfiringSurrounds ||
                row.type == .unremovableRearUpfiringSurrounds {
                cell!.selectionStyle = .none
                cell!.style = .unremovable
            } else {
                cell!.selectionStyle = .gray
                if row.type == .removeSubwoofer ||
                    row.type == .removeSurrounds ||
                    row.type == .removeRearSurrounds ||
                    row.type == .removeUpfiringSurrounds ||
                    row.type == .removeRearUpfiringSurrounds {
                    cell?.style = .remove
                }
            }

            return cell!
        }
    }

    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 .addSubwoofer, .addSurrounds, .addRearSurrounds, .addUpfiringSurrounds, .addRearUpfiringSurrounds:
            startSpeakerSetup(row: row.type)
        case .removeSubwoofer, .removeSurrounds, .removeRearSurrounds, .removeUpfiringSurrounds, .removeRearUpfiringSurrounds:
            removeMultichannel(row: row.type)

        case .additionalSurroundsCollapsed, .additionalSurroundsExpanded:
            toggleExpandedSection()
        case .adjustVolumes:
            adjustAudioRequested()
        default:
            break;
        }
    }

    func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        let row = sections[indexPath.section].rows[indexPath.row]
        if row.type == .unremovableSubwoofer ||
            row.type == .unremovableSurrounds ||
            row.type == .unremovableRearSurrounds ||
            row.type == .unremovableUpfiringSurrounds ||
            row.type == .unremovableRearUpfiringSurrounds {
            return nil
        } else {
            return indexPath
        }
    }

    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 = sections[section].title
        return header
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return false
    }
}

extension SpeakerSetUpMenuViewController {
    private func setUpObservers() {
        let deviceHandler: (DeviceNotification) -> () = { [weak self] notification in
            guard let self = self else { return }
            self.setUpTableView()
            self.tableView?.reloadData()
        }

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

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

            if notification.reason == .availabilityChanged {
                self.setUpTableView()
                self.tableView.reloadData()
            }
        }

        let homeTheaterObserver = NotificationCenter.default.addObserver(forType: HomeTheaterNotification.self,
                                                                            object: playerController,
                                                                            queue: OperationQueue.main,
                                                                            using: homeTheaterHandler)

        observers = [deviceObserver, homeTheaterObserver]
    }

    private func toggleExpandedSection() {
		expandAdvancedOptions = !expandAdvancedOptions
        setUpTableView()
        tableView.reloadSections(IndexSet(integer: 1), with: .fade)
    }

    private func basicSection(player: IoTPlayer) -> Section {
		var speakerRows = [Row] ()

        var subwooferRow: Row
        if player.haveHomeTheaterChannel(.subwoofer) {
            if player.haveRemovableHomeTheaterChannel(.subwoofer) {
                subwooferRow = Row(type: .removeSubwoofer,
                                   title: NSLocalizedString("Remove Subwoofer", comment: "Remove Subwoofer TV cell"),
                                   subtitle: nil)
            } else {
                subwooferRow = Row(type: .unremovableSubwoofer,
                                   title: NSLocalizedString("Built-In Subwoofer", comment: "Unremovable Subwoofer TV cell"),
                                   subtitle: nil)
            }
        } else {
            subwooferRow = Row(type: .addSubwoofer,
                               title: NSLocalizedString("Add Subwoofer", comment: "Add Subwoofer TV cell"),
                               subtitle: nil)
        }
        speakerRows.append(subwooferRow)

        var surroundRow: Row
        if player.haveHomeTheaterChannel(.leftSurround) || player.haveHomeTheaterChannel(.rightSurround) {
            if player.haveRemovableHomeTheaterChannel(.leftSurround) || player.haveRemovableHomeTheaterChannel(.rightSurround){
                surroundRow = Row(type: .removeSurrounds,
                                  title: NSLocalizedString("Remove Surrounds", comment: "Remove Surrounds TV cell"),
                                  subtitle: nil)
            } else {
                surroundRow = Row(type: .unremovableSurrounds,
                                  title: NSLocalizedString("Built-In Surrounds", comment: "Unremovable Surrounds TV cell"),
                                  subtitle: nil)

            }
        } else {
            surroundRow = Row(type: .addSurrounds,
                              title: NSLocalizedString("Add Surrounds", comment: "Add Surrounds TV cell"),
                              subtitle: nil)

        }
        speakerRows.append(surroundRow)



        let section = Section(type: .basic,
                              title: NSLocalizedString("COMPLETE YOUR 5.1 SYSTEM", comment: "Basic Surround Setup header"),
                              rows: speakerRows)
        return section
    }

    private func advancedSection(player: IoTPlayer) -> Section {
        var rows = [Row] ()

        let expandRow = Row(type: expandAdvancedOptions ? .additionalSurroundsExpanded :  .additionalSurroundsCollapsed,
                            title: NSLocalizedString("Additional Surrounds", comment: "Remove Subwoofer TV cell"),
                            subtitle: nil)
        rows.append(expandRow)

        if expandAdvancedOptions {
            var rearSurrounds: Row
            if player.haveHomeTheaterChannel(.rightRearSurround) || player.haveHomeTheaterChannel(.leftRearSurround) {
                if player.haveRemovableHomeTheaterChannel(.rightRearSurround) || player.haveRemovableHomeTheaterChannel(.leftRearSurround){
                    rearSurrounds = Row(type: .removeRearSurrounds,
                                        title: NSLocalizedString("Remove Rear Surrounds", comment: "Remove Left and Right Rear Surrounds cell"),
                                        subtitle: nil)
                } else {
                    rearSurrounds = Row(type: .unremovableRearSurrounds,
                                        title: NSLocalizedString("Built-In Rear Surrounds", comment: "Unremovable Rear Surrounds TV cell"),
                                        subtitle: nil)

                }
            } else {
                rearSurrounds = Row(type: .addRearSurrounds,
                                    title: NSLocalizedString("Add Rear Surrounds", comment: "Add Left and Right Rear Surrounds TV cell"),
                                    subtitle: NSLocalizedString("For a 7.1 System", comment: "For a 7.1 System cell"))
            }
            rows.append(rearSurrounds)

            var upfiringSurrounds: Row
            if player.haveHomeTheaterChannel(.rightUpfiringSurround) || player.haveHomeTheaterChannel(.leftUpfiringSurround) {
                if player.haveRemovableHomeTheaterChannel(.rightUpfiringSurround) || player.haveRemovableHomeTheaterChannel(.leftUpfiringSurround){
                    upfiringSurrounds = Row(type: .removeUpfiringSurrounds,
                                            title: NSLocalizedString("Remove Upfiring Surrounds", comment: "Remove Left and Right Upfiring Surrounds cell"),
                                            subtitle: nil)
                } else {
                    upfiringSurrounds = Row(type: .unremovableUpfiringSurrounds,
                                            title: NSLocalizedString("Built-In Upfiring Surrounds", comment: "Unremovable Upfiring Rear Surrounds TV cell"),
                                            subtitle: nil)

                }
            } else {
                upfiringSurrounds = Row(type: .addUpfiringSurrounds,
                                        title: NSLocalizedString("Add Upfiring Surrounds", comment: "Add Left and Right Upfiring Surrounds TV cell"),
                                        subtitle: NSLocalizedString("For a 5.1.2 or 7.1.2 System", comment: "For a 5.1.2 or 7.1.2 System cell"))
            }
            rows.append(upfiringSurrounds)

            var rearUpfiringSurrounds: Row
            if player.haveHomeTheaterChannel(.rightRearUpfiringSurround) || player.haveHomeTheaterChannel(.leftRearUpfiringSurround) {
                if player.haveRemovableHomeTheaterChannel(.rightRearUpfiringSurround) || player.haveRemovableHomeTheaterChannel(.leftRearUpfiringSurround){
                    rearUpfiringSurrounds = Row(type: .removeRearUpfiringSurrounds,
                                                title: NSLocalizedString("Remove Rear Upfiring Surrounds", comment: "Remove Left and Right Upfiring Surrounds cell"),
                                                subtitle: nil)
                } else {
                    rearUpfiringSurrounds = Row(type: .unremovableRearUpfiringSurrounds,
                                            title: NSLocalizedString("Built-In Rear Upfiring Surrounds", comment: "Unremovable Rear Upfiring Rear Surrounds TV cell"),
                                            subtitle: nil)

                }
            } else {
                rearUpfiringSurrounds = Row(type: .addRearUpfiringSurrounds,
                                        title: NSLocalizedString("Add Rear Upfiring Surrounds", comment: "Add Left and Right Upfiring Surrounds TV cell"),
                                        subtitle: NSLocalizedString("For a 5.1.4 or 7.1.4 System", comment: "For a 5.1.2 or 7.1.2 System cell"))
            }
            rows.append(rearUpfiringSurrounds)
        }

        let section = Section(type: .basic,
                              title: NSLocalizedString("ADVANCED OPTIONS", comment: "Basic Surround Setup header"),
                              rows: rows)
        return section
    }

    private func volumesSection(player: IoTPlayer) -> Section {
		let row = Row(type: .adjustVolumes,
                      title: NSLocalizedString("Adjust Audio Levels", comment: "Adjust Audio Levels Cell"),
                      subtitle: nil)

        let section = Section(type: .volumes,
                              title: NSLocalizedString("", comment: ""),
                              rows: [row])
        return section
    }

    private func setUpTableView() {
        guard let device = playerController.device(id: deviceID ?? ""),
            let player = playerController.player(device: device) else {
                sections = [Section] ()
                tableView.reloadData()
                return
        }

        var newSections = [Section] ()

        newSections.append(basicSection(player: player))
        newSections.append(advancedSection(player: player))
        newSections.append(volumesSection(player: player))

        sections = newSections
    }

    private func deviceNameForRowType(_ row: RowType) -> String? {
        switch row {
        case .addSubwoofer, .removeSubwoofer:
            return NSLocalizedString("subwoofer", comment: "subwoofer name")
        case .addSurrounds, .removeSurrounds:
            return NSLocalizedString("surround speakers", comment: "surround speakers")
        case .addRearSurrounds, .removeRearSurrounds:
            return NSLocalizedString("rear surround speakers", comment: "rear surround speakers")
        case .addUpfiringSurrounds, .removeUpfiringSurrounds:
            return NSLocalizedString("upfiring surround speakers", comment: "upfiring surround speakers")
        case .addRearUpfiringSurrounds, .removeRearUpfiringSurrounds:
            return NSLocalizedString("rear upfiring surround speakers", comment: "rear upfiring surround speakers")
        default:
            break
        }
        return nil
    }

    private func speakerInfoForRowType(_ row: RowType) -> SpeakerSetUpInfo {
        let baseChannels = baseChannelsForRowType(row)

        guard
            let deviceID = deviceID,
            let device = playerController.device(id: deviceID),
            let player = playerController.player(device: device) else {
                return SpeakerSetUpInfo(baseChannels: baseChannels,
                                        upfiringChannels: nil)
        }

        var upfiringChannels: [IoTHomeTheaterChannel]? = nil
        switch row {
/*
// Currently only rears supported.
            case .addSurrounds, .removeSurrounds:
            if !player.haveHomeTheaterChannel(.leftUpfiringSurround) &&
                !player.haveHomeTheaterChannel(.rightUpfiringSurround) {
                upfiringChannels = [IoTHomeTheaterChannel.leftUpfiringSurround, IoTHomeTheaterChannel.rightUpfiringSurround]
            }
 */
        case .addRearSurrounds, .removeRearSurrounds:
            if !player.haveHomeTheaterChannel(.leftRearUpfiringSurround) &&
                !player.haveHomeTheaterChannel(.rightRearUpfiringSurround) {
                upfiringChannels = [IoTHomeTheaterChannel.leftRearUpfiringSurround, IoTHomeTheaterChannel.rightRearUpfiringSurround]
            }
        default:
            break
        }
        return SpeakerSetUpInfo(baseChannels: baseChannels,
                                upfiringChannels: upfiringChannels)
    }

    private func baseChannelsForRowType(_ row: RowType) -> [IoTHomeTheaterChannel] {
        switch row {
        case .addSubwoofer, .removeSubwoofer:
            return [IoTHomeTheaterChannel.subwoofer]
        case .addSurrounds, .removeSurrounds:
            return [IoTHomeTheaterChannel.leftSurround, IoTHomeTheaterChannel.rightSurround]
        case .addRearSurrounds, .removeRearSurrounds:
            return [IoTHomeTheaterChannel.leftRearSurround, IoTHomeTheaterChannel.rightRearSurround]
        case .addUpfiringSurrounds, .removeUpfiringSurrounds:
            return [IoTHomeTheaterChannel.leftUpfiringSurround, IoTHomeTheaterChannel.rightUpfiringSurround]
        case .addRearUpfiringSurrounds, .removeRearUpfiringSurrounds:
            return [IoTHomeTheaterChannel.leftRearUpfiringSurround, IoTHomeTheaterChannel.rightRearUpfiringSurround]
        default:
            break
        }
        return []
    }

    private func removeMultichannel(row: RowType) {
        guard
            let deviceID = deviceID,
            let device = playerController.device(id: deviceID),
            let player = self.playerController.player(device: device),
            let channelsName = deviceNameForRowType(row) else {
                return
        }

        let channels = baseChannelsForRowType(row)

        let someMissing = channels.contains { channel -> Bool in
            return !player.isHomeTheaterChannelAvailable(channel)
        }

        let title = NSString.localizedStringWithFormat("Remove %@?", channelsName.capitalized) as String
        var message = NSString.localizedStringWithFormat("Are you sure you want to disconnect your %@ from your sound system?", channelsName) as String
        if someMissing {
            message.append(" " + NSLocalizedString("Some devices are currently not available.", comment: "Some devices are currently not available."))
        }

        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("Remove", comment: "Remove button"),
                                                style: .default,
                                                handler: { _ in
                                                    guard let player = self.playerController.player(device: device) else {
                                                        return
                                                    }

                                                    let mapped = channels.map { channel in
                                                        return NSNumber(integerLiteral: channel.rawValue)
                                                    }

                                                    player.removeHomeTheaterChannels(mapped,
                                                                                     completion: nil,
                                                                                     completionQueue: nil)
        }))
        present(alertController, animated: true, completion: nil)
    }

    private func startSpeakerSetup(row: RowType) {
        if let deviceID = deviceID,
            let device = playerController.device(id: deviceID),
            let _ = playerController.player(device: device) {
            speakerInfoForWizard = speakerInfoForRowType(row)
            performSegue(withIdentifier: Segues.showSetUpWizard, sender: self)
        }
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == Segues.showSetUpWizard {
            if let nav = segue.destination as? UINavigationController,
                let vc = nav.topViewController as? SpeakerSetUpPageViewController {
                vc.injectDependencies(preferences: preferences, playerController: playerController)
                vc.setUpInfo = speakerInfoForWizard
                vc.soundbarDeviceID = deviceID
                if let deviceID = deviceID,
                    let device = playerController.device(id: deviceID),
                    let player = playerController.player(device: device) {
                    vc.soundbarPlayerID = player.playerID
                }
            }
        } else if segue.identifier == Segues.showAdjustAudio {
            if let nav = segue.destination as? UINavigationController,
                let vc = nav.topViewController as? AdjustAudioIntroViewController {
                vc.injectDependencies(preferences: preferences, playerController: playerController)
                vc.soundbarDeviceID = deviceID
                if let deviceID = deviceID,
                    let device = playerController.device(id: deviceID),
                    let player = playerController.player(device: device) {
                    vc.soundbarPlayerID = player.playerID
                }
            }
        }
    }

    // MARK: Adjust Audio
    private func adjustAudioRequested() {
        if let deviceID = deviceID,
            let device = playerController.device(id: deviceID),
            let _ = playerController.player(device: device) {
            performSegue(withIdentifier: Segues.showAdjustAudio, sender: self)
        }
    }
}

