class AnimationsTestViewController: UIViewController {
    
    enum AnimationPosiXDirection {
        case left, center, right
    }
    
    enum AnimationFlipDirection {
        case yAxis, center, xAxis
    }
    
    private let containerView: UIView = {
        let view = UIView(frame: .zero)
        view.backgroundColor = .white
        view.layer.cornerRadius = 16
        view.layer.borderWidth = 1.0
        view.layer.borderColor = UIColor.lightGray.cgColor
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        configureContentView()
    }
    
    private func configureContentView() {
        view.addSubview(containerView)
        NSLayoutConstraint.activate([
            containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            containerView.widthAnchor.constraint(equalToConstant: 200),
            containerView.heightAnchor.constraint(equalToConstant: 170)
        ])
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        animateRotate3DFlip(from: .right, direction: .xAxis, rotationCount: 3)
    }
    
    private func animateRotate3DFlip(from: AnimationPosiXDirection, direction: AnimationFlipDirection, centerLeftToRight: Bool = false, rotationCount: CGFloat = 1) {
        prepareScale3DAnimation(from: from)
        start3DFlipAnimationWith(direction: direction, centerLeftToRight: centerLeftToRight, rotationCount: rotationCount)
    }

    private func prepareScale3DAnimation(from: AnimationPosiXDirection) {
        var perspective = CATransform3DIdentity
        switch from {
        case .left:
            perspective.m34 = 1 / -200
        case .center:
            perspective.m34 = 0 / -200
        case .right:
            perspective.m34 = -1 / -200
        }
        containerView.layer.transform = perspective
    }
    
    private func start3DFlipAnimationWith(direction: AnimationFlipDirection, centerLeftToRight: Bool = false, rotationCount: CGFloat = 1) {
        var rotateAnimation: CABasicAnimation!
        switch direction {
        case .xAxis:
            rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.y")
        case .center:
            rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        case .yAxis:
            rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.x")
        }
        rotateAnimation.fromValue = 0
        let directionAndTimes = CGFloat.pi * rotationCount
        rotateAnimation.byValue = centerLeftToRight ? directionAndTimes : -directionAndTimes
        rotateAnimation.duration = 1
        rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        // add animation
        containerView.layer.add(rotateAnimation, forKey: "3Dflip")
    }
}