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

import UIKit
import CocoaLumberjack
import Kingfisher


final class NowPlayingViewController: UIViewController {
	// MARK: Public Properties
	var playerGroup: IoTPlayerGroup?
	
	// MARK: Private Properties/Types
	private struct Segues {
		static let showQueue = "showQueue"
		static let showSpeakerVolumes = "showSpeakerVolumes"
		static let dismiss = "dismiss"
	}
	
	private weak var progressTimer: Timer?
	private var observers = [ObserverToken] ()
//	private let operationsQueue = DispatchQueue(label: "com.qualcomm.qti.NowPlayingViewController.blockingOperations")
	
	// MARK: UI Outlets
	@IBOutlet private weak var emptyView: UIView?
	@IBOutlet private weak var songTitleLabel: UILabel?
	@IBOutlet private weak var artistLabel: UILabel?
	@IBOutlet private weak var albumLabel: UILabel?
	@IBOutlet private weak var blurryBackgroundAlbumArtworkImageView: UIImageView?
	@IBOutlet private weak var albumArtworkImageView: UIImageView?
	@IBOutlet private weak var progressSlider: UISlider?
	@IBOutlet private weak var playPauseButton: UIButton?
	@IBOutlet private weak var skipForwardButton: UIButton?
	@IBOutlet private weak var skipBackwardButton: UIButton?
	@IBOutlet private weak var favoriteButton: UIButton?
	@IBOutlet private weak var speakersButton: UIButton?
	@IBOutlet private weak var repeatButton: UIButton?
	@IBOutlet private weak var shuffleButton: UIButton?
	@IBOutlet private weak var volumeSlider: UISlider?
	@IBOutlet private weak var leftSwipeGestureRecognizer: UISwipeGestureRecognizer?
	@IBOutlet private weak var rightSwipeGestureRecognizer: UISwipeGestureRecognizer?
}

// MARK: - Overridden Methods
extension NowPlayingViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = NSLocalizedString("NOW PLAYING", comment: "Now playing text")
        
        if let closeImage = UIImage(named: "btn-down"),
            let closeImagePressed = UIImage(named: "btn-down-pressed") {
            let resizableCloseImage = closeImage.resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: closeImage.size.width-1, bottom: 0, right: 0))
            let resizableCloseImagePressed = closeImagePressed.resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: closeImagePressed.size.width-1, bottom: 0, right: 0))
            let leftBarButton = UIBarButtonItem(title: " ", style: .plain, target: self, action: #selector(didTapModalClose(_:)))
            leftBarButton.setBackgroundImage(resizableCloseImage, for: .normal, barMetrics: .default)
            leftBarButton.setBackgroundImage(resizableCloseImagePressed, for: .highlighted, barMetrics: .default)
            leftBarButton.makeAccessible(Accessibility.NowPlayingScreen.closeButton)
            navigationItem.leftBarButtonItem = leftBarButton
        }
        
        if let queueImage = UIImage(named: "btn-queue"),
            let queueImagePressed = UIImage(named: "btn-queue-pressed") {
            let resizableQueueImage = queueImage.resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: queueImage.size.width-1))
            let resizableQueueImagePressed = queueImagePressed.resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: queueImagePressed.size.width-1))
            let rightBarButton = UIBarButtonItem(title: " ", style: .plain, target: self, action: #selector(didTapQueue(_:)))
            rightBarButton.setBackgroundImage(resizableQueueImage, for: .normal, barMetrics: .default)
            rightBarButton.setBackgroundImage(resizableQueueImagePressed, for: .highlighted, barMetrics: .default)
			rightBarButton.makeAccessible(Accessibility.NowPlayingScreen.queueButton)
            navigationItem.rightBarButtonItem = rightBarButton
        }

        skipForwardButton?.makeAccessible(Accessibility.NowPlayingScreen.skipForwardButton)
        skipBackwardButton?.makeAccessible(Accessibility.NowPlayingScreen.skipBackwardButton)
        volumeSlider?.makeAccessible(Accessibility.NowPlayingScreen.volumeSlider)
        progressSlider?.makeAccessible(Accessibility.NowPlayingScreen.seekBar)
        speakersButton?.makeAccessible(Accessibility.NowPlayingScreen.speakerVolumesButton)
        
        emptyView?.alpha = 0.0;
    }
	
	override func viewWillAppear(_ animated: Bool) {
		super.viewWillAppear(animated)
		
		let t = Timer(timeInterval: 1.0,
					  target: self,
					  selector: #selector(progressTimerFired(_ :)),
					  userInfo: nil,
					  repeats: true)
		
		RunLoop.current.add(t, forMode: RunLoop.Mode.common)
		progressTimer = t
		
		updateUI()
		
		if observers.count == 0 {
			setupObservers()
		}
	}
	
	override func viewWillDisappear(_ animated: Bool) {
		super.viewWillDisappear(animated)
		
		observers.forEach { NotificationCenter.default.removeObserver($0) }
		observers.removeAll()
		
		progressTimer?.invalidate()
		progressTimer = nil
	}
	
	override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
		if let queueController = segue.destination as? QueueViewController {
			queueController.injectDependencies(preferences: preferences, playerController: playerController)
			queueController.playerGroup = playerGroup
		}
		
		if let speakerController = segue.destination as? SpeakerVolumesViewController {
			speakerController.injectDependencies(preferences: preferences, playerController: playerController)
			speakerController.playerGroup = playerGroup
		}
	}
}

// MARK: - UI Actions
extension NowPlayingViewController {
	@IBAction private func unwindFromQueueViewController(segue: UIStoryboardSegue) {
		// DO NOT REMOVE
	}
	
	@IBAction private func unwindFromSpeakerVolumesViewController(segue: UIStoryboardSegue) {
		// DO NOT REMOVE
	}

	override func didTapModalClose(_ sender: AnyObject?) {
		performSegue(withIdentifier: Segues.dismiss, sender: self)
	}
	
	@objc private func didTapQueue(_ sender: AnyObject?) {
		performSegue(withIdentifier: Segues.showQueue, sender: self)
	}
	
	@IBAction private func showVolumes(_ sender: UIButton) {
		performSegue(withIdentifier: Segues.showSpeakerVolumes, sender: self)
    }

    @IBAction private func volumeChanged(_ slider: UISlider) {
        guard let playerGroup = self.playerGroup else {
            return
        }

        let maxVolume = playerGroup.maxVolume
        let requested = Double(slider.value) * maxVolume
        playerGroup.setVolume(requested,
                              completion: { (success, e) in
                                if !success, let error = e {
                                    DDLogError("Error thrown setting volume: \(error)")
                                }
        }, completionQueue: DispatchQueue.main)
    }

    @IBAction private func progressChanged(_ sender: UISlider) {
        guard let playerGroup = self.playerGroup else {
            return
        }

        if let currentItem = playerGroup.playlist?.currentItem,
            !currentItem.isEmpty {
            let duration = currentItem.duration // in ms
            if (duration > 0) {
                let requested = Int32(Float(duration) * sender.value)
                DDLogVerbose("Setting position: \(requested)")
                playerGroup.setPlayerPosition(requested,
                                              completion: { (success, e) in
                                                if !success, let error = e {
                                                    DDLogError("Error changing progress: \(error)")
                                                }
                }, completionQueue: DispatchQueue.main)
            }
        }
    }

    @IBAction private func togglePlayPaused(_ sender: UIButton) {
        guard let playerGroup = self.playerGroup else {
            return
        }

        let currentItem = playerGroup.playlist?.currentItem
        let isEmpty = currentItem?.isEmpty ?? true
        let playing = !isEmpty && playerGroup.playState == .playing
        if playing {
            playerGroup.pause(completion: nil, completionQueue: nil)
        } else {
            if playerGroup.activeInputSelector?.type == .bluetooth || (playerGroup.playlist?.indexPlaying ?? -1 >= 0) {
                playerGroup.play(completion: nil, completionQueue: nil)
            } else {
                playerGroup.playlist?.playItem(at: 0, completion: nil, completionQueue: nil)
            }
        }
    }

	@IBAction private func skipForward(_ sender: UIButton) {
		skipForward()
	}
	
	@IBAction private func skipBackward(_ sender: UIButton) {
		skipBackward()
	}
	
    @IBAction private func toggleShuffle(_ sender: UIButton) {
        guard let playlist = playerGroup?.playlist else {
            return
        }

        let isShuffling = playlist.shuffleMode == .shuffle
        let newShuffle: IoTShuffleMode = isShuffling ? .linear : .shuffle

        playlist.setShuffleMode(newShuffle,
                                completion: { (success, e) in
                                    if !success, let error = e {
                                        DDLogError("Error on shuffle set: \(error)")
                                    }
        }, completionQueue: DispatchQueue.main)
    }

    @IBAction private func toggleLoop(_ sender: UIButton) {
        guard let playlist = playerGroup?.playlist else {
            return
        }

        var nextLoopMode = IoTLoopMode.none
        switch playlist.loopMode {
        case .none:
            nextLoopMode = .one
        case .one:
            nextLoopMode = .all
        case .all:
            nextLoopMode = .none
        @unknown default:
            fatalError()
        }

        playlist.setLoopMode(nextLoopMode,
                             completion: { (success, e) in
                                if !success, let error = e {
                                    DDLogError("Error on shuffle set: \(error)")
                                }
        }, completionQueue: DispatchQueue.main)
    }

	@IBAction private func toggleFavorite(_ sender: UIButton) {
		if let currentItem = playerGroup?.playlist?.currentItem {
			if !currentItem.isEmpty {
				if FavoritesController.sharedInstance.isFavorite(item: currentItem) {
					FavoritesController.sharedInstance.removeFavorite(item: currentItem)
				} else {
					FavoritesController.sharedInstance.makeFavorite(item: currentItem)
				}
				updateUI()
			}
		}
	}
	
	@IBAction private func didSwipeLeft(_ sender: UISwipeGestureRecognizer) {
		skipForward()
	}
	
	@IBAction private func didSwipeRight(_ sender: UISwipeGestureRecognizer) {
		skipBackward()
	}
}

// MARK: - Private Methods
extension NowPlayingViewController {
	private func setupObservers() {
		let playerGroupHandler: (PlayerGroupNotification) -> () = { [weak self] notification in
            guard let self = self,
                let group = self.playerGroup,
                notification.playerGroup == group  else {
                    return
                    
            }
            
            if notification.reason == .removed {
                // We've been deleted!!!
                self.playerGroup = nil
                self.presentingViewController?.dismiss(animated: true, completion: nil)
                return
            }
            
			self.updateUI()
		}
		
		let playerGroupObserver = NotificationCenter.default.addObserver(forType: PlayerGroupNotification.self,
																  object: playerController,
																  queue: OperationQueue.main,
																  using: playerGroupHandler)
		
		observers.append(playerGroupObserver)
		
		// **********
		
		let playlistHandler: (PlaylistNotification) -> () = { [weak self] notification in
			guard let self = self else { return }
			
			guard notification.playlist == self.playerGroup?.playlist else {
				return
			}
			
			self.updateUI()
		}
		
		let playlistObserver = NotificationCenter.default.addObserver(forType: PlaylistNotification.self,
																	  object: playerController,
																	  queue: OperationQueue.main,
																	  using: playlistHandler)
		
		observers.append(playlistObserver)
		
		// **********
		
		let playerHandler: (PlayerNotification) -> () = { [weak self] notification in
			guard let self = self else { return }
			self.updateUI()
		}
		
		let playerObserver = NotificationCenter.default.addObserver(forType: PlayerNotification.self,
                                                                    object: playerController,
                                                                    queue: OperationQueue.main,
                                                                    using: playerHandler)
        
        observers.append(playerObserver)
    }
	
    private func updateUI() {
        guard let unwrappedPlayerGroup = playerGroup else {
            return
        }

        var availableMembers = 0;
        unwrappedPlayerGroup.members.forEach {
            if $0.available {
                availableMembers = availableMembers + 1
            }
        }
        speakersButton?.isEnabled = availableMembers > 1

        let currentItem = unwrappedPlayerGroup.playlist?.currentItem
        let isEmpty = currentItem?.isEmpty ?? true

        let interruptibleAndPlaying = unwrappedPlayerGroup.isInterruptible && !isEmpty && unwrappedPlayerGroup.playState == .playing
        if interruptibleAndPlaying || !unwrappedPlayerGroup.isInterruptible {
            playPauseButton?.makeAccessible(Accessibility.NowPlayingScreen.pauseButton)
            playPauseButton?.setImage(UIImage(named: "btn-pause"), for: .normal)
            playPauseButton?.setImage(UIImage(named: "btn-pause-pressed"), for: .selected)
            playPauseButton?.setImage(UIImage(named: "btn-pause-pressed"), for: .highlighted)
        } else {
            playPauseButton?.makeAccessible(Accessibility.NowPlayingScreen.playButton)
            playPauseButton?.setImage(UIImage(named: "btn-play"), for: .normal)
            playPauseButton?.setImage(UIImage(named: "btn-play-pressed"), for: .selected)
            playPauseButton?.setImage(UIImage(named: "btn-play-pressed"), for: .highlighted)
        }
        
        var isFavorite = false
        var isShuffle = false
        var repeatType = IoTLoopMode.none
        var showEmptyView = true
        
        favoriteButton?.isEnabled = false
        shuffleButton?.isEnabled = false
        repeatButton?.isEnabled = false
        skipForwardButton?.isEnabled = canSkipForward()
        leftSwipeGestureRecognizer?.isEnabled = canSkipForward()
        skipBackwardButton?.isEnabled = canSkipBackward()
        rightSwipeGestureRecognizer?.isEnabled = canSkipBackward()
        playPauseButton?.isEnabled = canPlayPause()
        progressSlider?.isEnabled = canSeek()
        
        if let playlist = unwrappedPlayerGroup.playlist {

            if playlist.isShuffleModeEnabled {
                isShuffle = playlist.shuffleMode == .shuffle
                shuffleButton?.isEnabled = true
            }

            if playlist.isLoopModeEnabled {
                repeatType = playlist.loopMode
                repeatButton?.isEnabled = true
            }

            let currentItem = playlist.currentItem
            let isEmpty = currentItem.isEmpty

            favoriteButton?.isEnabled = isEmpty ? false : FavoritesController.sharedInstance.isFavoritable(item: currentItem)

            showEmptyView = isEmpty
            artistLabel?.text = isEmpty ? "" : currentItem.artist
            albumLabel?.text = isEmpty ? "" : currentItem.album
            songTitleLabel?.text = isEmpty ? "" : currentItem.title.uppercased()

            if isEmpty {
                songTitleLabel?.makeAccessible("")
                albumLabel?.makeAccessible("")
                artistLabel?.makeAccessible("")

                isFavorite = false
                albumArtworkImageView?.image = UIImage(named: "album-art-default-large-noshadow")
                blurryBackgroundAlbumArtworkImageView?.image = UIImage(named: "album-art-default-large-noshadow")?.grayscaleImage()
            } else {
                songTitleLabel?.makeAccessible(localizablePrefix: Accessibility.NowPlayingScreen.songTitleLabelPrefix,
                                               value: currentItem.title)
                albumLabel?.makeAccessible(localizablePrefix: Accessibility.NowPlayingScreen.albumLabelPrefix,
                                           value: currentItem.album)
                artistLabel?.makeAccessible(localizablePrefix: Accessibility.NowPlayingScreen.artistLabelPrefix,
                                            value: currentItem.artist)

                isFavorite = FavoritesController.sharedInstance.isFavorite(item: currentItem)
                albumArtworkImageView?.kf.setImage(with: URL(string: currentItem.thumbnailURL),
                                                   placeholder: UIImage(named: "album-art-default-large-noshadow"))
                let processor = BlackWhiteProcessor()

                blurryBackgroundAlbumArtworkImageView?.kf.setImage(with: URL(string: currentItem.thumbnailURL),
                                                                   placeholder: UIImage(named: "album-art-default-large-noshadow")?.grayscaleImage(),
                                                                   options: [.processor(processor)])

            }

            if isFavorite {
                favoriteButton?.makeAccessible(Accessibility.NowPlayingScreen.removeFavoriteButton)
                favoriteButton?.setImage(UIImage(named: "btn-favorite-selected" ), for: .normal)
                favoriteButton?.setImage(UIImage(named: "btn-favorite-selected-pressed" ), for: .selected)
                favoriteButton?.setImage(UIImage(named: "btn-favorite-selected-pressed" ), for: .highlighted)
            } else {
                favoriteButton?.makeAccessible(Accessibility.NowPlayingScreen.makeFavoriteButton)
                favoriteButton?.setImage(UIImage(named: "btn-favorite" ), for: .normal)
                favoriteButton?.setImage(UIImage(named: "btn-favorite-pressed" ), for: .selected)
                favoriteButton?.setImage(UIImage(named: "btn-favorite-pressed" ), for: .highlighted)
            }

            if isShuffle {
                shuffleButton?.makeAccessible(Accessibility.NowPlayingScreen.disableShuffleButton)
                shuffleButton?.setImage(UIImage(named: "btn-shuffle-selected" ), for: .normal)
                shuffleButton?.setImage(UIImage(named: "btn-shuffle-selected-pressed" ), for: .selected)
                shuffleButton?.setImage(UIImage(named: "btn-shuffle-selected-pressed" ), for: .highlighted)
            } else {
                shuffleButton?.makeAccessible(Accessibility.NowPlayingScreen.enableShuffleButton)
                shuffleButton?.setImage(UIImage(named: "btn-shuffle" ), for: .normal)
                shuffleButton?.setImage(UIImage(named: "btn-shuffle-pressed" ), for: .selected)
                shuffleButton?.setImage(UIImage(named: "btn-shuffle-pressed" ), for: .highlighted)
            }

            switch repeatType {
            case .none:
                repeatButton?.makeAccessible(Accessibility.NowPlayingScreen.repeatButtonOff)
                repeatButton?.setImage(UIImage(named: "btn-repeat" ), for: .normal)
                repeatButton?.setImage(UIImage(named: "btn-repeat-pressed" ), for: .selected)
                repeatButton?.setImage(UIImage(named: "btn-repeat-pressed" ), for: .highlighted)
            case .one:
                repeatButton?.makeAccessible(Accessibility.NowPlayingScreen.repeatButtonOneSelected)
                repeatButton?.setImage(UIImage(named: "btn-repeat-once-selected" ), for: .normal)
                repeatButton?.setImage(UIImage(named: "btn-repeat-once-selected-pressed" ), for: .selected)
                repeatButton?.setImage(UIImage(named: "btn-repeat-once-selected-pressed" ), for: .highlighted)
            case .all:
                repeatButton?.makeAccessible(Accessibility.NowPlayingScreen.repeatButtonAllSelected)
                repeatButton?.setImage(UIImage(named: "btn-repeat-selected" ), for: .normal)
                repeatButton?.setImage(UIImage(named: "btn-repeat-selected-pressed" ), for: .selected)
                repeatButton?.setImage(UIImage(named: "btn-repeat-selected-pressed" ), for: .highlighted)
            @unknown default:
                fatalError()
            }
        }

        emptyView?.alpha = showEmptyView ? 1.0 : 0.0
        
        updateProgress()
        updateVolume()
    }
	
	private func canSkipForward() -> Bool {
		guard let z = playerGroup,
			z.isInterruptible,
			z.isNextEnabled else {
				return false
		}
		
		return true
	}
	
	private func canSkipBackward() -> Bool {
		guard let z = playerGroup,
			z.isInterruptible,
            z.isPreviousEnabled else {
				return false
		}
		
		return true
	}
	
	private func canPlayPause() -> Bool {
		guard let z = playerGroup,
			z.isInterruptible,
			z.isPauseEnabled else {
				return false
		}
		
		return true
	}
	
    private func canSeek() -> Bool {
        guard let z = playerGroup,
            z.isInterruptible,
            z.isSeekEnabled,
            let currentItem = z.playlist?.currentItem,
            !currentItem.isEmpty,
            currentItem.duration > 0
        else {
                return false
        }

        return true
    }
    
    @objc private func progressTimerFired(_ timer: Timer) {
        guard let unwrappedPlayerGroup = playerGroup else {
            return
        }
        if unwrappedPlayerGroup.playState == .playing {
            updateProgress()
        }
    }
    
    private func updateProgress() {
        guard let unwrappedPlayerGroup = playerGroup else {
            return
        }

        if progressSlider!.isTracking {
            DDLogVerbose("Volume Slider is Tracking - not updating")
            return
        }
        
        var progress: Float = 0.0
        
        if let currentItem = unwrappedPlayerGroup.playlist?.currentItem,
            !currentItem.isEmpty {
            let duration = currentItem.duration // in ms
            if (duration > 0) {
                let position = unwrappedPlayerGroup.playerPosition
                progress = min(Float(position) / Float(duration), 1.0)
            }
        }
        
        progressSlider?.value = progress
    }
    
    private func updateVolume() {
        guard let unwrappedPlayerGroup = playerGroup else {
            return
        }

        if volumeSlider!.isTracking {
            DDLogVerbose("Volume Slider is Tracking - not updating")
            return
        }
        
        var volume: Float = 0.0
        
        let maxVolume = unwrappedPlayerGroup.maxVolume
        if (maxVolume > 0) {
            let currentVolume = unwrappedPlayerGroup.volume
            volume = min(Float(currentVolume) / Float(maxVolume), 1.0)
        }
        
        volumeSlider?.value = volume
    }

    private func skipBackward() {
        guard let playerGroup = self.playerGroup else {
            return
        }

        playerGroup.previous(completion: { (success, e) in
            if !success, let error = e {
                DDLogError("Error on previous: \(error)")
            }
        }, completionQueue: DispatchQueue.main)
    }

    private func skipForward() {
        guard let playerGroup = self.playerGroup else {
            return
        }

        playerGroup.next(completion: { (success, e) in
            if !success, let error = e {
                DDLogError("Error on next: \(error)")
            }
        }, completionQueue: DispatchQueue.main)
    }
}
