//
//  LargeImageContentView.swift
//  Tusker
//
//  Created by Shadowfacts on 6/17/20.
//  Copyright © 2020 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm
@preconcurrency import AVFoundation
@preconcurrency import VisionKit
import TuskerComponents

protocol LargeImageContentView: UIView {
    var animationImage: UIImage? { get }
    var activityItemsForSharing: [Any] { get }
    var owner: LargeImageViewController? { get set }
    func setControlsVisible(_ controlsVisible: Bool, animated: Bool)
    func grayscaleStateChanged()
}

class LargeImageImageContentView: UIImageView, LargeImageContentView {
    
    #if !targetEnvironment(macCatalyst)
    @available(iOS 16.0, *)
    private static let analyzer = ImageAnalyzer()
    private var _analysisInteraction: AnyObject?
    @available(iOS 16.0, *)
    private var analysisInteraction: ImageAnalysisInteraction? { _analysisInteraction as? ImageAnalysisInteraction }
    #endif
    
    var animationImage: UIImage? { image! }
    var activityItemsForSharing: [Any] {
        guard let data else {
            return []
        }
        return [ImageActivityItemSource(data: data, url: url, image: image)]
    }
    weak var owner: LargeImageViewController?
    
    private let url: URL
    private let data: Data?
    
    init(url: URL, data: Data?, image: UIImage) {
        self.url = url
        self.data = data
        
        super.init(image: image)
        
        contentMode = .scaleAspectFit
        isUserInteractionEnabled = true
        
        #if !targetEnvironment(macCatalyst)
        if #available(iOS 16.0, *),
           ImageAnalyzer.isSupported {
            let interaction = ImageAnalysisInteraction()
            self._analysisInteraction = interaction
            interaction.delegate = self
            interaction.preferredInteractionTypes = .automatic
            addInteraction(interaction)
            Task {
                do {
                    let result = try await LargeImageImageContentView.analyzer.analyze(image, configuration: ImageAnalyzer.Configuration([.text, .machineReadableCode]))
                    interaction.analysis = result
                } catch {
                    // if analysis fails, we just don't show anything
                }
            }
        }
        #endif
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setControlsVisible(_ controlsVisible: Bool, animated: Bool) {
        #if !targetEnvironment(macCatalyst)
        if #available(iOS 16.0, *),
           let analysisInteraction {
            analysisInteraction.setSupplementaryInterfaceHidden(!controlsVisible, animated: animated)
        }
        #endif
    }
    
    func grayscaleStateChanged() {
        guard let data else {
            return
        }
        
        let image: UIImage?
        if Preferences.shared.grayscaleImages {
            image = ImageGrayscalifier.convert(url: nil, data: data)
        } else {
            image = UIImage(data: data)
        }
        
        if let image = image {
            self.image = image
        }
    }
}

#if !targetEnvironment(macCatalyst)
@available(iOS 16.0, *)
extension LargeImageImageContentView: ImageAnalysisInteractionDelegate {
    func presentingViewController(for interaction: ImageAnalysisInteraction) -> UIViewController? {
        return owner
    }
}
#endif

class LargeImageGifContentView: GIFImageView, LargeImageContentView {
    var animationImage: UIImage? { image }
    var activityItemsForSharing: [Any] {
        [ImageActivityItemSource(data: gifController!.gifData, url: url, image: image)]
    }
    weak var owner: LargeImageViewController?
    
    private let url: URL
    
    init(url: URL, gifController: GIFController) {
        self.url = url
        
        super.init(image: gifController.lastFrame?.image)
        
        contentMode = .scaleAspectFit
        
        gifController.attach(to: self)
        // todo: doing this in the init feels wrong
        gifController.startAnimating()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setControlsVisible(_ controlsVisible: Bool, animated: Bool) {
    }
    
    func grayscaleStateChanged() {
        // todo
    }
}

class LargeImageGifvContentView: GifvAttachmentView, LargeImageContentView {
    private(set) var animationImage: UIImage?
    var activityItemsForSharing: [Any] {
        [GifvActivityItemSource(asset: asset, attachment: attachment)]
    }
    weak var owner: LargeImageViewController?
    
    private let attachment: Attachment
    private let asset: AVURLAsset
    
    private var videoSize: CGSize?
    override var intrinsicContentSize: CGSize {
        videoSize ?? CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric)
    }
    
    init(attachment: Attachment, source: UIImageView) {
        precondition(attachment.kind == .gifv)
        
        self.attachment = attachment
        self.asset = AVURLAsset(url: attachment.url)
        
        super.init(asset: asset, gravity: .resizeAspect)
        
        self.animationImage = source.image
        
        self.player.play()
        
        Task {
            do {
                if let track = try await asset.loadTracks(withMediaType: .video).first {
                    let (size, transform) = try await track.load(.naturalSize, .preferredTransform)
                    self.videoSize = size.applying(transform)
                    self.invalidateIntrinsicContentSize()
                }
            } catch {
            }
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setControlsVisible(_ controlsVisible: Bool, animated: Bool) {
    }
    
    func grayscaleStateChanged() {
        // no-op, GifvAttachmentView observes the grayscale state itself
    }
    
}

fileprivate class ImageActivityItemSource: NSObject, UIActivityItemSource {
    let data: Data
    let url: URL
    let image: UIImage?

    init(data: Data, url: URL, image: UIImage?) {
        self.data = data
        self.url = url
        self.image = image
    }

    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return url
    }

    func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? {
        return image
    }

    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
        do {
            let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
            try data.write(to: tempURL)
            return tempURL
        } catch {
            return nil
        }
    }

    func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String {
        return (UTType(filenameExtension: url.pathExtension) ?? .image).identifier
    }
}

fileprivate class GifvActivityItemSource: NSObject, UIActivityItemSource {
    let asset: AVAsset
    let attachment: Attachment
    
    init(asset: AVAsset, attachment: Attachment) {
        self.asset = asset
        self.attachment = attachment
    }
    
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return attachment.url
    }
    
    func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? {
        let generator = AVAssetImageGenerator(asset: self.asset)
        generator.appliesPreferredTrackTransform = true
        if let image = try? generator.copyCGImage(at: .zero, actualTime: nil) {
            return UIImage(cgImage: image)
        } else {
            return nil
        }
    }
    
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
        do {
            let data = try Data(contentsOf: attachment.url)
            let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(attachment.url.lastPathComponent)
            try data.write(to: tempURL)
            return tempURL
        } catch {
            return nil
        }
    }
    
    func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String {
        return (UTType(filenameExtension: attachment.url.pathExtension) ?? .video).identifier
    }
}
