//
//  ViewController.swift
//  SwiftAudioPlayer
//
//  Created by tanhakabir on 01/28/2019.
//  Copyright (c) 2019 tanhakabir. All rights reserved.
//

import AVFoundation
import SwiftAudioPlayer
import UIKit

class ViewController: UIViewController {
  var selectedAudio: AudioInfo = AudioInfo(index: 0)

  var freq: [Int] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  @IBOutlet weak var currentUrlLocationLabel: UILabel!
  @IBOutlet weak var bufferProgress: UIProgressView!
  @IBOutlet weak var scrubberSlider: UISlider!

  @IBOutlet weak var playPauseButton: UIButton!
  @IBOutlet weak var skipBackwardButton: UIButton!
  @IBOutlet weak var skipForwardButton: UIButton!

  @IBOutlet weak var audioSelector: UISegmentedControl!
  @IBOutlet weak var streamButton: UIButton!
  @IBOutlet weak var downloadButton: UIButton!
  @IBOutlet weak var rateSlider: UISlider!

  @IBOutlet weak var rateLabel: UILabel!

  @IBOutlet weak var reverbLabel: UILabel!
  @IBOutlet weak var reverbSlider: UISlider!
  @IBOutlet weak var durationLabel: UILabel!
  @IBOutlet weak var currentTimestampLabel: UILabel!

  var isDownloading: Bool = false
  var isStreaming: Bool = false
  var beingSeeked: Bool = false
  var loopEnabled = false

  var downloadId: UInt?
  var durationId: UInt?
  var bufferId: UInt?
  var playingStatusId: UInt?
  var queueId: UInt?
  var elapsedId: UInt?

  var duration: Double = 0.0
  var playbackStatus: SAPlayingStatus = .paused

  var lastPlayedAudioIndex: Int?

  var isPlayable: Bool = false {
    didSet {
      if isPlayable {
        playPauseButton.isEnabled = true
        skipBackwardButton.isEnabled = true
        skipForwardButton.isEnabled = true
      } else {
        playPauseButton.isEnabled = false
        skipBackwardButton.isEnabled = false
        skipForwardButton.isEnabled = false
      }
    }
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    SAPlayer.Downloader.allowUsingCellularData = true
    SAPlayer.shared.HTTPHeaderFields = ["User-Agent": "foobar"]

    //        SAPlayer.shared.DEBUG_MODE = true

    isPlayable = false
    checkIfAudioDownloaded()
    selectAudio(atIndex: 0)

    //        addRandomModifiers()

    subscribeToChanges()
  }

  func addRandomModifiers() {
    let node = AVAudioUnitReverb()
    SAPlayer.shared.audioModifiers.append(node)
    node.wetDryMix = 300
    let frequency: [Int] = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]
    let node2 = AVAudioUnitEQ(numberOfBands: frequency.count)
    node2.globalGain = 1
    for i in 0...(node2.bands.count - 1) {
      node2.bands[i].frequency = Float(frequency[i])
      node2.bands[i].gain = 0
      node2.bands[i].bypass = false
      node2.bands[i].filterType = .parametric
    }
    SAPlayer.shared.audioModifiers.append(node2)
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }

  @IBAction func audioSelected(_ sender: Any) {
    let selected = audioSelector.selectedSegmentIndex

    selectAudio(atIndex: selected)
  }

  func selectAudio(atIndex i: Int) {
    selectedAudio.setIndex(i)

    if selectedAudio.savedUrl != nil {
      downloadButton.isEnabled = true
      downloadButton.setTitle("Delete downloaded", for: .normal)
      streamButton.isEnabled = false
    } else {
      downloadButton.isEnabled = true
      downloadButton.setTitle("Download", for: .normal)
      streamButton.isEnabled = true
    }
  }

  func checkIfAudioDownloaded() {
    for i in 0...2 {
      if let savedUrl = SAPlayer.Downloader.getSavedUrl(
        forRemoteUrl: selectedAudio.getUrl(atIndex: i))
      {
        selectedAudio.addSavedUrl(savedUrl, atIndex: i)
      }
    }
  }

  func subscribeToChanges() {
    durationId = SAPlayer.Updates.Duration.subscribe { [weak self] (duration) in
      guard let self = self else { return }
      self.durationLabel.text = SAPlayer.prettifyTimestamp(duration)
      self.duration = duration
    }

    elapsedId = SAPlayer.Updates.ElapsedTime.subscribe { [weak self] (position) in
      guard let self = self else { return }

      self.currentTimestampLabel.text = SAPlayer.prettifyTimestamp(position)

      guard self.duration != 0 else { return }

      self.scrubberSlider.value = Float(position / self.duration)
    }

    downloadId = SAPlayer.Updates.AudioDownloading.subscribe { [weak self] (url, progress) in
      guard let self = self else { return }
      guard url == self.selectedAudio.url else { return }

      if self.isDownloading {
        DispatchQueue.main.async {
          UIView.performWithoutAnimation {
            self.downloadButton.setTitle(
              "Cancel \(String(format: "%.2f", (progress * 100)))%", for: .normal)
          }
        }
      }
    }

    bufferId = SAPlayer.Updates.StreamingBuffer.subscribe { [weak self] (buffer) in
      guard let self = self else { return }

      self.bufferProgress.progress = Float(buffer.bufferingProgress)

      if buffer.bufferingProgress >= 0.99 {
        self.streamButton.isEnabled = false
      } else {
        self.streamButton.isEnabled = true
      }

      self.isPlayable = buffer.isReadyForPlaying
    }

    playingStatusId = SAPlayer.Updates.PlayingStatus.subscribe { [weak self] (playing) in
      guard let self = self else { return }

      self.playbackStatus = playing

      switch playing {
      case .playing:
        self.isPlayable = true
        self.playPauseButton.setTitle("Pause", for: .normal)
        return
      case .paused:
        self.isPlayable = true
        self.playPauseButton.setTitle("Play", for: .normal)
        return
      case .buffering:
        self.isPlayable = false
        self.playPauseButton.setTitle("Loading", for: .normal)
        return
      case .ended:
        if !self.loopEnabled {
          self.isPlayable = false
          self.playPauseButton.setTitle("Done", for: .normal)
        }
        return
      }
    }

    queueId = SAPlayer.Updates.AudioQueue.subscribe { [weak self] forthcomingPlaybackUrl in
      guard let self = self else { return }
      /// we update the selected audio. this is a little contrived, but allows us to update outlets
      if let indexFound = self.selectedAudio.getIndex(forURL: forthcomingPlaybackUrl) {
        self.selectAudio(atIndex: indexFound)
      }

      self.currentUrlLocationLabel.text = "\(forthcomingPlaybackUrl.absoluteString)"
    }
  }

  func unsubscribeFromChanges() {
    guard let durationId = self.durationId,
      let elapsedId = self.elapsedId,
      let downloadId = self.downloadId,
      let queueId = self.queueId,
      let bufferId = self.bufferId,
      let playingStatusId = self.playingStatusId
    else { return }

    SAPlayer.Updates.Duration.unsubscribe(durationId)
    SAPlayer.Updates.ElapsedTime.unsubscribe(elapsedId)
    SAPlayer.Updates.AudioDownloading.unsubscribe(downloadId)
    SAPlayer.Updates.AudioQueue.unsubscribe(queueId)
    SAPlayer.Updates.StreamingBuffer.unsubscribe(bufferId)
    SAPlayer.Updates.PlayingStatus.unsubscribe(playingStatusId)
  }

  @IBAction func scrubberStartedSeeking(_ sender: UISlider) {
    beingSeeked = true
  }

  @IBAction func scrubberSeeked(_ sender: Any) {
    let value = Double(scrubberSlider.value) * duration
    SAPlayer.shared.seekTo(seconds: value)
    beingSeeked = false
  }

  @IBAction func rateChanged(_ sender: Any) {
    let speed = rateSlider.value
    rateLabel.text = "rate: \(speed)x"

    if skipSilencesSwitch.isOn {
      SAPlayer.Features.SkipSilences.setRateSafely(speed)  // if using Skip Silences, we need use this version of setting rate to safely change the rate with the feature enabled.
    } else {
      SAPlayer.shared.rate = speed
    }
  }
  @IBAction func reverbChanged(_ sender: Any) {
    let reverb = reverbSlider.value
    reverbLabel.text = "reverb: \(reverb)"
    if let node = SAPlayer.shared.audioModifiers[1] as? AVAudioUnitReverb {
      node.wetDryMix = reverb
    }
  }
  @IBAction func queueTouched(_ sender: Any) {
    if let savedUrl = selectedAudio.savedUrl {
      SAPlayer.shared.queueSavedAudio(withSavedUrl: savedUrl)
    } else {
      SAPlayer.shared.queueRemoteAudio(withRemoteUrl: selectedAudio.url)
    }

    print("queue: \(SAPlayer.shared.audioQueued)")
  }

  @IBAction func downloadTouched(_ sender: Any) {
    if !isDownloading {
      if let savedUrl = SAPlayer.Downloader.getSavedUrl(forRemoteUrl: selectedAudio.url) {
        SAPlayer.Downloader.deleteDownloaded(withSavedUrl: savedUrl)
        selectedAudio.deleteSavedUrl()
        downloadButton.setTitle("Download", for: .normal)
        streamButton.isEnabled = true
        isDownloading = false
      } else {
        downloadButton.setTitle("Cancel 0%", for: .normal)
        isDownloading = true
        SAPlayer.Downloader.downloadAudio(
          withRemoteUrl: selectedAudio.url,
          completion: { [weak self] (url, error) in
            guard let self = self else { return }
            guard error == nil else {
              DispatchQueue.main.async {
                self.currentUrlLocationLabel.text = "ERROR! \(error!.localizedDescription)"
              }
              return
            }

            DispatchQueue.main.async {
              self.currentUrlLocationLabel.text = "saved to: \(url.lastPathComponent)"
              self.selectedAudio.addSavedUrl(url)
            }
          })
        streamButton.isEnabled = false
      }
    } else {
      SAPlayer.Downloader.cancelDownload(withRemoteUrl: selectedAudio.url)
      downloadButton.setTitle("Download", for: .normal)
      streamButton.isEnabled = true
      isDownloading = false
    }
  }

  @IBAction func streamTouched(_ sender: Any) {
    if !isStreaming {
      self.currentUrlLocationLabel.text = "remote url: \(selectedAudio.url.absoluteString)"
      if selectedAudio.index == 2 {  // radio
        SAPlayer.shared.startRemoteAudio(
          withRemoteUrl: selectedAudio.url, bitrate: .low, mediaInfo: selectedAudio.lockscreenInfo)
      } else {
        SAPlayer.shared.startRemoteAudio(
          withRemoteUrl: selectedAudio.url, mediaInfo: selectedAudio.lockscreenInfo)
      }

      lastPlayedAudioIndex = selectedAudio.index
      streamButton.setTitle("Cancel streaming", for: .normal)
      downloadButton.isEnabled = false
      isStreaming = true
    } else {
      SAPlayer.shared.stopStreamingRemoteAudio()
      streamButton.setTitle("Stream", for: .normal)
      downloadButton.isEnabled = true
      isStreaming = false
    }
  }

  @IBAction func playPauseTouched(_ sender: Any) {
    SAPlayer.shared.togglePlayAndPause()
  }

  @IBAction func skipBackwardTouched(_ sender: Any) {
    SAPlayer.shared.skipBackwards()
  }

  @IBAction func skipForwardTouched(_ sender: Any) {
    SAPlayer.shared.skipForward()
  }
  @IBAction func setEqualizerValue(_ sender: Any) {
    if let slider = sender as? UISlider {
      print("slider of index:", slider.tag, "is changed to", slider.value)
      freq[slider.tag] = Int(slider.value)
      print("current frequency : ", freq)
      if let node = SAPlayer.shared.audioModifiers[2] as? AVAudioUnitEQ {
        for i in 0...(node.bands.count - 1) {
          node.bands[i].gain = Float(freq[i])
        }
      }
    }

  }

  @IBOutlet weak var skipSilencesSwitch: UISwitch!

  @IBAction func skipSilencesSwitched(_ sender: Any) {
    if skipSilencesSwitch.isOn {
      _ = SAPlayer.Features.SkipSilences.enable()
    } else {
      _ = SAPlayer.Features.SkipSilences.disable()
    }
  }
  @IBOutlet weak var sleepSwitch: UISwitch!

  @IBAction func sleepSwitched(_ sender: Any) {
    if sleepSwitch.isOn {
      _ = SAPlayer.Features.SleepTimer.enable(afterDelay: 5.0)
    } else {
      _ = SAPlayer.Features.SleepTimer.disable()
    }
  }

  @IBOutlet weak var loopSwitch: UISwitch!

  @IBAction func loopSwitched(_ sender: Any) {
    loopEnabled = loopSwitch.isOn

    if loopSwitch.isOn {
      SAPlayer.Features.Loop.enable()
    } else {
      SAPlayer.Features.Loop.disable()
    }

  }
}
