//
//  ParentModelProtocol.swift
//  MVMCoreUI
//
//  Created by Kyle on 3/19/21.
//  Copyright © 2021 Verizon Wireless. All rights reserved.
//

import Foundation

public protocol ParentMoleculeModelProtocol: MoleculeModelProtocol {
    
    var children: [MoleculeModelProtocol] { get }
    
}

public extension ParentMoleculeModelProtocol {
    
    func reduceDepthFirstTraverse<Result>(options: TreeTraversalOptions, depth: Int, initialResult: Result, nextPartialResult: (Result, MoleculeModelProtocol, Int) -> Result) -> Result {
        var result = initialResult
        if (options == .parentFirst) {
            result = nextPartialResult(result, self, depth)
        }
        result = children.reduce(result) { (result, molecule) -> Result in
            if let additionalParent = molecule as? ParentMoleculeModelProtocol {
                // Safety net to make sure the ParentMoleculeModelProtocol's method extension is called over the base MoleculeModelProtocol.
                return additionalParent.reduceDepthFirstTraverse(options: options, depth: depth + 1, initialResult: result, nextPartialResult: nextPartialResult)
            }
            return molecule.reduceDepthFirstTraverse(options: options, depth: depth + 1, initialResult: result, nextPartialResult: nextPartialResult)
        }
        if (options == .childFirst) {
            result = nextPartialResult(result, self, depth)
        }
        // if options == .leafOnly don't call on self.
        return result
    }
    
    func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol)->Void) {
        if (options == .parentFirst) {
            onVisit(depth, self)
        }
        children.forEach { (molecule) in
            if let additionalParent = molecule as? ParentMoleculeModelProtocol {
                // Safety net to make sure the ParentMoleculeModelProtocol's method extension is called over the base MoleculeModelProtocol.
                additionalParent.depthFirstTraverse(options: options, depth: depth + 1, onVisit: onVisit)
            } else {
                molecule.depthFirstTraverse(options: options, depth: depth + 1, onVisit: onVisit)
            }
        }
        if (options == .childFirst) {
            onVisit(depth, self)
        }
        // if options == .leafOnly don't call on self.
    }
}
