//
//  LargeImageExpandAnimationController.swift
//  Tusker
//
//  Created by Shadowfacts on 9/1/18.
//  Copyright © 2018 Shadowfacts. All rights reserved.
//

import UIKit
import TuskerComponents

protocol LargeImageAnimatableViewController: UIViewController {
    var animationSourceView: UIImageView? { get }
    var largeImageController: LargeImageViewController? { get }
    var animationImage: UIImage? { get }
    var dismissInteractionController: LargeImageInteractionController? { get }
    var isInteractivelyAnimatingDismissal: Bool { get set }
}

extension LargeImageAnimatableViewController {
    func sourceViewFrame(in coordinateSpace: UIView) -> CGRect? {
        guard let sourceView = animationSourceView else { return nil }
        
        var sourceFrame = sourceView.convert(sourceView.bounds, to: coordinateSpace)
        if let scrollView = coordinateSpace as? UIScrollView {
            let scale = scrollView.zoomScale
            let width = sourceFrame.width * scale
            let height = sourceFrame.height * scale
            let x = sourceFrame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
            let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
            sourceFrame = CGRect(x: x, y: y, width: width, height: height)
        }
        
        return sourceFrame
    }
}

class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        if UIAccessibility.prefersCrossFadeTransitions {
            return 0.2
        } else {
            return 0.4
        }
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromVC = transitionContext.viewController(forKey: .from),
            let toVC = transitionContext.viewController(forKey: .to) as? LargeImageAnimatableViewController else {
                return
        }
        
        if UIAccessibility.prefersCrossFadeTransitions {
            animateCrossFadeTransition(using: transitionContext)
            return
        }
        
        let containerView = transitionContext.containerView
        containerView.addSubview(toVC.view)

        let finalVCFrame = transitionContext.finalFrame(for: toVC)
        guard let sourceView = toVC.animationSourceView,
              let sourceFrame = toVC.sourceViewFrame(in: fromVC.view),
              let image = toVC.animationImage else {
                toVC.view.frame = finalVCFrame
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
                return
        }
        
        // use alpha, because isHidden makes stack views re-layout
        sourceView.alpha = 0
        toVC.view.alpha = 0
        toVC.largeImageController?.contentView.isHidden = true
        toVC.largeImageController?.setControlsVisible(false, animated: false)

        var finalFrameSize = finalVCFrame.inset(by: toVC.view.safeAreaInsets).size
        let newWidth = finalFrameSize.width / image.size.width
        let newHeight = finalFrameSize.height / image.size.height
        if newHeight < newWidth {
            finalFrameSize.width = newHeight * image.size.width
        } else {
            finalFrameSize.height = newWidth * image.size.height
        }
        let finalFrame = CGRect(origin: CGPoint(x: finalVCFrame.midX - finalFrameSize.width / 2, y: finalVCFrame.midY - finalFrameSize.height / 2), size: finalFrameSize)

        let imageView = GIFImageView(frame: sourceFrame)
        imageView.image = image
        if let gifController = (sourceView as? GIFImageView)?.gifController {
            gifController.attach(to: imageView)
        }
        imageView.contentMode = .scaleAspectFill
        imageView.layer.cornerRadius = sourceView.layer.cornerRadius
        imageView.layer.maskedCorners = sourceView.layer.maskedCorners
        imageView.layer.masksToBounds = true
        
        containerView.addSubview(imageView)
        
        let duration = transitionDuration(using: transitionContext)
        let velocity = 1 / CGFloat(duration)
        UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.75, initialSpringVelocity: velocity, options: []) {
            imageView.frame = finalFrame
            imageView.layer.cornerRadius = 0
            toVC.view.alpha = 1
            toVC.largeImageController?.setControlsVisible(true, animated: false)
        } completion: { (_) in
            // This shouldn't be necessary. I believe it's a workaround for using a XIB
            // for the large image VC. Without this, the final frame of the large image VC
            // is not set to the propper rect (it uses the frame of the preview device
            // in the XIB). When using a storyboard, the final frame is automatically set
            // (or UIKit does layout differently when loading the view) and this is not necessary.
            toVC.view.frame = finalVCFrame
            
            toVC.largeImageController?.contentView.isHidden = false
            fromVC.view.isHidden = false
            imageView.removeFromSuperview()
            
            sourceView.alpha = 1

            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
    
    func animateCrossFadeTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toVC = transitionContext.viewController(forKey: .to) as? LargeImageAnimatableViewController else {
            return
        }
        
        transitionContext.containerView.addSubview(toVC.view)
        toVC.view.alpha = 0
        
        let duration = transitionDuration(using: transitionContext)
        UIView.animate(withDuration: duration) {
            toVC.view.alpha = 1
        } completion: { (_) in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
    
}
