import Foundation
import AVFoundation
import RxSwift
import RxRelay

enum PlayerState {
    case stopped
    case paused
    case playing
}

class AudioPlayer: NSObject, AVAudioPlayerDelegate {
    
    static let shared = AudioPlayer()
    
    private var player: AVAudioPlayer?
    private var url: URL?
    private var state = BehaviorRelay<PlayerState>(value: .stopped)
    private var progress = BehaviorRelay<Double>(value: 0)
    private var progressTimer: Timer?
    
    func set(url: URL) throws {
        if let curUrl = self.url, curUrl == url {
            return
        }
        
        self.stop()
        self.url = url
        self.player = try AVAudioPlayer(contentsOf: url)
        self.player?.delegate = self
    }
    
    func play() throws {
        if let player = self.player {
            if player.isPlaying {
                player.pause()
                try self.deactivateSession()
                self.state.accept(.paused)
            } else {
                try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.duckOthers])
                try AVAudioSession.sharedInstance().setActive(true)
                player.play()
                self.state.accept(.playing)
                if self.progressTimer == nil {
                    self.progressTimer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(progressTick), userInfo: nil, repeats: true)
                }
            }
        }
    }
    
    func play(url: URL) throws {
        try self.set(url: url)
        try self.play()
    }
    
    func pause() {
        if let player = self.player {
            player.pause()
            try? self.deactivateSession()
            self.state.accept(.paused)
        }
    }
    
    func stop() {
        if let player = self.player {
            player.stop()
            try? self.deactivateSession()
            self.state.accept(.stopped)
            self.progressTimer?.invalidate()
            self.progressTimer = nil
        }
    }
    
    func getState() -> PlayerState {
        return self.state.value
    }
    
    func getProgress() -> Double {
        return self.progress.value
    }
    
    func getUrl() -> URL? {
        return self.url
    }
    
    func duration() -> TimeInterval {
        return self.player?.duration ?? 0
    }
    
    func stateObservable() -> Observable<PlayerState> {
        return self.state.asObservable()
    }
    
    func progressObservable() -> Observable<Double> {
        return self.progress.asObservable()
    }
    
    func deactivateSession() throws {
        try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
    }
    
    // MARK: - AVAudioPlayerDelegate
    
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        try? self.deactivateSession()
        self.progress.accept(1)
        self.stop()
        self.state.accept(.stopped)
    }
    
    @objc func progressTick() {
        if let player = self.player {
            let progress = player.currentTime/player.duration
            self.progress.accept(progress)
        }
    }
}
