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

import Foundation
import SPCCore
import SPCIPC
import AVFoundation

let speech = Speech()

let audioPlayerQueue = DispatchQueue(label: "com.apple.audioPlayerQueue")

/// Speaks the given text.
///
/// - Parameter text: The text to be spoken.
/// - Parameter voice: The voice in which to speak the text. Leave out to use the default voice.
///
/// - localizationKey: speak(_:voice:)
public func speak(_ text: String, voice: SpeechVoice = SpeechVoice()) {
    speech.speak(text, voice: voice)
}

/// Stops any speech that’s currently being spoken.
///
/// - localizationKey: stopSpeaking()
public func stopSpeaking() {
    speech.stopSpeaking()
}

/// 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 (ranging from `0` to `100`).
///
/// - localizationKey: playSound(_:volume:)
public func playSound(_ sound: Sound, volume: Int = 80) {
    if !AudioSession.current.isPlaybackBlocked && audioController.isSoundEffectsAudioEnabled {
        audioPlayerQueue.async {
                if let url = sound.url {
                    do {
                        let audioPlayer = try AVAudioPlayer(contentsOf: url)
                        audioPlayer.volume = Float(max(min(volume, 100), 0)) / 100.0
                        audioController.register(audioPlayer)
                        audioPlayer.play()
                    } catch {}
                }
        }
    }
}

/// Plays the given music. Optionally, specify a volume from `0` (silent) to `100` (loudest), with `75` being the default.
///
/// - Parameter music: The music to be played.
/// - Parameter volume: The volume at which the music is to be played (ranging from `0` to `100`).
///
/// - localizationKey: playMusic(_:volume:)
public func playMusic(_ music: Music, volume: Int = 75) {
    if !audioController.isBackgroundAudioLoopPlaying {
        audioController.playBackgroundAudioLoop(music, volume: volume)
    } else if let background = audioController.backgroundAudioMusic, music != background {
        audioController.playBackgroundAudioLoop(music, volume: volume)
    } else {
        audioController.adjustBackgroundAudioLoop(volume: volume)
    }
}

private var instruments = [Instrument.Kind: Instrument]()
private var instrumentsEngine: AudioPlayerEngine = {
    let audioPlayerEngine = AudioPlayerEngine()
    audioPlayerEngine.start()
    return audioPlayerEngine
}()

private func createInstrument(_ kind: Instrument.Kind) -> Instrument {
    let instrument = Instrument(kind: kind)
    instrument.connect(instrumentsEngine)
    instrument.defaultVelocity = 64
    return instrument
}

/// Plays a note with the given instrument. Optionally, specify a volume from `0` (silent) to `100` (loudest), with `75` being the default.
///
/// - Parameter instrumentKind: The kind of instrument with which to play the note.
/// - Parameter note: The note to be played (ranging from `0` to `23`).
/// - Parameter volume: The volume at which the note is played (ranging from `0` to `100`).
///
/// - Important: Each instrument has a different range of notes available:
///  * **electricGuitar**: `0...15`
///  * **bassGuitar**: `0...14`
///  * **warmBells, sevenSynth,  bassSynth, crystalSynth**: `0...19`
///  * **piano**: `0...23`
/// - localizationKey: playInstrument(_:note:volume:)
public func playInstrument(_ instrumentKind: Instrument.Kind, note: Double, volume: Double = 75) {
    if audioController.isSoundEffectsAudioEnabled {
        if instruments[instrumentKind] == nil {
            instruments[instrumentKind] = createInstrument(instrumentKind)
        }
        guard let instrument = instruments[instrumentKind] else { return }
        
        // Get corresponding MIDI note value.
        let noteIndex = min(max(Int(note), 0), instrument.availableNotes.count - 1)
        
        let velocity = Double(max(min(Int(volume), 100), 0)) / 100.0 * 127.0
        
        instrument.startPlaying(noteValue: instrument.availableNotes[noteIndex], withVelocity: UInt8(velocity), onChannel: 0)
    }
}


/// Plays a note with the given instrument. Optionally, specify a volume from `0` (silent) to `127` (loudest), with `75` being the default.
///
/// - Parameter kind: The kind of instrument with which to play the note.
/// - Parameter note: The MIDI code for the note to be played.
/// - Parameter volume: The volume at which the note is played (ranging from `0` to `127`).
///
/// - Important: Each instrument has a different range of notes available:
///  * **electricGuitar**: `0...15`
///  * **bassGuitar**: `0...14`
///  * **warmBells, sevenSynth,  bassSynth, crystalSynth**: `0...19`
///  * **piano**: `0...23`

public func playInstrument(_ kind: Instrument.Kind, note: UInt8, volume: UInt8 = 95) {
    guard audioController.isSoundEffectsAudioEnabled else { return }
        
    if instruments[kind] == nil {
        instruments[kind] = createInstrument(kind)
    }
    guard let instrument = instruments[kind] else { return }
        
    instrument.startPlaying(noteValue: note, withVelocity: volume, onChannel: 0)
    
}
