<commit_before>//
//  Coordinator.swift
//  Cruciverber
//
//  Created by <NAME> on 12.03.16.
//  Copyright © 2016 Flinesoft. All rights reserved.
//

import UIKit
import HandySwift

typealias ActionClosure = () -> ()

/// The different presentation options.
///
/// - Modal: Present modally with optional completion handler.
/// - Push: Push within navigation controller.
public enum PresentationStyle {
    case modal(completion: (() -> Void)?)
    case push
}

/// A protocol to conform to in view controllers.
public protocol Coordinatable {
    /// Action enum with all action cases to coordinate.
    associatedtype Action

    /// Set this in coordinators to react to actions.
    var coordinate: ((Action) -> Void)! { get set }
}

/// The base coordinator class helping to use the coordinator concept. A coordinators task is to control
/// the screen flow of views and controllers that belong together or are presented in a specific order
/// even if there is logic behind it. Simply put: Thanks to coordinators controllers responsibility now
/// is to only present the views – all decision logic and data handling should be part of the coordinator.
///
/// This class was inspired by the concept presented by Soroush Khanlou here:
/// http://khanlou.com/2015/10/coordinators-redux/
///
/// This class was **designed to be subclassed** (and will be abstract once Swift enables it).
/// All subclasses should override the following properties and methods:
///
///   - mainViewController
///   - start()
open class Coordinator {
    // MARK: - Stored Instance Properties

    /// The coordinators started from this coordinator.
    private var childCoordinators: [Coordinator] = []

    /// The coordinator who started this coordinator.
    private var parentCoordinator: Coordinator?

    /// The view controller to be presented from. Can be a UINavigationViewController.
    public let presentingViewController: UIViewController

    /// The current main view controller of the coordinator. Can be a UINavigationViewController.
    open var mainViewController: UIViewController? { return nil }

    private var finishClosure: (() -> Void)?
    private var disappearClosure: (() -> Void)?

    /// This can be used on didDisappear to assume a screen pan swipe or back button press.
    public var finishCalled = false


    // MARK: - Computed Instance Properties

    /// Returns the presenting navigation controller if available. Extracts the navigation controller
    /// from a UIViewController or directly returns a UINavigationController if such presenting.
    private var presentingNavigationController: UINavigationController? {
        if let presentingNavCtrl = presentingViewController as? UINavigationController {
            return presentingNavCtrl
        }
        return presentingViewController.navigationController
    }


    // MARK: - Initializers

    /// Initialize a new coordinator object.
    ///
    /// - Parameters:
    ///   - presentingViewController: The view controller (or the navigation controller) presenting the new coordinator.
    public init(presentingViewController: UIViewController) {
        self.presentingViewController = presentingViewController
    }


    // MARK: - Instance Methods

    /// Starts the screen flow represented by this coordinator. Subclasses should put their screen flow logic in here.
    open func start() {}

    /// Starts a sub coordinator from within this coordinator – no need to call `start()` on the coordinator manually.
    /// This ensures the sub coordinator is correctly added to the child coordinators array.
    ///
    /// - Parameters:
    ///   - subCoordinator: The sub coordinator to be started and added to the child coordinators.
    /// - Returns: The child coordinator object for consecutive callback definitions (like `onFinish`).
    public func start(subCoordinator childCoordinator: Coordinator) -> Coordinator {
        childCoordinators.append(childCoordinator)
        childCoordinator.parentCoordinator = self
        childCoordinator.start()
        return childCoordinator
    }

    /// Callback to be called when the coordinator finishes.
    ///
    /// - Parameters:
    ///    - closure: The callback to be called on finish.
    public func onFinish(_ closure: @escaping () -> Void) {
        finishClosure = closure
    }

    /// Callback to be called when the coordinators main view controller has disappeared.
    ///
    /// - Parameters:
    ///   - closure: The callback to be called on disappear.
    public func onDisappear(_ closure: @escaping () -> Void) {
        disappearClosure = closure
    }

    /// Finishes a sub coordinator and removes it from its parent coordinators child coordinators (if any).
    ///
    /// - Parameters:
    ///   - viewCtrl: The view controller to be finished.
    ///   - makeDisappear: Dismisses or pops the view controller if set to `true`.
    public func finish(alreadyDisappeared: Bool = false) {
        finishCalled = true

        if let parentCoordinator = parentCoordinator {
            let foundChildIndex = parentCoordinator.childCoordinators.index { $0 === self }
            if let childIndex = foundChildIndex {
                parentCoordinator.childCoordinators.remove(at: childIndex)
            }
        }

        if let finishClosure = finishClosure {
            finishClosure()
        }

        guard !alreadyDisappeared else { return }

        if let viewCtrl = mainViewController {
            if let navigationCtrl = viewCtrl.navigationController {
                if navigationCtrl.viewControllers.first == viewCtrl {
                    if let presentingViewCtrl = navigationCtrl.presentingViewController {
                        presentingViewCtrl.dismiss(animated: true, completion: disappearClosure)
                    } else {
                        // TODO: this case needed?
                        navigationCtrl.dismiss(animated: true, completion: disappearClosure)
                    }
                } else {
                    navigationCtrl.popViewController(animated: true)
                    if let disappearClosure = disappearClosure {
                        delay(bySeconds: 0.3, closure: disappearClosure)
                    }
                }
            } else {
                presentingViewController.dismiss(animated: true, completion: disappearClosure)
            }
        }
    }

    /// Presents a view controller given the specified presentation style.
    ///
    /// If no presentation style is given then it will be automatically detected by the following rules:<commit_msg>Prevent reference cycles on child coordinators<commit_after>//
//  Coordinator.swift
//  Cruciverber
//
//  Created by <NAME> on 12.03.16.
//  Copyright © 2016 Flinesoft. All rights reserved.
//

import UIKit
import HandySwift

typealias ActionClosure = () -> ()

/// The different presentation options.
///
/// - Modal: Present modally with optional completion handler.
/// - Push: Push within navigation controller.
public enum PresentationStyle {
    case modal(completion: (() -> Void)?)
    case push
}

/// A protocol to conform to in view controllers.
public protocol Coordinatable {
    /// Action enum with all action cases to coordinate.
    associatedtype Action

    /// Set this in coordinators to react to actions.
    var coordinate: ((Action) -> Void)! { get set }
}

/// The base coordinator class helping to use the coordinator concept. A coordinators task is to control
/// the screen flow of views and controllers that belong together or are presented in a specific order
/// even if there is logic behind it. Simply put: Thanks to coordinators controllers responsibility now
/// is to only present the views – all decision logic and data handling should be part of the coordinator.
///
/// This class was inspired by the concept presented by <NAME> here:
/// http://khanlou.com/2015/10/coordinators-redux/
///
/// This class was **designed to be subclassed** (and will be abstract once Swift enables it).
/// All subclasses should override the following properties and methods:
///
///   - mainViewController
///   - start()
open class Coordinator {
    // MARK: - Stored Instance Properties

    /// The coordinators started from this coordinator.
    private var childCoordinators: [Coordinator] = []

    /// The coordinator who started this coordinator.
    private weak var parentCoordinator: Coordinator?

    /// The view controller to be presented from. Can be a UINavigationViewController.
    public weak let presentingViewController: UIViewController

    /// The current main view controller of the coordinator. Can be a UINavigationViewController.
    open var mainViewController: UIViewController? { return nil }

    private var finishClosure: (() -> Void)?
    private var disappearClosure: (() -> Void)?

    /// This can be used on didDisappear to assume a screen pan swipe or back button press.
    public var finishCalled = false


    // MARK: - Computed Instance Properties

    /// Returns the presenting navigation controller if available. Extracts the navigation controller
    /// from a UIViewController or directly returns a UINavigationController if such presenting.
    private var presentingNavigationController: UINavigationController? {
        if let presentingNavCtrl = presentingViewController as? UINavigationController {
            return presentingNavCtrl
        }
        return presentingViewController.navigationController
    }


    // MARK: - Initializers

    /// Initialize a new coordinator object.
    ///
    /// - Parameters:
    ///   - presentingViewController: The view controller (or the navigation controller) presenting the new coordinator.
    public init(presentingViewController: UIViewController) {
        self.presentingViewController = presentingViewController
    }


    // MARK: - Instance Methods

    /// Starts the screen flow represented by this coordinator. Subclasses should put their screen flow logic in here.
    open func start() {}

    /// Starts a sub coordinator from within this coordinator – no need to call `start()` on the coordinator manually.
    /// This ensures the sub coordinator is correctly added to the child coordinators array.
    ///
    /// - Parameters:
    ///   - subCoordinator: The sub coordinator to be started and added to the child coordinators.
    /// - Returns: The child coordinator object for consecutive callback definitions (like `onFinish`).
    public func start(subCoordinator childCoordinator: Coordinator) -> Coordinator {
        childCoordinators.append(childCoordinator)
        childCoordinator.parentCoordinator = self
        childCoordinator.start()
        return childCoordinator
    }

    /// Callback to be called when the coordinator finishes.
    ///
    /// - Parameters:
    ///    - closure: The callback to be called on finish.
    public func onFinish(_ closure: @escaping () -> Void) {
        finishClosure = closure
    }

    /// Callback to be called when the coordinators main view controller has disappeared.
    ///
    /// - Parameters:
    ///   - closure: The callback to be called on disappear.
    public func onDisappear(_ closure: @escaping () -> Void) {
        disappearClosure = closure
    }

    /// Finishes a sub coordinator and removes it from its parent coordinators child coordinators (if any).
    ///
    /// - Parameters:
    ///   - alreadyDisappeared: Dismisses or pops the view controller if set to `false`.
    public func finish(alreadyDisappeared: Bool = false) {
        finishCalled = true

        let finishClosure = self.finishClosure
        self.finishClosure = nil

        let disappearClosure = self.disappearClosure
        self.disappearClosure = nil

        parentCoordinator?.childCoordinators = parentCoordinator!.childCoordinators.filter { $0 !== self }

        if let finishClosure = finishClosure {
            finishClosure()
            self.finishClosure = nil
        }

        guard !alreadyDisappeared else { return }

        if let viewCtrl = mainViewController {
            if let navigationCtrl = viewCtrl.navigationController {
                if navigationCtrl.viewControllers.first == viewCtrl {
                    if let presentingViewCtrl = navigationCtrl.presentingViewController {
                        presentingViewCtrl.dismiss(animated: true, completion: disappearClosure)
                    } else {
                        // TODO: this case needed?
                        navigationCtrl.dismiss(animated: true, completion: disappearClosure)
                    }
                } else {
                    navigationCtrl.popViewController(animated: true)
                    if let disappearClosure = disappearClosure {
                        delay(bySeconds: 0.3, closure: disappearClosure)
                    }
                }
            } else {
                presentingViewController.dismiss(animated: true, completion: disappearClosure)
            }
        }

        self.disappearClosure = nil
    }

    /// Presents a view controller given the specified presentation style.
    ///
    /// If no presentation style is given then it will be automatically detected by the following rules:
{'fraction_non_alphanumeric': 0.07811793093047806, 'fraction_numerical': 0.0028653295128939827, 'mean_word_length': 2.4774514944939696, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 22, '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': '5191355', 'n_tokens_mistral': 3315, 'n_tokens_neox': 3039, 'n_words': 1237}