//  LoadingLargeImageViewController.swift
//  Tusker
//
//  Created by Shadowfacts on 6/14/19.
//  Copyright © 2019 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm
import TuskerComponents

class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableViewController {

    private var attachment: Attachment?
    let url: URL
    let cache: ImageCache
    let imageDescription: String?

    private(set) var loaded = false
    private(set) var largeImageVC: LargeImageViewController?
    private var loadingVC: LoadingViewController?
    
    private var imageRequest: ImageCache.Request?
    
    private var initialControlsVisible: Bool = true
    var controlsVisible: Bool {
        get {
            return largeImageVC?.controlsVisible ?? initialControlsVisible
        }
        set {
            if let largeImageVC = largeImageVC {
                largeImageVC.setControlsVisible(newValue, animated: false)
            } else {
                initialControlsVisible = newValue
            }
        }
    }
    
    var shrinkGestureEnabled = true
    
    weak var animationSourceView: UIImageView?
    var largeImageController: LargeImageViewController? { largeImageVC }
    var animationImage: UIImage? { largeImageVC?.animationImage ?? animationSourceView?.image }
    var dismissInteractionController: LargeImageInteractionController?
    
    var isInteractivelyAnimatingDismissal: Bool = false {
        didSet {
            setNeedsStatusBarAppearanceUpdate()
        }
    }
    
    override var prefersStatusBarHidden: Bool {
        return !isInteractivelyAnimatingDismissal
    }
    override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
        return .none
    }
    override var childForHomeIndicatorAutoHidden: UIViewController? {
        return largeImageVC
    }
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }
    
    init(url: URL, cache: ImageCache, imageDescription: String?) {
        self.url = url
        self.cache = cache
        self.imageDescription = imageDescription
        
        super.init(nibName: nil, bundle: nil)
        
        modalPresentationStyle = .fullScreen
    }
    
    convenience init(attachment: Attachment) {
        self.init(url: attachment.url, cache: .attachments, imageDescription: attachment.description)
        self.attachment = attachment
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        overrideUserInterfaceStyle = .dark
        view.backgroundColor = .black
        
        // always load full resolution from disk for large image, in case the cache is scaled
        if let entry = cache.get(url, loadOriginal: true) {
            // todo: if load original is true, is there any way entry.data could be nil?
            // feels like the data param of createLargeImage shouldn't be optional
            createLargeImage(data: entry.data, image: entry.image, url: url)
        } else {
            createPreview()
            
            loadingVC = LoadingViewController()
            embedChild(loadingVC!)
            imageRequest = cache.get(url, loadOriginal: true) { [weak self] (data, image) in
                guard let self = self, let image = image else { return }
                self.imageRequest = nil
                DispatchQueue.main.async {
                    self.loadingVC?.removeViewAndController()
                    self.createLargeImage(data: data, image: image, url: self.url)
                }
            }
        }
        
        if shrinkGestureEnabled {
            dismissInteractionController = LargeImageInteractionController(viewController: self)
        }
    }
    
    override func didMove(toParent parent: UIViewController?) {
        super.didMove(toParent: parent)
        
        if parent == nil {
            imageRequest?.cancel()
        }
    }
    
    private func createLargeImage(data: Data?, image: UIImage, url: URL) {
        guard !loaded else { return }
        loaded = true
        
        let content: LargeImageContentView
        
        // todo: p sure grayscaling gifs has never worked
        if url.pathExtension == "gif", let data = data {
            // todo: pulling the gif controller out of the source view feels icky
            // is it possible for the source view's gif controller to have different data than we just got?
            // should this be a property set by the animation controller instead?
            let gifController = (animationSourceView as? GIFImageView)?.gifController ?? GIFController(gifData: data)
            content = LargeImageGifContentView(url: url, gifController: gifController)
        } else {
            if let transformedImage = ImageGrayscalifier.convertIfNecessary(url: url, image: image) {
                content = LargeImageImageContentView(url: url, data: data, image: transformedImage)
            } else {
                content = LargeImageImageContentView(url: url, data: data, image: image)
            }
        }
        
        setContent(content)
    }
    
    private func setContent(_ content: LargeImageContentView) {
        if let existing = largeImageVC {
            existing.contentView = content
        } else {
            largeImageVC = LargeImageViewController(contentView: content, description: imageDescription, sourceView: animationSourceView)
            largeImageVC!.initialControlsVisible = initialControlsVisible
            largeImageVC!.shrinkGestureEnabled = false
            embedChild(largeImageVC!)
        }
    }
    
    private func createPreview() {
        guard !self.loaded,
              var image = animationSourceView?.image else { return }
        
        if Preferences.shared.grayscaleImages,
           let source = image.cgImage,
           let grayscale = ImageGrayscalifier.convert(url: nil, cgImage: source) {
            image = grayscale
        }
        setContent(LargeImageImageContentView(url: url, data: nil, image: image))
    }
    
}
