
public enum MolecularError: Swift.Error {
    case error(String)
    case countImbalance(String)
}


public protocol MoleculeModelProtocol: ModelProtocol, AccessibilityModelProtocol, MoleculeTreeTraversalProtocol {
    var moleculeName: String { get }
    var backgroundColor: Color? { get set }
}

public extension MoleculeModelProtocol {
    
    var moleculeName: String { type(of: self).identifier }
    
    static var categoryName: String { "\(MoleculeModelProtocol.self)" }
    
    static var categoryCodingKey: String { "moleculeName" }
}


// Helpers made due to swift not able to reconcile which category.
extension KeyedDecodingContainer where Key: CodingKey {
    /// Decodes to a registered molecule based on the identifier
    public func decodeMoleculeIfPresent<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> T? {
        
        guard let model: MoleculeModelProtocol = try decodeModelIfPresent(codingKey: codingKey) else { return nil }
        guard let modelT = model as? T else {
            let message = "ModelRegistry Error wrong type: \(codingKey.stringValue)"
            MVMCoreLoggingHandler.logDebugMessage(withDelegate: message)
            throw ModelRegistry.Error.decoderOther(message: message)
        }
        return modelT
    }
    
    /// Decodes to a registered molecule based on the identifier.
    public func decodeMolecule<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> T {
        guard let model: T = try decodeMoleculeIfPresent(codingKey: codingKey) else {
            MVMCoreLoggingHandler.logDebugMessage(withDelegate: "ModelRegistry Error decoderErrorObjectNotPresent: \(codingKey.stringValue)")
            throw ModelRegistry.Error.decoderErrorObjectNotPresent(codingKey: codingKey, codingPath: codingPath)
        }
        return model
    }
}

public extension MoleculeModelProtocol {
    
    // Base case. No additional children to traverse.
    func reduceDepthFirstTraverse<Result>(options: TreeTraversalOptions, depth: Int, initialResult: Result, nextPartialResult: (Result, MoleculeModelProtocol, Int)->Result) -> Result {
        return nextPartialResult(initialResult, self, depth)
    }
    
    // Base case. No additional children to traverse.
    func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol)->Void) {
        onVisit(depth, self)
    }
    
}

public extension Array where Element == MoleculeModelProtocol {
    
    func reduceDepthFirstTraverse<Result>(options: TreeTraversalOptions, depth: Int, initialResult: Result, nextPartialResult: (Result, MoleculeModelProtocol, Int)->Result) -> Result {
        return reduce(initialResult) { (result, molecule) -> Result in
            return molecule.reduceDepthFirstTraverse(options: options, depth: depth, initialResult: result, nextPartialResult: nextPartialResult)
        }
    }
    
    func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol)->Void) {
        forEach { (molecule) in
            molecule.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
        }
    }
}

// Would prefer these to be defined in MoleculeTreeTraversalProtocol that MoleculeModelProtocol inherits.
public extension Array where Element == MoleculeModelProtocol {
    
    func countMolecules() -> Int {
        return reduce(0) { (accumulator, molecule) in
            return accumulator + molecule.countMolecules()
        }
    }
    
    func printMolecules() {
        forEach { (molecule) in
            molecule.printMolecules()
        }
    }
    
    func allMoleculesOfType<T>() -> [T] {
        return reduce([]) { (accumulator, molecule) in
            return accumulator + molecule.allMoleculesOfType()
        }
    }
    
}
