//
//  TemplateModelProtocol.swift
//  MVMCoreUI
//
//  Created by Suresh, Kamlesh on 11/22/19.
//  Copyright © 2019 Verizon Wireless. All rights reserved.
//


public protocol TemplateModelProtocol: PageModelProtocol, ModelProtocol, MoleculeTreeTraversalProtocol, ParentModelProtocol {
    var template: String { get }
    var rootMolecules: [MoleculeModelProtocol] { get }
}

public extension TemplateModelProtocol {
    
    var children: [MoleculeModelProtocol] {
        return rootMolecules
    }
    
    var template: String {
        get { return Self.identifier }
    }
    
    static var categoryCodingKey: String {
        return "template"
    }
        
    static var categoryName: String {
        return "\(TemplateModelProtocol.self)"
    }
    
    func reduceDepthFirstTraverse<Result>(options: TreeTraversalOptions, depth: Int, initialResult: Result, nextPartialResult: (Result, MoleculeModelProtocol, Int) -> Result) -> Result {
        return rootMolecules.reduceDepthFirstTraverse(options: options, depth: depth, initialResult: initialResult, nextPartialResult: nextPartialResult)
    }
    
    func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol, inout Bool) -> Void) {
        return rootMolecules.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
    }
}

extension TemplateModelProtocol {
    
    /// Recursively finds and replaces the first child matching the replacement molecule id property.
    mutating func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
        // Attempt root level replacement on the template model first.
        if try replaceChildMolecule(with: replacementMolecule) {
            return true
        }
        
        var didReplaceMolecule = false
        var possibleError: Error?
        // Dive into each root thereafter.
        depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in
            guard var parentMolecule = molecule as? ParentMoleculeModelProtocol else { return }
            do {
                didReplaceMolecule =  try parentMolecule.replaceChildMolecule(with: replacementMolecule)
            } catch {
                possibleError = error
            }
            stop = didReplaceMolecule || possibleError != nil
        }
        if let error = possibleError {
            throw error
        }
        return didReplaceMolecule
    }
}
