//
//  SoundFX.swift
//  
//  Copyright © 2016-2019 Apple Inc. All rights reserved.
//

import Foundation
import AVFoundation
import PlaygroundSupport

var audioController = AudioController()

class AudioController: NSObject, AVAudioPlayerDelegate {
    
    private let audioEnabledKey = "AudioEnabled"
    
    var activeAudioPlayers = Set<AVAudioPlayer>()
    
    private var backgroundAudioPlayer: AVAudioPlayer?
    
    var isAudioEnabled: Bool {
        get {
            if case let .boolean(enabled)? = PlaygroundKeyValueStore.current[audioEnabledKey] {
                return enabled
            }
            return true
        }
        set {
            PlaygroundKeyValueStore.current[audioEnabledKey] = PlaygroundValue.boolean(newValue)
            
            if !newValue {
                stopAllPlayers()
            }
        }
    }
    
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        activeAudioPlayers.remove(player)
    }
    
    func register(_ player: AVAudioPlayer) {
        activeAudioPlayers.insert(player)
        player.delegate = self
    }
    
    func stopAllPlayers() {
        activeAudioPlayers.forEach { $0.stop() }
        activeAudioPlayers.removeAll()
    }
    
    func playBackgroundAudioLoop(_ sound: Sound, volume: Int = 80) {
        guard let url = sound.url, isAudioEnabled else { return }
        
        if let _ = backgroundAudioPlayer {
            stopBackgroundAudioLoop()
        }
        
        do {
            let audioPlayer = try AVAudioPlayer(contentsOf: url)
            audioPlayer.volume = Float(max(min(volume, 100), 0)) / 100.0
            register(audioPlayer)
            audioPlayer.numberOfLoops = -1
            audioPlayer.play()
            backgroundAudioPlayer = audioPlayer
        } catch {}
    }
    
    func pauseBackgroundAudioLoop() {
        guard let audioPlayer = backgroundAudioPlayer else { return }
        audioPlayer.pause()
    }
    
    func resumeBackgroundAudioLoop() {
        guard let audioPlayer = backgroundAudioPlayer else { return }
        audioPlayer.play()
    }
    
    func stopBackgroundAudioLoop() {
        guard let audioPlayer = backgroundAudioPlayer else { return }
        audioPlayer.stop()
        activeAudioPlayers.remove(audioPlayer)
        backgroundAudioPlayer = nil
    }
}

/// Plays the given sound. Optionally specify a volume from 0 (silent) to 100 (loudest), with 80 being the default.
///
/// - Parameter sound: The sound to be played.
/// - Parameter volume: The volume at which the sound is to be played (0 to 100).
/// - Parameter waitUntilDone: Pause execution until the sound has finished playing. Default is `false`.
///
/// - localizationKey: playSound(_:volume:waitUntilDone:)
public func playSound(_ sound: Sound, volume: Int = 80, waitUntilDone: Bool = false) {
    
    guard let url = sound.url, audioController.isAudioEnabled else { return }
    
    do {
        let audioPlayer = try AVAudioPlayer(contentsOf: url)
        audioPlayer.volume = Float(max(min(volume, 100), 0)) / 100.0
        audioController.register(audioPlayer)
        audioPlayer.play()
        
        if waitUntilDone {
            repeat {
                RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.25))
            } while (audioController.activeAudioPlayers.contains(audioPlayer))
        }
    } catch {}
}

/// An enumeration of all the different sounds that can be played.
///
/// - localizationKey: Sound
public enum Sound: String, Codable {
    
    case bark, clank, entering3, entering4, equipped1, equipped2, lightSwitchOn, switch1, switch2, itemRemoved1, itemRemoved2, cameraClick
    
    var url : URL? {
        
        var fileName: String?
        
        switch self {
        case .bark:
            fileName = "Bark"
        case .clank:
            fileName = "Clank"
        case .entering3:
            fileName = "entering1"
        case .entering4:
            fileName = "entering2"
        case .equipped1:
            fileName = "itemEquipped1"
        case .equipped2:
            fileName = "itemEquipped2"
        case .lightSwitchOn:
            fileName = "Light_Switch_On"
        case .switch1:
            fileName = "Switch_001"
        case .switch2:
            fileName = "Light_Switch_001"
        case .itemRemoved1:
            fileName = "itemRemoved1"
        case .itemRemoved2:
            fileName = "itemRemoved2"
        case .cameraClick:
            fileName = "cameraShutterClick"
        }
        guard let resourceName = fileName else { return nil }
        
        return Bundle.main.url(forResource: resourceName, withExtension: "m4a")
    }
}

public struct SoundFX {
}
