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

import UIKit
import CocoaLumberjack

class QueueDataSourceDelegate {
	// MARK: Public Properties
	var onItemsInvalidated: (() -> ())? // This is for MusicListDataSourceDelegate
	
	// MARK: Private Properties
	private(set) var expectingPlaylistChanges = false
	private(set) var currentPlayingIndex: Int = -1 // This is for MusicListDataSourceQueueOperationsDelegate
	private let internalQueue = DispatchQueue(label: "com.qualcomm.qti.QueueDataSourceDelegate.internal")
	private let playerGroup: IoTPlayerGroup
	
	// MARK: Initializers
	init (playerGroup: IoTPlayerGroup) {
		self.playerGroup = playerGroup
		updateCurrentPlayingItem()
	}
}

// MARK: - Public Methods
extension QueueDataSourceDelegate {
	func updateCurrentPlayingItem() {
		currentPlayingIndex = Int(playerGroup.playlist?.indexPlaying ?? -1)
	}
	
	func forceReload() {
		if let handler = onItemsInvalidated {
			handler()
		}
	}
}

// MARK: - MusicListDataSourceDelegate Conformance
extension QueueDataSourceDelegate: MusicListDataSourceDelegate {
	var title: String { return NSLocalizedString("Queue", comment: "Queue Title") }
	
    func itemsInitialState(completion: @escaping ([MusicListDisplayHelperItem]) -> () , completionQueue: DispatchQueue) -> () {
        mediaItemsForPlayerGroupPlaylist(completion: { mediaItems in
            var array = [MusicListDisplayHelperItem]()
            array.reserveCapacity(mediaItems.count)
            for i in 0..<mediaItems.count {
                array.append(MediaItemMusicListDisplayHelperItem(mediaItem: mediaItems[i],
                                                                 accessory: .options,
                                                                 options: []))
            }

            completion(array)
            }, completionQueue: completionQueue)
    }
	
	func optionsForItem(_ item: MusicListDisplayHelperItem, atIndex index: Int) -> [String] {
		guard let mlItem = item as? MediaItemMusicListDisplayHelperItem else {
			return []
		}
		
		let mediaItem = mlItem.mediaItem
		var options = [MusicLibraryItemOptions.playNow.toString(),
					   MusicLibraryItemOptions.playNext.toString()]
		if !FavoritesController.sharedInstance.isFavorite(item: mediaItem) {
			options.append(MusicLibraryItemOptions.addToFavorites.toString())
		}
		options.append(MusicLibraryItemOptions.removeFromQueue.toString())
		return options
	}
	
	
	func accessoryForItem(_ item: MusicListDisplayHelperItem, atIndex index: Int) -> MusicListDisplayHelperItemAccessory {
		if index == currentPlayingIndex {
			return (playerGroup.playState == .playing) ? .playing : .current
		} else {
			return .options
		}
	}
	
	func mediaItemForItem(_ item: MusicListDisplayHelperItem, atIndex index: Int) -> IoTMediaItem? {
		if let mlItem = item as? MediaItemMusicListDisplayHelperItem {
			return mlItem.mediaItem
		}
		return nil
	}
}

// MARK: - MusicListDataSourceQueueOperationsDelegate Conformance
extension QueueDataSourceDelegate: MusicListDataSourceQueueOperationsDelegate {
	func playItem(_ playlist: IoTPlaylist,
				  atIndex index: Int,
				  now: Bool,
				  completion: @escaping MusicListDataSourceOperationCompletionHandler,
                  queue: DispatchQueue) {
        if now {
            playlist.playItem(at: Int32(index),
                              completion: { [weak self] (success, error) in
                                guard let self = self else { return }
                                
                                self.updateCurrentPlayingItem()
                                queue.async {
                                    completion(success, error)
                                }
                }, completionQueue: internalQueue)
        } else {
			// It actually a move - we need to generate to index in same way as tableview move.

			let to = index > currentPlayingIndex ? currentPlayingIndex + 1 : currentPlayingIndex
            if index != to {
                moveItem(playlist,
                         from: index,
                         to: to,
                         completion: completion,
                         queue: queue)
            } else {
                queue.async {
                    completion(true, nil)
                }
            }
        }
	}
	
	func removeItemFromQueue(_ playlist: IoTPlaylist,
							 atIndex index: Int,
							 completion: @escaping (Bool, Error?) -> (),
							 queue: DispatchQueue) {
		expectingPlaylistChanges = true
		QueueOperationsHelper.sharedInstance.remove(itemAtIndex: index,
													fromPlaylist: playlist,
													completion: { [weak self] (success, error) in
														guard let self = self else { return }
														self.expectingPlaylistChanges = false
														self.updateCurrentPlayingItem()
														queue.async {
															completion(success, error)
														}
			},
													completionQueue: internalQueue)
	}

	func moveItem(_ playlist: IoTPlaylist,
				  from: Int,
				  to: Int,
				  completion: @escaping (Bool, Error?) -> (),
				  queue: DispatchQueue) {
		expectingPlaylistChanges = true
		QueueOperationsHelper.sharedInstance.move(itemAtIndex: from,
												  toIndex: to,
												  playlist: playlist,
												  completion: { [weak self] (success, error) in
													guard let self = self else { return }
													self.expectingPlaylistChanges = false
													self.updateCurrentPlayingItem()
													queue.async {
														completion(success, error)
													}
			},
												  completionQueue: internalQueue)
	}
}

// MARK: - Private Methods
extension QueueDataSourceDelegate {
    private func mediaItemsForPlayerGroupPlaylist(completion: @escaping ([IoTMediaItem]) -> (), completionQueue: DispatchQueue) -> () {
        if let playlist = playerGroup.playlist {
            QueueOperationsHelper.sharedInstance.allItems(playlist: playlist,
                                                          completion: { (items, error) in
                                                            if items != nil {
                                                                completionQueue.async {
                                                                    completion(items!)
                                                                }
                                                            }
            }, completionQueue: self.internalQueue)
        }
    }
}

