//
//  MoleculeStackTemplate.swift
//  MVMCoreUI
//
//  Created by Scott Pfeil on 2/11/19.
//  Copyright © 2019 Verizon Wireless. All rights reserved.
//

import UIKit


open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
    //--------------------------------------------------
    // MARK: - Properties
    //--------------------------------------------------
    
    var observer: NSKeyValueObservation?
    public var templateModel: StackPageTemplateModel?
    
    //--------------------------------------------------
    // MARK: - Lifecycle
    //--------------------------------------------------
    
    open override func handleNewData() {
        topViewOutsideOfScroll = templateModel?.anchorHeader ?? false
        bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false
        super.handleNewData()
    }
    
    // For subclassing the model.
    open func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> StackPageTemplateModel {
        return try decoder.decode(StackPageTemplateModel.self, from: data)
    }
    
    open override func parsePageJSON() throws {
        try parseTemplate(json: loadObject?.pageJSON)
        try super.parsePageJSON()
    }
    
    open override var loadObject: MVMCoreLoadObject? {
        didSet {
            if loadObject != oldValue {
                updateRequiredModules()
                observer?.invalidate()
                if let newObject = loadObject {
                    observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] (object, change) in
                        self?.updateRequiredModules()
                    }
                }
            }
        }
    }
    
    open override func spaceBetweenTopAndMiddle() -> CGFloat? { 0 }
    
    open override func viewForTop() -> UIView? {
        guard let headerModel = templateModel?.header,
            let molecule = ModelRegistry.createMolecule(headerModel, delegateObject: delegateObjectIVar)
            else { return nil }
        
        return molecule
    }
    
    open override func viewForMiddle() -> UIView? {
        guard let moleculeStackModel = templateModel?.moleculeStack else { return  nil }
        
        // By default: Stack template stack has vertical space before the first item, dynamic stack items have default horizontal padding. 
        let stack = MoleculeStackView(frame: .zero)
        moleculeStackModel.useStackSpacingBeforeFirstItem = true
        for stackItem in moleculeStackModel.molecules {
            guard let stackItem = stackItem as? MoleculeStackItemModel,
                stackItem.useHorizontalMargins == nil
                else { continue }
            
            stackItem.useHorizontalMargins = true
        }
        stack.set(with: moleculeStackModel, delegateObject() as? MVMCoreUIDelegateObject, nil)
        return stack
    }
    
    override open func viewForBottom() -> UIView? {
        guard let footerModel = templateModel?.footer,
            let molecule = ModelRegistry.createMolecule(footerModel, delegateObject: delegateObjectIVar)
            else { return nil }
        
        return molecule
    }
    
    //--------------------------------------------------
    // MARK: - Cache Handling
    //--------------------------------------------------
    
    /// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map.
    open func updateRequiredModules() {
        if let requiredModules = requiredModules(), let pageType = pageType {
            MVMCoreViewControllerMappingObject.shared()?.addRequiredModules(toMapping: requiredModules, forPageType: pageType)
        }
    }
    
    /// Gets modules required by the loadObject.pageJSON.
    open func requiredModules() -> [Any]? {
        var modules: [String]? = []
        var errors: [MVMCoreErrorObject]? = nil
        ModelRegistry.addRequiredModules(for: templateModel?.header, delegateObjectIVar, moduleList: &modules, errorList: &errors)
        ModelRegistry.addRequiredModules(for: templateModel?.footer, delegateObjectIVar, moduleList: &modules, errorList: &errors)
        ModelRegistry.addRequiredModules(for: templateModel?.moleculeStack, delegateObjectIVar, moduleList: &modules, errorList: &errors)
        return modules
    }
}
