//
//  ThreeLayerTableViewController.swift
//  MVMCoreUI
//
//  Created by Scott Pfeil on 4/18/19.
//  Copyright © 2019 Verizon Wireless. All rights reserved.
//

import UIKit
import MVMAnimationFramework


open class ThreeLayerTableViewController: ProgrammaticTableViewController {
    //--------------------------------------------------
    // MARK: - Main Views
    //--------------------------------------------------
    
    private var topView: UIView?
    private var bottomView: UIView?
    private var headerView: UIView?
    private var footerView: UIView?
    
    //--------------------------------------------------
    // MARK: - Properties
    //--------------------------------------------------
    
    public var bottomViewOutsideOfScrollArea: Bool = false
    public var topViewOutsideOfScrollArea: Bool = false
    
    //--------------------------------------------------
    // MARK: - Constraints
    //--------------------------------------------------
    
    private var topViewBottomConstraint: NSLayoutConstraint?
    private var bottomViewTopConstraint: NSLayoutConstraint?
    
    //--------------------------------------------------
    // MARK: - MFViewController
    //--------------------------------------------------
    
    open override func updateViews() {
        super.updateViews()
        let width = view.bounds.width
        if let topView = topView as? MVMCoreViewProtocol {
            topView.updateView(width)
            showHeader(width)
        }
        if let bottomView = bottomView as? MVMCoreViewProtocol {
            bottomView.updateView(width)
            showFooter(width)
        }
        tableView.reloadData()
    }
    
    open override func handleNewData() {
        super.handleNewData()
        createViewForTableHeader()
        createViewForTableFooter()
        tableView?.reloadData()
        accessibilityElements = [tableView as Any]
    }
    
    override open func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setNoSectionHeadersFooters()
        
        // Ensures the footer and headers are the right size
        tableView.frameChangeAction = { [weak self] in
            self?.view.setNeedsUpdateConstraints()
        }
    }

    //--------------------------------------------------
    // MARK: - Spacing
    //--------------------------------------------------
    
    // If both are subclassed to return a value, then the buttons will not be pinned towards the bottom because neither spacing would try to fill the screen.
    /// Space between the top view and the table sections, nil to fill. 0 default
    open func spaceBelowTopView() -> CGFloat? { 0 }
    
    /// Space between the bottom view and the table sections, nil to fill. nil default
    open func spaceAboveBottomView() -> CGFloat? { nil }
    
    /// can override to return a minimum fill space.
    open func minimumFillSpace() -> CGFloat { 0 }
    
    open override func updateViewConstraints() {
        guard let tableView = tableView else {
            super.updateViewConstraints()
            return
        }
        
        let minimumSpace: CGFloat = minimumFillSpace()
        var currentSpace: CGFloat = 0
        var totalMinimumSpace: CGFloat = 0

        var fillTop = false
        if spaceBelowTopView() == nil, tableView.tableHeaderView != nil {
            fillTop = true
            currentSpace += topViewBottomConstraint?.constant ?? 0
            totalMinimumSpace += minimumSpace
        }
        
        var fillBottom = false
        if spaceAboveBottomView() == nil, tableView.tableFooterView != nil {
            fillBottom = true
            currentSpace += bottomViewTopConstraint?.constant ?? 0
            totalMinimumSpace += minimumSpace
        }
        
        guard fillTop || fillBottom else {
            super.updateViewConstraints()
            return
        }
        
        let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace)
        let width = view.bounds.width
        if !MVMCoreGetterUtility.cgfequalwiththreshold(newSpace, currentSpace, 0.1) {
            if fillTop && fillBottom {
                // space both
                let half = newSpace / 2
                topViewBottomConstraint?.constant = half
                bottomViewTopConstraint?.constant = half
                showHeader(width)
                showFooter(width)
            } else if fillTop {
                topViewBottomConstraint?.constant = newSpace
                showHeader(width)
            } else if fillBottom {
                // Only bottom is spaced.
                bottomViewTopConstraint?.constant = newSpace
                showFooter(width)
            }
            refreshTable()
        }
        super.updateViewConstraints()
    }
    
    //--------------------------------------------------
    // MARK: - Header Footer
    //--------------------------------------------------
    
    /// Gets the top view and adds it to a spacing view, headerView, and then calls showHeader.
    open func createViewForTableHeader() {
        var topView = viewForTop()
        self.topView = topView
        
        // If top view is outside of scroll area, create a dummy view for the header. Small height is needed to stop apple from adding padding for grouped tables when no header.
        if topViewOutsideOfScrollArea {
            topView = MVMCoreUICommonViewsUtility.getView(with: 0.5)
        }
        let headerView = MVMCoreUICommonViewsUtility.commonView()
        headerView.addSubview(topView)
        topView.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
        topView.leftAnchor.constraint(equalTo: headerView.leftAnchor).isActive = true
        headerView.rightAnchor.constraint(equalTo: topView.rightAnchor).isActive = true
        topViewBottomConstraint = headerView.bottomAnchor.constraint(equalTo: topView.bottomAnchor, constant: spaceBelowTopView() ?? 0)
        topViewBottomConstraint?.isActive = true
        self.headerView = headerView
        showHeader(nil)
    }

    /// Gets the bottom view and adds it to a spacing view, footerView, and then calls showFooter.
    open func createViewForTableFooter() {
        var bottomView = viewForBottom()
        self.bottomView = bottomView
        
        // If bottom view is outside of scroll area, create a dummy view for the header. Small height is needed to stop apple from adding padding for grouped tables when no header.
        if bottomViewOutsideOfScrollArea {
            bottomView = MVMCoreUICommonViewsUtility.getView(with: 0.5)
        }
        let footerView = MVMCoreUICommonViewsUtility.commonView()
        footerView.addSubview(bottomView)
        bottomViewTopConstraint = bottomView.topAnchor.constraint(equalTo: footerView.topAnchor, constant: spaceAboveBottomView() ?? 0)
        bottomViewTopConstraint?.isActive = true
        bottomView.leftAnchor.constraint(equalTo: footerView.leftAnchor).isActive = true
        footerView.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true
        footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true
        self.footerView = footerView
        showFooter(nil)
    }
    
    /// Takes the current headerView and adds it to the tableHeaderView
    func showHeader(_ sizingWidth: CGFloat?) {
        headerView?.removeFromSuperview()
        tableView?.tableHeaderView = nil
        guard let headerView = headerView else { return }
        
        if topViewOutsideOfScrollArea,
            let topView = topView {
            // put top view outside of scrolling area.
            topConstraint?.isActive = false
            view.addSubview(topView)
            topView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
            topView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
            view.rightAnchor.constraint(equalTo: topView.rightAnchor).isActive = true
            tableView.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true
        } else {
            topConstraint?.isActive = true
        }
        // This extra view is needed because of the wonkiness of apple's table header. Things breaks if using autolayout.
        headerView.setNeedsLayout()
        headerView.layoutIfNeeded()
        MVMCoreUIUtility.sizeView(toFit: headerView)
        let tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: MVMCoreUIUtility.getWidth(), height: headerView.frame.height))
        tableHeaderView.addSubview(headerView)
        NSLayoutConstraint.constraintPinSubview(toSuperview: headerView)
        tableView?.tableHeaderView = tableHeaderView
    }
    
    /// Takes the current footerView and adds it to the tableFooterView
    func showFooter(_ sizingWidth: CGFloat?) {
        footerView?.removeFromSuperview()
        guard let footerView = footerView,
            let tableView = tableView else {
                self.tableView?.tableFooterView = nil
                return
        }
        
        if bottomViewOutsideOfScrollArea,
            let bottomView = bottomView {
            // put bottom view outside of scrolling area.
            bottomConstraint?.isActive = false
            view.addSubview(bottomView)
            bottomView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true
            bottomView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
            view.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true
            view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true
        } else {
            bottomConstraint?.isActive = true
        }
        
        // if footer already exists, use the same y location to avoid strange moving animation
        let y = tableView.tableFooterView?.frame.minY ?? 0.0
        
        // This extra view is needed because of the wonkiness of apple's table footer. Things breaks if using autolayout.
        MVMCoreUIUtility.sizeView(toFit: footerView)
        let tableFooterView = UIView(frame: CGRect(x: 0, y: y, width: MVMCoreUIUtility.getWidth(), height: footerView.frame.height))
        tableFooterView.addSubview(footerView)
        NSLayoutConstraint.constraintPinSubview(toSuperview: footerView)
        tableView.tableFooterView = tableFooterView
    }
    
    //--------------------------------------------------
    // MARK: - Functions to subclass
    //--------------------------------------------------
    
    /// Subclass for a top view.
    open func viewForTop() -> UIView {
        // Small height is needed to stop apple from adding padding for grouped tables when no header.
        MVMCoreUICommonViewsUtility.getView(with: 0.5)
    }
        
    /// Subclass for a bottom view.
    open func viewForBottom() -> UIView {
        // Default spacing is standard when no buttons.
        MVMCoreUICommonViewsUtility.getView(with: PaddingDefaultVerticalSpacing)
    }
    
    deinit {
        tableView?.delegate = nil
    }
    
    // Ensures the footer and headers are the right size
    func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) {
        view.setNeedsUpdateConstraints()
    }
}
