//
//  TextViewCaretScrolling.swift
//  Tusker
//
//  Created by Shadowfacts on 11/11/20.
//  Copyright © 2020 Shadowfacts. All rights reserved.
//

import UIKit

protocol TextViewCaretScrolling: AnyObject {
    var caretScrollPositionAnimator: UIViewPropertyAnimator? { get set }
}

extension TextViewCaretScrolling {
    func ensureCursorVisible(textView: UITextView) {
        guard textView.isFirstResponder,
              let range = textView.selectedTextRange,
              let scrollView = findParentScrollView(of: textView) else {
            return
        }
        
        // We use a UIViewProperty animator to change the scroll view position so that we can store the currently
        // running one on the Coordinator. This allows us to cancel the running one, preventing multiple animations
        // from attempting to change the scroll view offset simultaneously, causing it to jitter around. This can
        // happen if the user is pressing return and quickly creating many new lines.
        
        if let existing = caretScrollPositionAnimator {
            existing.stopAnimation(true)
        }
        
        let cursorRect = textView.caretRect(for: range.start)
        var rectToMakeVisible = textView.convert(cursorRect, to: scrollView)
        
        // expand the rect to be three times the cursor height centered on the cursor so that there's
        // some space between the bottom of the line of text being edited and the top of the keyboard
        rectToMakeVisible.origin.y -= cursorRect.height
        rectToMakeVisible.size.height *= 3
        
        let animator = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut) {
            scrollView.scrollRectToVisible(rectToMakeVisible, animated: false)
        }
        self.caretScrollPositionAnimator = animator
        animator.startAnimation()
    }
    
    private func findParentScrollView(of view: UIView) -> UIScrollView? {
        var current: UIView = view
        while let superview = current.superview {
            if let scrollView = superview as? UIScrollView,
               scrollView.isScrollEnabled {
                return scrollView
            } else {
                current = superview
            }
        }
        
        return nil
    }
}
