//
//  MVMCoreUIThreeLayerViewController.swift
//  MVMCoreUI
//
//  Created by Scott Pfeil on 2/13/19.
//  Copyright © 2019 Verizon Wireless. All rights reserved.
//
//  This class has three views, a top, middle, and bottom. The top view is just the first view in the scrollview. The middle can be aligned center, top, or bottom depending on if spaceBetweenTopAndMiddle() or spaceBetweenMiddleAndBottom() return values. The bottom view, if inside the scrollview, appears pinned to the bottom unless content pushes it off the screen. If outside the scroll, it's pinned to the bottom under the scrollview.

import UIKit
import MVMAnimationFramework

open class ThreeLayerViewController: ProgrammaticScrollViewController {
    
    // The three main views
    var topView: UIView?
    var middleView: UIView?
    var bottomView: UIView?
    var useMargins: Bool = false
    
    // The top view can be put outside of the scrolling area.
    var topViewOutsideOfScroll = false
    
    // The bottom view can be put outside of the scrolling area.
    var bottomViewOutsideOfScroll = false
    
    var heightConstraint: NSLayoutConstraint?
 
    open override func updateViews() {
        super.updateViews()
        let width = view.bounds.width
        MFStyler.setDefaultMarginsFor(contentView, size: width)
        if let topView = topView as? MVMCoreViewProtocol {
            topView.updateView(width)
        }
        if let middleView = middleView as? MVMCoreViewProtocol {
            middleView.updateView(width)
        }
        if let bottomView = bottomView as? MVMCoreViewProtocol {
            bottomView.updateView(width)
        }
    }
    
    open override func loadView() {
        super.loadView()
        // The height is used to keep the bottom view at the bottom.
        if let contentView = contentView, let scrollView = scrollView {
            heightConstraint = contentView.heightAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.heightAnchor, multiplier: 1.0)
            heightConstraint?.priority = UILayoutPriority.defaultLow
        }
    }
    
    open override func handleNewData() {
        super.handleNewData()

        // Removes the views
        topView?.removeFromSuperview()
        middleView?.removeFromSuperview()
        bottomView?.removeFromSuperview()
        if let subViews = contentView?.subviews {
            for view in subViews {
                view.removeFromSuperview()
            }
        }
        
        // Reset constraints
        topConstraint?.isActive = true
        bottomConstraint?.isActive = true
        heightConstraint?.isActive = false
        
        setupLayers()
    }
    
    //MARK:-Functions to subclass
    // Subclass for a top view.
    open func viewForTop() -> UIView? {
        return nil
    }
    
    // Subclass for a middle view.
    open func viewForMiddle() -> UIView? {
        return nil
    }
    
    // Subclass for a bottom view.
    open func viewForBottom() -> UIView? {
        return nil
    }
    
    // If a value is set, the middle view is pinned this value below the top view, if not, space is left to fill.
    open func spaceBetweenTopAndMiddle() -> CGFloat? {
        return nil
    }
    
    // If a value is set, the middle view is pinned this value above the bottom view, if not, space is left to fill.
    open func spaceBetweenMiddleAndBottom() -> CGFloat? {
        return nil
    }
}

//MARK:-Setup
extension ThreeLayerViewController {
    func setupViewAsTop() -> UIView {
        var topView = viewForTop() ?? MVMCoreUICommonViewsUtility.getView(with: 0)
        self.topView = topView
        
        // Adds the top view outside the scroll if directed.
        if topViewOutsideOfScroll {
            topConstraint?.isActive = false
            addViewOutsideOfScrollViewTop(topView)
            
            // Adds and returns an empty view to use for the internal logic.
            topView = MVMCoreUICommonViewsUtility.getView(with: 0)
            addViewInsideOfScrollViewTop(topView)
        } else {
            topConstraint?.isActive = true
            addViewInsideOfScrollViewTop(topView)
        }
        return topView
    }
    
    func setupViewAsMiddle() -> UIView {
        let middleView = viewForMiddle() ?? MVMCoreUICommonViewsUtility.getView(with: 0)
        self.middleView = middleView
        
        guard let contentView = contentView else { return middleView }
        contentView.addSubview(middleView)
        NSLayoutConstraint.pinViewLeft(toSuperview: middleView, useMargins: useMargins, constant: 0).isActive = true
        NSLayoutConstraint.pinViewRight(toSuperview: middleView, useMargins: useMargins, constant: 0).isActive = true
        middleView.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical)
        return middleView
    }
    
    func setupViewAsBottom() -> UIView {
        var bottomView = viewForBottom() ?? MVMCoreUICommonViewsUtility.getView(with: 0)
        self.bottomView = bottomView
        
        // Adds the bottom view outside the scroll if directed.
        if bottomViewOutsideOfScroll {
            bottomConstraint?.isActive = false
            addViewOutsideOfScrollViewBottom(bottomView)
            
            // Adds and returns an empty view to use for the internal logic.
            bottomView = MVMCoreUICommonViewsUtility.getView(with: 0)
            addViewInsideOfScrollViewBottom(bottomView)
        } else {
            bottomConstraint?.isActive = true
            addViewInsideOfScrollViewBottom(bottomView)
        }
        return bottomView
    }
    
    func setupLayers() {
        guard let contentView = contentView else { return }
        let topView = setupViewAsTop()
        let middleView = setupViewAsMiddle()
        let bottomView = setupViewAsBottom()
        let spaceAbove = spaceBetweenTopAndMiddle()
        let spaceBelow = spaceBetweenMiddleAndBottom()
        if let spaceAbove = spaceAbove, let spaceBelow = spaceBelow {
            // Both top and bottom space set, buttons not pinned to bottom.
            middleView.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: spaceAbove).isActive = true
            bottomView.topAnchor.constraint(equalTo: middleView.bottomAnchor, constant: spaceBelow).isActive = true
        } else {
            heightConstraint?.isActive = true
            
            if let spaceAbove = spaceAbove {
                // Space above is set, space below is free.
                let bottomSpacer = MVMCoreUICommonViewsUtility.commonView()
                contentView.addSubview(bottomSpacer)
                bottomSpacer.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
                contentView.rightAnchor.constraint(equalTo: bottomSpacer.rightAnchor).isActive = true
                bottomSpacer.heightAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true
                middleView.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: spaceAbove).isActive = true
                bottomSpacer.topAnchor.constraint(equalTo: middleView.bottomAnchor).isActive = true
                bottomView.topAnchor.constraint(equalTo: bottomSpacer.bottomAnchor).isActive = true
            } else if let spaceBelow = spaceBelow {
                // Space below is set, space above is free.
                let topSpacer = MVMCoreUICommonViewsUtility.commonView()
                contentView.addSubview(topSpacer)
                topSpacer.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
                contentView.rightAnchor.constraint(equalTo: topSpacer.rightAnchor).isActive = true
                topSpacer.heightAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true
                topSpacer.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true
                middleView.topAnchor.constraint(equalTo: topSpacer.bottomAnchor).isActive = true
                bottomView.topAnchor.constraint(equalTo: middleView.bottomAnchor, constant: spaceBelow).isActive = true
            } else {
                // No set space above or below, make the spacers the same height with a default minimum.
                let topSpacer = MVMCoreUICommonViewsUtility.commonView()
                let bottomSpacer = MVMCoreUICommonViewsUtility.commonView()
                contentView.addSubview(topSpacer)
                contentView.addSubview(bottomSpacer)
                topSpacer.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
                contentView.rightAnchor.constraint(equalTo: topSpacer.rightAnchor).isActive = true
                bottomSpacer.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
                contentView.rightAnchor.constraint(equalTo: bottomSpacer.rightAnchor).isActive = true
                topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor).isActive = true
                topSpacer.heightAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true
                topSpacer.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true
                middleView.topAnchor.constraint(equalTo: topSpacer.bottomAnchor).isActive = true
                bottomSpacer.topAnchor.constraint(equalTo: middleView.bottomAnchor).isActive = true
                bottomView.topAnchor.constraint(equalTo: bottomSpacer.bottomAnchor).isActive = true
            }
        }
    }
    
    func addViewInsideOfScrollViewTop(_ view: UIView) {
        guard let contentView = contentView else { return }
        contentView.addSubview(view)
        NSLayoutConstraint.pinViewTop(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
        NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
        NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
    }
       
    func addViewOutsideOfScrollViewTop(_ view: UIView) {
        guard let scrollView = scrollView, let parentView = self.view else { return }
        parentView.addSubview(view)
        scrollView.topAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
        NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
        view.topAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.topAnchor).isActive = true
   }
    
    func addViewInsideOfScrollViewBottom(_ view: UIView) {
        guard let contentView = contentView else { return }
        contentView.addSubview(view);
        NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
        NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
        NSLayoutConstraint.pinViewBottom(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
    }
    
    func addViewOutsideOfScrollViewBottom(_ view: UIView) {
        guard let scrollView = scrollView, let parentView = self.view else { return }
        parentView.addSubview(view)
        view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
        NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
        parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    }
}
