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

import UIKit
import CocoaLumberjack

protocol MiniPlayerTapDelegate: class {
    func miniPlayerDidTapToExpand(_ miniPlayer: MiniPlayerViewController)
}

// MARK: -

class MiniPlayerViewController: UIViewController {
	// MARK: Public Properties
    static let expandedLayoutHeight: CGFloat = 100.0
    var playerGroup: IoTPlayerGroup? {
        didSet {
            updateUI()
        }
    }
    weak var tapDelegate: MiniPlayerTapDelegate?
    weak var containerViewController: UIViewController? = nil
	
	// MARK: Outlets
    @IBOutlet private weak var togglePlayPauseButton: UIButton?
    @IBOutlet private weak var expandButton: UIButton?

    @IBOutlet private weak var songTitleLabel: UILabel?
    @IBOutlet private weak var artistLabel: UILabel?
    @IBOutlet private weak var albumArtworkImageView: UIImageView?
    @IBOutlet private weak var progressView: UIProgressView?
    @IBOutlet private weak var playingIndicatorImageView: UIImageView?
    @IBOutlet private weak var playpauseIndicatorImageView: UIImageView?
	
	// MARK: Private Properties
	private var observers = [ObserverToken] ()
	private weak var progressTimer: Timer?
}

// MARK: - Overridden Methods
extension MiniPlayerViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        expandButton?.makeAccessible(Accessibility.MiniPlayer.expandButton)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        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.updateUI()
        }

        let playerGroupObserver = NotificationCenter.default.addObserver(forType: PlayerGroupNotification.self,
                                                                         object: playerController,
                                                                         queue: OperationQueue.main,
                                                                         using: playerGroupHandler)

        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 = [playerGroupObserver, playlistObserver, playerObserver]

        
        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()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        observers.forEach { NotificationCenter.default.removeObserver($0) }
        observers.removeAll()
        
        progressTimer?.invalidate()
        progressTimer = nil
    }
}

// MARK: - UI Actions
extension MiniPlayerViewController {
	@IBAction private func togglePlayPaused(_ sender: UIButton) {
		guard let z = playerGroup,
            z.isInterruptible else {
				return
		}

        let currentItem = z.playlist?.currentItem
        let isEmpty = currentItem?.isEmpty ?? true
        let playing = !isEmpty && z.playState == .playing
		if playing {
            z.pause(completion: nil, completionQueue: nil)
		} else {
            if z.activeInputSelector?.type == .bluetooth || (z.playlist?.indexPlaying ?? -1 >= 0) {
				z.play(completion: nil, completionQueue: nil)
            } else {
                z.playlist?.playItem(at: 0, completion: nil, completionQueue: nil)
            }
		}
	}
	
	@IBAction private func expand(_ sender: UIButton) {
		self.tapDelegate?.miniPlayerDidTapToExpand(self)
	}
}

// MARK: - Private Methods
extension MiniPlayerViewController {
    private func updateUI() {
        guard let unwrappedPlayerGroup = playerGroup else {
            return
        }

        togglePlayPauseButton?.isEnabled = unwrappedPlayerGroup.isInterruptible
        playpauseIndicatorImageView?.isHidden = !unwrappedPlayerGroup.isInterruptible

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

        let interruptibleAndPlaying = unwrappedPlayerGroup.isInterruptible && !isEmpty && unwrappedPlayerGroup.playState == .playing
        if interruptibleAndPlaying || !unwrappedPlayerGroup.isInterruptible {
            togglePlayPauseButton?.makeAccessible(Accessibility.MiniPlayer.pauseButton)
            playpauseIndicatorImageView?.image = UIImage(named: "ic-pause-album-art")
            playingIndicatorImageView?.loadAnimationImages(nameStem: "playing-animation", count: 30)
            playingIndicatorImageView?.startAnimating()
        } else {
            togglePlayPauseButton?.makeAccessible(Accessibility.MiniPlayer.playButton)
            playpauseIndicatorImageView?.image = UIImage(named: "ic-play-album-art")
            playingIndicatorImageView?.stopAnimating()
            playingIndicatorImageView?.image = UIImage(named: "playing-animation-paused")
        }

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

        if isEmpty {
            songTitleLabel?.makeAccessible("")
            artistLabel?.makeAccessible("")
        } else {
            songTitleLabel?.makeAccessible(localizablePrefix: Accessibility.MiniPlayer.songTitleLabelPrefix, value: currentItem!.title)
            artistLabel?.makeAccessible(localizablePrefix: Accessibility.MiniPlayer.artistLabelPrefix, value: currentItem!.artist)
        }

        if !isEmpty,
            let url = currentItem?.thumbnailURL {
            albumArtworkImageView?.kf.setImage(with: URL(string: url),
                                               placeholder: UIImage(named: "album-art-default-med"))
        } else {
			albumArtworkImageView?.image = UIImage(named: "album-art-default-med")
            
        }

        updateProgress()
    }

    @objc private func progressTimerFired(_ timer: Timer) {
        guard let unwrappedPlayerGroup = playerGroup else {
            return
        }
        if unwrappedPlayerGroup.playState == .playing {
            updateProgress()
        }
    }
    
    private func updateProgress() {
        guard let unwrappedPlayerGroup = playerGroup,
            let playlist = playerGroup?.playlist else {
            return
        }
        
        var progress: Float = 0.0
        
        let currentItem = playlist.currentItem
        if !currentItem.isEmpty {
            let duration = currentItem.duration // in ms
            if (duration > 0) {
                let position = unwrappedPlayerGroup.playerPosition
                progress = min(Float(position) / Float(duration), 1.0)
            }
        }
        
        progressView?.progress = progress
    }
}
