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

import UIKit

class MasterViewControllerDataSource {
    private let testing = false
    private let playerController: PlayerControllerProtocol

    init(playerController: PlayerControllerProtocol) {
        self.playerController = playerController
    }
}

extension MasterViewControllerDataSource {
    func displayableItems(mode: MasterViewControllerMode) -> [MasterViewControllerDisplayable] {
        switch mode {
        case .kind:
            return byKindItems()
        case .alphabetical:
            return alphabeticalItems()
        case .network:
            return treeItems()
        }
    }

    func ancestry(item: MasterViewControllerDisplayable) -> [MasterViewControllerDisplayable]? {
        var ancestors = [MasterViewControllerDisplayable] ()
        var currentItem: MasterViewControllerDisplayable? = item
        repeat {
            ancestors.append(currentItem!)
            currentItem = currentItem?.parent
        } while currentItem != nil
        return ancestors
    }
}

extension MasterViewControllerDataSource {

    private func topLevelItems(tree: [MasterViewControllerDisplayable], parent: MasterViewControllerDisplayable?) -> [MasterViewControllerDisplayable] {
        var items = [MasterViewControllerDisplayable] ()
        tree.forEach { (item) in
            if let gpItem = item as? PlayerGroupDisplayableWrapper {
                let flattenedItem = PlayerGroupDisplayableWrapper(playerGroup: gpItem.playerGroup,
                                                                  depth: 0,
                                                                  children: [],
                                                                  hasBTChildren: gpItem.hasBTChildren,
                                                                  hasZBChildren: gpItem.hasZBChildren)
                flattenedItem.parent = parent
                items.append(flattenedItem)
            }
            if let btItem = item as? BluetoothDisplayableWrapper {
                let flattenedItem = BluetoothDisplayableWrapper(name: btItem.name,
                                                                btAddress: btItem.btAddress,
                                                                playerID: btItem.playerID,
                                                                depth: 0,
                                                                available: btItem.available)
                flattenedItem.parent = parent
                items.append(flattenedItem)
            }
            if let zbItem = item as? ZigbeeDisplayableWrapper {
                let flattenedItem = ZigbeeDisplayableWrapper(name: zbItem.name,
                                                             zbDeviceID: zbItem.zbDeviceID,
                                                             zbDeviceType: zbItem.zbDeviceType,
                                                             playerID: zbItem.playerID,
                                                             depth: 0,
                                                             available: zbItem.available)
                flattenedItem.parent = parent
                items.append(flattenedItem)
            }
            items.append(contentsOf: topLevelItems(tree: item.children, parent: item))
        }
        return items
    }

    private func alphabeticalItems() -> [MasterViewControllerDisplayable] {
        let wrappers = topLevelItems(tree: treeItems(), parent: nil)
        return wrappers.sorted{ $0.name <  $1.name }
    }

    private func byKindItems() -> [MasterViewControllerDisplayable] {
        let alphabetical = alphabeticalItems()
        return alphabetical.sorted{ return $0.encapsulatedType <  $1.encapsulatedType }
    }

    private func treeItems() -> [MasterViewControllerDisplayable] {
        if testing {
            return generateDummyTree()
        }
		let groups = playerController.playerGroups
        let wrappers = groups.map { (playerGroup)->(PlayerGroupDisplayableWrapper) in
            var children = [MasterViewControllerDisplayable] ()
            playerGroup.members.forEach {
                let playerWrapper = processGroupMember($0, depth: playerGroup.isConvenienceGroup ? 0 : 1)
                children.append(playerWrapper)
            }

            var hasBTChildren = false
            var hasZBChildren = false
            if playerGroup.isConvenienceGroup {
                // Directly attach the sole player's children to the group
                if let playerChild = children.first as? PlayerDisplayableWrapper {
					hasBTChildren = playerChild.hasBTChildren
                    hasZBChildren = playerChild.hasZBChildren
                    children = playerChild.children
                } else {
                    children = []
                }
            }

            let parent = PlayerGroupDisplayableWrapper(playerGroup: playerGroup,
                                                       depth: 0,
                                                       children: children,
                                                       hasBTChildren: hasBTChildren,
                                                       hasZBChildren: hasZBChildren)

            children.forEach{ $0.parent = parent}
            return parent
        }
        return wrappers.sorted{ $0.name <  $1.name }
    }

    private func generateDummyTree() -> [MasterViewControllerDisplayable] {
        var wrappers = [MasterViewControllerDisplayable] ()
        for groupIndex in 0..<4 {
            var children = [MasterViewControllerDisplayable] ()

            let grandchildDepth: UInt = groupIndex == 0 ? 1 : 2

            for childIndex in 0..<5 {
                var hasBTChildren = false
                var hasZBChildren = false
                var grandchildren = [MasterViewControllerDisplayable] ()
                let mod = childIndex % 3
                switch mod {
                case 0:
                    let grandchild1 = SurroundChannelDisplayableWrapper(playerID: "id\(childIndex)",
                        channelPlayerID: "id=1",
                        channel: .leftSurround,
                        depth: grandchildDepth,
                        available: true,
                        suppliedName: "Child 1")
                    let grandchild2 = SurroundChannelDisplayableWrapper(playerID: "id\(childIndex)",
                        channelPlayerID: "id=2",
                        channel: .rightSurround,
                        depth: grandchildDepth,
                        available: true,
                        suppliedName: "Child 2")
                    grandchildren = [grandchild1, grandchild2]
                case 1:
                    let bt = BluetoothDisplayableWrapper(name: "Bluetooth gizmo",
                                                         btAddress: "id=1",
                                                         playerID: "id\(childIndex)",
                        depth: grandchildDepth,
                        available: false)
                    grandchildren = [bt]
                    hasBTChildren = true
                case 2:
                    let zb1 = ZigbeeDisplayableWrapper(name: "Zigbee Light",
                                                       zbDeviceID: 0,
                                                       zbDeviceType: .light,
                                                       playerID: "id\(childIndex)",
                        depth: grandchildDepth,
                        available: false)
                    let zb2 = ZigbeeDisplayableWrapper(name: "Zigbee Thermostat",
                                                       zbDeviceID: 1,
                                                       zbDeviceType: .thermostat,
                                                       playerID: "id\(childIndex)",
                        depth: grandchildDepth,
                        available: false)
                    grandchildren = [zb1, zb2]
                    hasZBChildren = true
                default:
                    break
                }

                let child = PlayerDisplayableWrapper(name: "Dummy Player \(childIndex)",
                    playerID: "id\(childIndex)",
                    depth: 1,
                    available: true,
                    children: grandchildren,
                    hasBTChildren: hasBTChildren,
                    hasZBChildren: hasZBChildren)
                children.append(child)
                grandchildren.forEach{ $0.parent = child}
            }

            let group = DummyIoTPlayerGroup(groupID: "group-\(groupIndex)",
                isConvenienceGroup: groupIndex == 0,
                displayName: groupIndex == 0 ? "Dummy Convenience Group" : "Dummy Group",
                playlist: nil,
                available: true,
                members: [])

            if groupIndex == 0 {
                // Convenience group
                children = children.first?.children ?? []
            }

            let parent = PlayerGroupDisplayableWrapper(playerGroup: group,
                                                       depth: 0,
                                                       children: children,
                                                       hasBTChildren: false,
                                                       hasZBChildren: false)
            children.forEach{ $0.parent = parent}
            wrappers.append(parent)
        }

        return wrappers.sorted{ $0.name <  $1.name }
    }

    private func processGroupMember(_ member: IoTPlayerGroupMember, depth: UInt) -> MasterViewControllerDisplayable {
        var children = [MasterViewControllerDisplayable] ()
        var hasBTChildren = false
        var hasZBChildren = false
        if (member.available) {
            if let player = playerController.player(id: member.playerID) {
                if player.homeTheaterSupported && player.isSoundBar {
                    // Do we have any satellite speakers.
                    let channels: [IoTHomeTheaterChannel] = [.subwoofer, .leftSurround, .rightSurround, .leftRearSurround, .rightRearSurround,
                                                             .leftUpfiringSurround, .rightUpfiringSurround, .leftRearUpfiringSurround, .rightRearUpfiringSurround]

                    channels.forEach { channel in
                        if player.haveHomeTheaterChannel(channel) {
                            player.homeTheaterChannelDevices(channel).forEach { device in
                                children.append(SurroundChannelDisplayableWrapper(playerID: member.playerID,
                                                                                  channelPlayerID: device.playerID,
                                                                                  channel: channel,
                                                                                  depth: depth + 1,
                                                                                  available: device.connected,
                                                                                  suppliedName: device.displayName))
                            }
                        }
                    }
                }
                
                if let device = playerController.device(player: player) {
                    if device.bluetoothEnabled == .enabled {
                        device.pairedBluetoothDevices.forEach { (btDevice) in
                            let bt = BluetoothDisplayableWrapper(name: btDevice.name,
                                                                 btAddress: btDevice.address,
                                                                 playerID: player.playerID,
                                                                 depth: depth + 1,
                                                                 available: true)
                            children.append(bt)
                        }
                        hasBTChildren = device.pairedBluetoothDevices.count > 0
                    }

                    if device.zigbeeEnabled == .enabled &&
                    	device.zigbeeCoordinatorState == .nominated {
                        let zbDevices = device.zigbeeDevices
                        let zbChildren = zbDevices.map { (zbDevice) -> ZigbeeDisplayableWrapper in
                            return ZigbeeDisplayableWrapper(name: zbDevice.name,
                                                            zbDeviceID: zbDevice.identifier,
                                                            zbDeviceType: zbDevice.type,
                                                            playerID: player.playerID,
                                                            depth: depth + 1,
                                                            available: true)
                        }
                        children.append(contentsOf: zbChildren)
                        hasZBChildren = device.zigbeeDevices.count > 0
                    }
                }
            }
        }
        let parent = PlayerDisplayableWrapper(name: member.displayName,
                                              playerID: member.playerID,
                                              depth: depth,
                                              available: member.available,
                                              children: children,
                                              hasBTChildren: hasBTChildren,
                                              hasZBChildren: hasZBChildren)
        children.forEach{ $0.parent = parent}
        return parent
    }
}
