//
//  Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//

import UIKit.UIGestureRecognizerSubclass

public struct PanDirection: OptionSet {
    public let rawValue: Int
    public init(rawValue: Int) {
        self.rawValue = rawValue
    }

    public static let left  = PanDirection(rawValue: 1 << 0)
    public static let right = PanDirection(rawValue: 1 << 1)
    public static let up    = PanDirection(rawValue: 1 << 2)
    public static let down  = PanDirection(rawValue: 1 << 3)

    public static let horizontal: PanDirection = [.left, .right]
    public static let vertical: PanDirection = [.up, .down]
    public static let any: PanDirection = [.left, .right, .up, .down]
}

public class DirectionalPanGestureRecognizer: UIPanGestureRecognizer {

    let direction: PanDirection

    public init(direction: PanDirection, target: AnyObject, action: Selector) {
        self.direction = direction

        super.init(target: target, action: action)
    }

    override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        // Only start gesture if it's initially in the specified direction.
        if state == .possible {
            guard let touch = touches.first else {
                return
            }

            let previousLocation = touch.previousLocation(in: view)
            let location = touch.location(in: view)
            let deltaY = previousLocation.y - location.y
            let deltaX = previousLocation.x - location.x

            let isSatisified: Bool = {
                if abs(deltaY) > abs(deltaX) {
                    if direction.contains(.up) && deltaY < 0 {
                        return true
                    }

                    if direction.contains(.down) && deltaY > 0 {
                        return true
                    }
                } else {
                    if direction.contains(.left) && deltaX < 0 {
                        return true
                    }

                    if direction.contains(.right) && deltaX > 0 {
                        return true
                    }
                }

                return false
            }()

            guard isSatisified else {
                return
            }
        }

        // Gesture was already started, or in the correct direction.
        super.touchesMoved(touches, with: event)

        if state == .began {
            let vel = velocity(in: view)
            switch direction {
            case .left, .right:
                if fabs(vel.y) > fabs(vel.x) {
                    state = .cancelled
                }
            case .up, .down:
                if fabs(vel.x) > fabs(vel.y) {
                    state = .cancelled
                }
            default:
                break
            }
        }
    }
}