<commit_before>//
//  UIViewController+States.swift
//  Created by <NAME> on 15/09/2018.
//

import UIKit


// MARK: - Error and Loading
@nonobjc
extension UIViewController {
    
    // MARK: - Loaders
    public func showLoadingView(_ loadingView: UIView, animated: Bool = true, stateViewFrame: CGRect? = nil) {
        self.addStateView(loadingView, animated: animated, stateViewFrame: stateViewFrame)
    }
    
    public func showLoader(stateViewFrame: CGRect? = nil, animated: Bool = true) {
        self.showLoadingView(LoadingView(), animated: animated, stateViewFrame: stateViewFrame)
    }
    
    public func hideLoader(stateViewFrame: CGRect? = nil, animated: Bool = true) {
        self.removeStateView(animated: animated)
    }
    
    public func showErrorView(_ errorView: UIView, animated: Bool = true, stateViewFrame: CGRect? = nil) {
        self.addStateView(errorView, animated: animated, stateViewFrame: stateViewFrame)
    }
    
    public func showErrorMessage(_ message: String, error: Error, retryButton: ButtonConfiguration? = nil, animated: Bool = true, stateViewFrame: CGRect? = nil) {
        
        #if DEBUG
        let errorMessage = "\(message) \nError code: \(error.localizedDescription)"
        #else
        let errorMessage = message
        #endif
        
        let styler = TextStyler.styler
        let errorView = ErrorView(
            title: styler.attributedString("Error").bolded,
            message: styler.attributedString(errorMessage),
            buttonConfiguration: retryButton
        )
        showErrorView(errorView, animated: animated)
    }
    
    public func hideError(animated: Bool = true) {
        self.removeStateView(animated: animated)
    }
    
    private func addStateView(_ stateView: UIView, animated: Bool = true, stateViewFrame: CGRect?) {
        removeStateView(animated: animated)
        let stateVC = StateContainerViewController(
            stateView: stateView,
            backgroundColor: self.view.backgroundColor ?? .clear
        )
        addChild(stateVC)
        view.addSubview(stateVC.view)
        if let _stateViewFrame = stateViewFrame {
            stateVC.view.frame = _stateViewFrame
        } else {
            stateVC.view.pinToSuperview()
        }
        stateVC.didMove(toParent: self)
        guard animated, let animator = StateContainerAppereance.transitionConfiguration?.animator else { return }
        stateVC.view.alpha = 0
        animator.addAnimations {
            stateVC.view.alpha = 1
        }
        animator.startAnimation()
    }
    
    private func removeStateView(animated: Bool = true) {
        guard let stateContainer = self.stateContainer else { return }
        guard animated, let animator = StateContainerAppereance.transitionConfiguration?.animator else {
            removeContainedViewController(stateContainer)
            return
        }
        stateContainer.willMove(toParent: nil)
        stateContainer.removeFromParent()
        stateContainer.view.alpha = 1
        animator.addAnimations {
            stateContainer.view.alpha = 0
        }
        animator.addCompletion { (_) in
            stateContainer.view.removeFromSuperview()
        }
        animator.startAnimation()
    }
    
    
    private var stateContainer: StateContainerViewController? {
        return self.children.compactMap({ return $0 as? StateContainerViewController }).first
    }
}

public enum StateContainerAppereance {
    public static var padding: CGFloat = 20
    public static var transitionConfiguration: TransitionConfiguration?

    public struct TransitionConfiguration {
        public let duration: TimeInterval
        public let curve: UIView.AnimationCurve

        public init(duration: TimeInterval, curve: UIView.AnimationCurve) {
            self.duration = duration
            self.curve = curve
        }

        public static func simple() -> TransitionConfiguration {
            return .init(duration: 0.3, curve: .easeInOut)
        }
        
        var animator: UIViewPropertyAnimator {
            return .init(duration: duration, curve: curve)
        }
    }
}

@objc(BSWStateContainerViewController)
private class StateContainerViewController: UIViewController {<commit_msg>Make sure state animator is public<commit_after>//
//  UIViewController+States.swift
//  Created by <NAME> on 15/09/2018.
//

import UIKit


// MARK: - Error and Loading
@nonobjc
extension UIViewController {
    
    // MARK: - Loaders
    public func showLoadingView(_ loadingView: UIView, animated: Bool = true, stateViewFrame: CGRect? = nil) {
        self.addStateView(loadingView, animated: animated, stateViewFrame: stateViewFrame)
    }
    
    public func showLoader(stateViewFrame: CGRect? = nil, animated: Bool = true) {
        self.showLoadingView(LoadingView(), animated: animated, stateViewFrame: stateViewFrame)
    }
    
    public func hideLoader(stateViewFrame: CGRect? = nil, animated: Bool = true) {
        self.removeStateView(animated: animated)
    }
    
    public func showErrorView(_ errorView: UIView, animated: Bool = true, stateViewFrame: CGRect? = nil) {
        self.addStateView(errorView, animated: animated, stateViewFrame: stateViewFrame)
    }
    
    public func showErrorMessage(_ message: String, error: Error, retryButton: ButtonConfiguration? = nil, animated: Bool = true, stateViewFrame: CGRect? = nil) {
        
        #if DEBUG
        let errorMessage = "\(message) \nError code: \(error.localizedDescription)"
        #else
        let errorMessage = message
        #endif
        
        let styler = TextStyler.styler
        let errorView = ErrorView(
            title: styler.attributedString("Error").bolded,
            message: styler.attributedString(errorMessage),
            buttonConfiguration: retryButton
        )
        showErrorView(errorView, animated: animated)
    }
    
    public func hideError(animated: Bool = true) {
        self.removeStateView(animated: animated)
    }
    
    private func addStateView(_ stateView: UIView, animated: Bool = true, stateViewFrame: CGRect?) {
        removeStateView(animated: animated)
        let stateVC = StateContainerViewController(
            stateView: stateView,
            backgroundColor: self.view.backgroundColor ?? .clear
        )
        addChild(stateVC)
        view.addSubview(stateVC.view)
        if let _stateViewFrame = stateViewFrame {
            stateVC.view.frame = _stateViewFrame
        } else {
            stateVC.view.pinToSuperview()
        }
        stateVC.didMove(toParent: self)
        guard animated, let animator = StateContainerAppereance.transitionConfiguration?.animator else { return }
        stateVC.view.alpha = 0
        animator.addAnimations {
            stateVC.view.alpha = 1
        }
        animator.startAnimation()
    }
    
    private func removeStateView(animated: Bool = true) {
        guard let stateContainer = self.stateContainer else { return }
        guard animated, let animator = StateContainerAppereance.transitionConfiguration?.animator else {
            removeContainedViewController(stateContainer)
            return
        }
        stateContainer.willMove(toParent: nil)
        stateContainer.removeFromParent()
        stateContainer.view.alpha = 1
        animator.addAnimations {
            stateContainer.view.alpha = 0
        }
        animator.addCompletion { (_) in
            stateContainer.view.removeFromSuperview()
        }
        animator.startAnimation()
    }
    
    
    private var stateContainer: StateContainerViewController? {
        return self.children.compactMap({ return $0 as? StateContainerViewController }).first
    }
}

public enum StateContainerAppereance {
    public static var padding: CGFloat = 20
    public static var transitionConfiguration: TransitionConfiguration?

    public struct TransitionConfiguration {
        public let duration: TimeInterval
        public let curve: UIView.AnimationCurve

        public init(duration: TimeInterval, curve: UIView.AnimationCurve) {
            self.duration = duration
            self.curve = curve
        }

        public static func simple() -> TransitionConfiguration {
            return .init(duration: 0.3, curve: .easeInOut)
        }
        
        public var animator: UIViewPropertyAnimator {
            return .init(duration: duration, curve: curve)
        }
    }
}

@objc(BSWStateContainerViewController)
private class StateContainerViewController: UIViewController {
{'fraction_non_alphanumeric': 0.08059981255857544, 'fraction_numerical': 0.003983130271790066, 'mean_word_length': 2.5854682906341875, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '970287', 'n_tokens_mistral': 2296, 'n_tokens_neox': 2195, 'n_words': 542}