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


public protocol MoleculeDelegateProtocol: AnyObject {
    
    func getTemplateModel() -> TemplateModelProtocol?
    
    func getRootMolecules() -> [MoleculeModelProtocol]
    
    /// Returns a raw module map for the corresponding module name if the key name exists.
    func getModuleWithName(_ name: String?) -> [AnyHashable: Any]?
    
    /// Returns the decoded module for the corresponding module name if the key name exists. Throws if there is a decoding error.
    func getModuleWithName(_ moleculeName: String) throws -> MoleculeModelProtocol?
    
    /// Notifies the delegate that the molecule layout update. Should be called when the layout may change due to an async method. Mainly used for list or collections.
    func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) //optional
    
    /// Attempts to replace the molecules provided. Returns the ones that replaced successfully.
    func replaceMoleculeData(_ moleculeModels: [MoleculeModelProtocol], completionHandler: (([MoleculeModelProtocol])->Void)?)
}

extension MoleculeDelegateProtocol {
    
    public func getRootMolecules() -> [MoleculeModelProtocol] {
        getTemplateModel()?.rootMolecules ?? []
    }
    
    public func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }

    public func getModuleWithName(_ moleculeName: String) throws -> MoleculeModelProtocol? {
        let moduleJSON: [AnyHashable: Any]? = getModuleWithName(moleculeName)
        guard let moduleJSON = moduleJSON as? [String: Any] else { return nil }
        
        guard let moleculeName = moduleJSON.optionalStringForKey(KeyMoleculeName),
              let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) else {
            throw ModelRegistry.Error.decoderErrorModelNotMapped(identifer: moduleJSON.optionalStringForKey(KeyMoleculeName))
        }
        
        return try modelType.decode(jsonDict: moduleJSON as [String : Any]) as? MoleculeModelProtocol
    }
}

extension MoleculeDelegateProtocol where Self: TemplateProtocol {
    
    public func getTemplateModel() -> TemplateModelProtocol? {
        return templateModel
    }
    
    public func getRootMolecules() -> [MoleculeModelProtocol] {
        templateModel?.rootMolecules ?? []
    }
}

extension MoleculeDelegateProtocol where Self: MVMCoreViewControllerProtocol {
    public func getModuleWithName(_ name: String?) -> [AnyHashable : Any]? {
        guard let name = name else { return nil }
        return loadObject??.modulesJSON?.optionalDictionaryForKey(name)
    }
}
