//
//  SubNavInteractor.swift
//  MobileFirstFramework
//
//  Created by Matt Bruce on 10/22/21.
//  Copyright © 2021 Verizon Wireless. All rights reserved.
//

import Foundation

@objc public protocol SubNavSwipeNavigationProtocol: AnyObject {
    func swipeLeft()
    func swipeRight()
    func update(percentage: CGFloat)
}

fileprivate enum SubNavPanningDirection : Int {
    case left
    case right
    case none
}

@objcMembers public class SubNavInteractor: UIPercentDrivenInteractiveTransition {

    //// true while panning
    public var panning: Bool = false

    ////Pan gesture instance
    public var panGesture: UIPanGestureRecognizer = .init()

    ////set pannable percentage 0 to 1
    public var pannablePercentage: CGFloat = 0.0

    //// can be used to keep track of if we are ..
    public var interactive: Bool = false
    
    //private
    private var shouldCompleteTransition: Bool = false
    private weak var delegate: SubNavSwipeNavigationProtocol?
    private var panningDirection: SubNavPanningDirection = .none
    
    ////constructor
    public convenience init(viewController: (UIViewController & SubNavSwipeNavigationProtocol)) {
        self.init(viewController: viewController, delegate: viewController)
    }
    
    public init(viewController: UIViewController, delegate: SubNavSwipeNavigationProtocol) {
        pannablePercentage = 0.15
        self.delegate = delegate
        super.init()
        panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(handlePanGesture));
        viewController.view.addGestureRecognizer(panGesture)
    }
    
    //overrides
    public override func cancel() {
        if interactive {
            delegate?.update(percentage: 0)
            super.cancel()
        }
        interactive = false
        panning = false
        shouldCompleteTransition = false
    }
    
    public override func finish() {
        if interactive {
            delegate?.update(percentage: 1)
            super.finish()
        }
        interactive = false
        panning = false
        shouldCompleteTransition = false
    }
    
    //private
    @objc private func handlePanGesture(_ pan: UIPanGestureRecognizer) {
        guard let panView = pan.view else { return }
        
        let velocityX = pan.velocity(in: panView).x
        let translation = pan.translation(in: panView)
        var percentage = translation.x / panView.bounds.width
        let locationInView = pan.location(in: panView)
        
        // Simulates an edge gesture by only accepting pans at the edge of the screen. Needed because edge gesture doesn't work nicely with extended menus such as on ipad.
        let frame = panView.frame
        let pannableFrameLeft = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width * pannablePercentage, height: frame.size.height)
        let pannableFrameRight = CGRect(x: frame.origin.x + frame.size.width * (1 - pannablePercentage), y: frame.origin.y, width: frame.size.width * pannablePercentage, height: frame.size.height)
    
        switch (pan.state) {
            
        case .began:
            // Begin the transition to the next page.
            panning = true
            if velocityX < 0 && pannableFrameRight.contains(locationInView) {
                delegate?.swipeLeft()
                panningDirection = .left
            } else if velocityX > 0 && pannableFrameLeft.contains(locationInView){
                delegate?.swipeRight()
                panningDirection = .right
            }
            
        case .changed:
            guard interactive else { return }
            
            if panningDirection == .right && translation.x < 0 ||
                panningDirection == .left && translation.x > 0 {
                percentage = 0
            }
            
            if percentage < 0 {
                percentage = -percentage;
            }
            if percentage > 1.00 {
                percentage = 1.00;
            }
            shouldCompleteTransition = percentage > 0.65;
            update(percentage)
            delegate?.update(percentage: percentage)
            
        case .cancelled:
            cancel()
            
        case .ended:
            if ((percentage < 0) != (velocityX < 0)) && abs(velocityX) > 50 {
                // If we are moving back toward the previous view we want to cancel. (the speed threshold is for shaky hands)
                cancel()
            } else if abs(velocityX) > 300.0 || shouldCompleteTransition {
                // Need to cancel the transition if we pass the threshold, but from a opposite direction(e.g. Start panning from left edge to right, then move back, pass the start x location, end with a high velocityX)
                if ((panningDirection == .right) && translation.x < 0) || ((panningDirection == .left) && translation.x > 0) {
                    cancel()
                } else {
                    // If we pass the speed or screen percentage thresholds, move on to the next screen.
                    finish()
                }
            } else {
                cancel()
            }

        default:
            return
        }
    }
}
