//
//  ViewController.swift
//  DownloaderParallelDemo
//
//

import PRESTOplay
import CastlabsApple
import CastlabsDownloader
import AVFoundation
import UIKit

class TestAssets {
    static let clearStreams = [
        hlsMultiTracks,
        hlsABR,
        hlsABR_advanced,
        hlsMultiTracksABR
    ]

    static let drmStreams = [
        fairplayMediaPlaylist,
        fairplayMasterPlaylistABR,
        fairplayMasterPlaylistNoExtSessionKey
    ]

    static let hlsMultiTracks = 
        PlayerConfiguration(with: URL(string: "https://demo.cf.castlabs.com/media/QA/hls-short-multiaudio-multisub/playlist.m3u8")!)

    static let hlsABR =
        PlayerConfiguration(with: URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8")!)

    static let hlsABR_advanced =
        PlayerConfiguration(with: URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8")!)

    static let hlsMultiTracksABR =
        PlayerConfiguration(with: URL(string: "https://castlabs-dl.s3.amazonaws.com/public/Client/hls/img_bipbop_adv_example_ts/master.m3u8")!)

    static var fairplayMediaPlaylist: PlayerConfiguration {
        let config =
            PlayerConfiguration(
                with: URL(string: "https://demo.cf.castlabs.com/media/fps/prog_index.m3u8")!)
        config.drmConfiguration = DrmConfiguration()
        config.drmType = .drmToday
        config.drmConfiguration?.environment = .staging
        config.drmConfiguration?.userId = "purchase"
        config.drmConfiguration?.sessionId = "session"
        config.drmConfiguration?.merchant = "six"
        config.drmConfiguration?.assetId = "twelve"
        config.drmConfiguration?.variantId = nil
        config.drmConfiguration?.authToken = nil
        config.drmConfiguration?.persistLicense = true
        config.drmSystem = .fairplay
        return config
    }

    static var fairplayMasterPlaylistABR: PlayerConfiguration {
        let config =
            PlayerConfiguration(
                with: URL(
                    string: "https://demo.cf.castlabs.com/public/Client/hls/ioss291-encrypted720/master.m3u8")!)
        config.drmConfiguration = DrmConfiguration()
        config.drmType = .drmToday
        config.drmConfiguration?.environment = .staging
        config.drmConfiguration?.userId = "purchase"
        config.drmConfiguration?.sessionId = "p1"
        config.drmConfiguration?.merchant = "six"
        config.drmConfiguration?.assetId = "FPSOffline"
        config.drmConfiguration?.variantId = nil
        config.drmConfiguration?.authToken = nil
        config.drmConfiguration?.persistLicense = true
        config.drmSystem = .fairplay
        return config
    }

    static var fairplayMasterPlaylistNoExtSessionKey: PlayerConfiguration {
        let config =
            PlayerConfiguration(
                with: URL(
                    string: "https://demo.cf.castlabs.com/media/FPS/hls_aes_fairplay/presentation.m3u8")!)
        config.drmConfiguration = DrmConfiguration()
        config.drmType = .drmToday
        config.drmConfiguration?.environment = .production
        config.drmConfiguration?.userId = "rental1"
        config.drmConfiguration?.sessionId = "p1"
        config.drmConfiguration?.merchant = "six"
        config.drmConfiguration?.assetId = "hls_aes_test_fairplay"
        config.drmConfiguration?.variantId = nil
        config.drmConfiguration?.authToken = nil
        config.drmConfiguration?.persistLicense = true
        config.drmSystem = .fairplay
        return config
    }
}

class ViewController: UIViewController {
    @IBOutlet var playButton: UIButton!
    var player: PlayerAPI?
    var downloader = PRESTOplaySDK.shared.downloader()
    var downloadedItem: Download? {
        didSet {
            playButton.isEnabled = (downloadedItem != nil)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        if let download = downloader.getDownloads().first {
            downloadedItem = download
        }
        try? AVAudioSession.sharedInstance().setCategory(.playback)
    }

    override func viewWillTransition(to size: CGSize,
                                     with coordinator: UIViewControllerTransitionCoordinator) {
        player?.update(size: size)
    }

    @IBAction
    func onDownload(_ button: UIButton) {
        player?.stop()
        downloadedItem = nil

        TestAssets.clearStreams.forEach { [weak self] config in
            DispatchQueue(label: "downloadClear").async { [weak self] in
                self?.downloadClearContents(config: config)
            }
        }
    }

    @IBAction
    func onDownloadDrm(_ button: UIButton) {
        player?.stop()
        downloadedItem = nil

        TestAssets.drmStreams.forEach { [weak self] config in
            DispatchQueue(label: "downloadDrm").async { [weak self] in
                self?.downloadDrmContents(config: config)
            }
        }
    }

    @IBAction
    func onDelete(_ button: UIButton) {
        downloader.getDownloads().forEach { download in
            if !download.isCompleted {
                print("Cancel download: \(download.uuid), \(download.playerConfiguration.contentUrl)")
                _ = downloader.cancelDownload(download.uuid)
            }
            print("Remove download: \(download.uuid), \(download.playerConfiguration.contentUrl)")
            _ = downloader.removeDownload(download.uuid)
        }
        downloadedItem = nil
    }

    @IBAction
    func onPlay(_ button: UIButton) {
        guard let downloadedItem else { return }
        play(downloadedItem)
    }
}

extension ViewController {
    func downloadClearContents(config: PlayerConfiguration) {
        let download = downloader.createDownload(config, headers: [:])

        downloader.prepareDownload(download.uuid) { [weak self] preparedDownload, error in
            guard let self else { return }
            guard error == nil else {
                print("Error: \(error?.message ?? "Unknown prepare error")")
                return
            }
            guard let preparedDownload else {
                print("Failed to prepare the download: \(download.uuid), \(download.playerConfiguration.contentUrl)")
                return
            }

            if let startError = self.downloader.startDownload(
                preparedDownload.uuid, delegate: self)
            {
                print("Error: \(startError.message ?? "Unknown download error"), \(preparedDownload.uuid), \(preparedDownload.playerConfiguration.contentUrl)")
            }
        }
    }

    func downloadDrmContents(config: PlayerConfiguration) {
        let prefetcher = PRESTOplaySDK.shared.prefetcher(for: config)
        let download = downloader.createDownload(config, headers: [:])

        prefetcher?.prefetchKeys { [weak self] error in
            guard let self else { return }
            guard error == nil else {
                print("Error: \(error?.message ?? "Unknown prefetch error")")
                return
            }

            downloader.prepareDownload(download.uuid) { [weak self] preparedDownload, error in
                guard let self else { return }
                guard error == nil else {
                    print("Error: \(error?.message ?? "Unknown prepare error")")
                    return
                }
                guard let preparedDownload else {
                    print("Failed to prepare the download: \(download.uuid), \(download.playerConfiguration.contentUrl)")
                    return
                }

                if let startError = self.downloader.startDownload(
                    preparedDownload.uuid, delegate: self)
                {
                    print("Error: \(startError.message ?? "Unknown download error"), \(preparedDownload.uuid), \(preparedDownload.playerConfiguration.contentUrl)")
                }
            }
        }
    }

    func play(_ download: Download) {
        guard let config = try? PlayerConfiguration(with: download) else {
            print("Failed to create a configuration from the download: \(download.uuid), \(download.playerConfiguration.contentUrl)")
            return
        }

        var player = PRESTOplaySDK.shared.player()
        player.load(config: config)
        player.onState = { previous, state in
            print("Player state: \(state), \(download.uuid), \(download.playerConfiguration.contentUrl)")

            if let error = player.error {
                print("Player error: \(error)")
                return
            }
        }

        player.attach(to: view.layer)
        player.open(autoplay: true)
        self.player = player
    }
}

// MARK: - DownloadDelegate

extension ViewController: DownloadDelegate {
    func didStateChange(_ download: Download) {
        print("Download state: \(download.uuid), \(download.playerConfiguration.contentUrl), \(download.state)")

        if download.state == .success,
           downloadedItem == nil {    // Play the first downloaded item.
            print("Download success: \(download.uuid), \(download.playerConfiguration.contentUrl)")
            downloadedItem = download
        }
    }

    func didProgressChange(_ download: Download) {
        print("Download progress: \(download.uuid), \(download.playerConfiguration.contentUrl), \(download.progress.percent)")
    }
}
