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

import Foundation
import MediaPlayer
import CocoaLumberjack

class LocalMusicLibrary {
	// MARK: Public Properties
	static let sharedInstance = LocalMusicLibrary()
	
	// MARK: Private Properties
	private let exporter = LocalMusicLibraryExporter()
	private let cache = LocalMusicLibraryCache()
	
	// MARK: Initializer
	private init () {}
}

// MARK: - Public Methods
extension LocalMusicLibrary {
	func imageDataForSong(id: UInt64, size: CGSize) -> Data? {
		let predicate = MPMediaPropertyPredicate(value: id, forProperty: MPMediaItemPropertyPersistentID)
		guard let image = artwork(predicate: predicate, size: size) else {
			return nil
		}
		
		let data = image.jpegData(compressionQuality: 0.9)
		return data
	}
	
	func dataForSong(id: UInt64,
							 completion: @escaping (_ data: Data?, _ mimeType: String?) -> ()){
		let predicate = MPMediaPropertyPredicate(value: id, forProperty: MPMediaItemPropertyPersistentID)
		let songQuery = MPMediaQuery.songs()
		songQuery.addFilterPredicate(predicate)
		guard let song = songQuery.items?.first,
			let assetURL = song.assetURL else {
				completion (nil, nil)
				return
		}
		
		DDLogVerbose("************ Data requested for song: \(String (describing: song.title))")
		
		exporter.export(id: id, assetURL: assetURL, completion: completion)
	}
}

// MARK: - MusicLibraryProtocol Conformance
extension LocalMusicLibrary: MusicLibraryProtocol {
	func allAlbums() -> [MusicLibraryAlbum] {
		let albums = cache.allAlbums(searchString: nil)
		return albums.map { $0.toMusicLibraryAlbum() }
	}
	
	func allArtists() -> [MusicLibraryArtist] {
		let artists = cache.allArtists(searchString: nil)
		return artists.map { $0.toMusicLibraryArtist() }
	}
	
	func allGenres() -> [MusicLibraryGenre] {
		let genres = cache.allGenres(searchString: nil)
		return genres.map { $0.toMusicLibraryGenre() }
	}
	
	func allSongs() -> [MusicLibrarySong] {
		return allSongs(searchString: nil)
	}
	
	func allAlbums(searchString: String?) -> ([MusicLibraryAlbum], [MusicLibrarySong]) {
		let albums = cache.allAlbums(searchString: searchString)
		let convertedAlbums = albums.map { $0.toMusicLibraryAlbum() }
		var songs = Set<MusicLibrarySong> ()
		albums.forEach { album in
			album.items.forEach { song in
				songs.insert(song.toMusicLibrarySong())
			}
		}
		
		return (convertedAlbums, Array(songs))
	}
	
	func allArtists(searchString: String?) -> ([MusicLibraryArtist], [MusicLibrarySong]) {
		let artists = cache.allArtists(searchString: searchString)
		let convertedArtists = artists.map { $0.toMusicLibraryArtist() }
		var songs = Set<MusicLibrarySong> ()
		artists.forEach { artist in
			artist.items.forEach { song in
				songs.insert(song.toMusicLibrarySong())
			}
		}
		
		return (convertedArtists, Array(songs))
	}
	
	func allGenres(searchString: String?) -> ([MusicLibraryGenre], [MusicLibrarySong]) {
		let genres = cache.allGenres(searchString: searchString)
		let convertedGenres = genres.map { $0.toMusicLibraryGenre() }
		var songs = Set<MusicLibrarySong> ()
		genres.forEach { genre in
			genre.items.forEach { song in
				songs.insert(song.toMusicLibrarySong())
			}
		}
		
		return (convertedGenres, Array(songs))
	}
	
	func allSongs(searchString: String?) -> [MusicLibrarySong] {
		let songs = cache.allSongs(searchString: searchString)
		return songs.map { $0.toMusicLibrarySong() }
	}
	
	func albumsForArtist(_ artist: MusicLibraryArtist) -> [MusicLibraryAlbum] {
		let id = artist.id
		let albums = cache.allAlbums(artistID: id)
		return albums.map { $0.toMusicLibraryAlbum() }
	}
	
	func songsForAlbum(_ album: MusicLibraryAlbum) -> [MusicLibrarySong] {
		let id = album.id
		let songs = cache.allSongs(albumID: id)
		return songs.map { $0.toMusicLibrarySong() }
	}
	
	func albumsForGenre(_ genre: MusicLibraryGenre) -> [MusicLibraryAlbum]{
		let id = genre.id
		let albums = cache.allAlbums(genreID: id)
		return albums.map { $0.toMusicLibraryAlbum() }
	}
	
	func mediaItemForSong(_ song: MusicLibrarySong) -> IoTMediaItem? {
		if let retrievedSong = cache.song(id: song.id) {
			
			let mediaItem = IoTMediaItem()
			mediaItem.title = retrievedSong.title
			mediaItem.artist = retrievedSong.artist
			mediaItem.album = retrievedSong.albumTitle
			mediaItem.genre = retrievedSong.genre
			mediaItem.duration = Int32(retrievedSong.duration * 1000)
			
			guard
				let streamURL = HTTPServer.sharedInstance.urlForLocalAsset(id: retrievedSong.id, type: .audio),
				let imageURL = HTTPServer.sharedInstance.urlForLocalAsset(id: retrievedSong.id, type: .artwork)
				else {
					return mediaItem
			}
			mediaItem.streamURL = streamURL
			mediaItem.thumbnailURL = imageURL
			return mediaItem
		}
		
		return nil
	}
}

// MARK: - Private Methods
extension LocalMusicLibrary {
	private func artwork(predicate: MPMediaPropertyPredicate, size: CGSize) -> UIImage? {
		let songQuery = MPMediaQuery.songs()
		songQuery.addFilterPredicate(predicate)
		if let song = songQuery.items?.first {
			return song.artwork?.image(at: size)
		} else {
			return nil
		}
	}
}

