//
//  Tabs.swift
//  MVMCoreUI
//
//  Created by Ryan on 2/7/20.
//  Copyright © 2020 Verizon Wireless. All rights reserved.
//

import UIKit

@objc public protocol TabsDelegate {
    func shouldSelectItem(_ indexPath: IndexPath, tabs: Tabs) -> Bool
    func didSelectItem(_ indexPath: IndexPath, tabs: Tabs)
}

@objcMembers open class Tabs: View, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol {

    public var tabsModel: TabsModel? {
        get { return model as? TabsModel }
    }
    
    var delegateObject: MVMCoreUIDelegateObject?
    var additionalData: [AnyHashable: Any]?
    
    let layout = UICollectionViewFlowLayout()
    public var collectionView: UICollectionView?
    
    let bottomScrollView = UIScrollView(frame: .zero)
    let bottomContentView = View()
    let bottomLine = Line()
    let selectionLine = View()
    var selectionLineLeftConstraint: NSLayoutConstraint?
    var selectionLineWidthConstraint: NSLayoutConstraint?
    
    private var widthLabel = Label()
    
    //delegate
    weak public var delegate: TabsDelegate?
    
    //control var
    public var selectedIndex: Int = 0
    public var paddingBeforeFirstTab: Bool = true
    
    //constant
    let TabCellId = "TabCell"
    public let itemSpacing: CGFloat = 20.0
    public let cellHeight: CGFloat = 28.0
    public let selectionLineHeight: CGFloat = 4.0
    public let minimumItemWidth: CGFloat = 32.0
    public let selectionLineMovingTime: TimeInterval = 0.2
    
    //-------------------------------------------------
    // MARK:- Layout Views
    //-------------------------------------------------
    
    open override func reset() {
        super.reset()
        selectedIndex = 0
        paddingBeforeFirstTab = true
    }
    
    open override func updateView(_ size: CGFloat) {
        super.updateView(size)
    }
    
    open override func setupView() {
        super.setupView()
        backgroundColor = .white
        addSubview(bottomLine)
        setupCollectionView()
        setupSelectionLine()
        setupConstraints()
    }
    
    func setupCollectionView () {
        layout.scrollDirection = .horizontal
        layout.minimumLineSpacing = 0
        
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.register(TabItemCell.self, forCellWithReuseIdentifier: TabCellId)
        collectionView.backgroundColor = .clear
        collectionView.showsVerticalScrollIndicator = false
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.dataSource = self
        collectionView.delegate = self
        addSubview(collectionView)
        self.collectionView = collectionView
    }
    
    func setupSelectionLine() {
        bottomScrollView.translatesAutoresizingMaskIntoConstraints = false
        bottomScrollView.delegate = self
        addSubview(bottomScrollView)
        bottomScrollView.addSubview(bottomContentView)
        selectionLine.backgroundColor = .mvmRed
        bottomContentView.addSubview(selectionLine)
        bringSubviewToFront(bottomScrollView)
    }
    
    func setupConstraints() {
        //collection view
        NSLayoutConstraint.constraintPinSubview(collectionView, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true)
        collectionView?.heightAnchor.constraint(equalToConstant: cellHeight).isActive = true
        
        //selection line
        bottomScrollView.topAnchor.constraint(equalTo: collectionView!.bottomAnchor).isActive = true;
        NSLayoutConstraint.constraintPinSubview(bottomScrollView, pinTop: false, pinBottom: false, pinLeft: true, pinRight: true)
        bottomScrollView.heightAnchor.constraint(equalToConstant: selectionLineHeight).isActive = true
        NSLayoutConstraint.constraintPinSubview(selectionLine, pinTop: true, pinBottom: true, pinLeft: false, pinRight: false)
        selectionLine.heightAnchor.constraint(equalToConstant: selectionLineHeight).isActive = true
        selectionLineLeftConstraint = selectionLine.leftAnchor.constraint(equalTo: bottomContentView.leftAnchor)
        selectionLineLeftConstraint?.isActive = true
        selectionLineWidthConstraint = selectionLine.widthAnchor.constraint(equalToConstant: minimumItemWidth)
        selectionLineWidthConstraint?.isActive = true
        NSLayoutConstraint.constraintPinSubview(toSuperview: bottomContentView)
        
        //bottom line
        bottomLine.topAnchor.constraint(equalTo: bottomScrollView.bottomAnchor).isActive = true;
        NSLayoutConstraint.constraintPinSubview(bottomLine, pinTop: false, pinBottom: true, pinLeft: true, pinRight: true)
    }
    
    //-------------------------------------------------
    // MARK:- Control Methods
    //-------------------------------------------------
    
    public func selectIndex(_ index: Int, animated: Bool) {
        guard let _ = collectionView, tabsModel?.tabs.count ?? 0 > 0 else {
            selectedIndex = index
            tabsModel?.selectedIndex = index
            return
        }
        MVMCoreDispatchUtility.performBlock(onMainThread: {
            let currentIndex = self.selectedIndex
            self.selectedIndex = index
            self.tabsModel?.selectedIndex = index
            self.deselect(indexPath: IndexPath(row: currentIndex, section: 0))
            self.selectItem(atIndexPath: IndexPath(row: index, section: 0), animated: animated)
        })
    }

    public func reloadData() {
        collectionView?.reloadData()
    }
    
    //-------------------------------------------------
    // MARK:- Molecule Setup
    //-------------------------------------------------
    
    override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
        super.set(with: model, delegateObject, additionalData)
        self.delegateObject = delegateObject
        self.additionalData = additionalData
        selectedIndex = tabsModel?.selectedIndex ?? 0
        // TODO: Commented out until we have model support for bar color. Should also do unselected color.
        //selectionLine.backgroundColor = tabsModel?.selectedColor.uiColor
        reloadData()
    }
}

//-------------------------------------------------
// MARK:- Collection View Methods
//-------------------------------------------------

extension Tabs: UICollectionViewDataSource {
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return tabsModel?.tabs.count ?? 0
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let labelModel = tabsModel?.tabs[indexPath.row].label, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TabCellId, for: indexPath) as? TabItemCell else {
            return UICollectionViewCell()
        }
        cell.updateCell(labelModel: labelModel, indexPath: indexPath, delegateObject: delegateObject, additionalData: additionalData, selected: indexPath.row == selectedIndex, tabsModel: tabsModel)
        return cell
    }
}

extension Tabs: UICollectionViewDelegateFlowLayout {
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        guard self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) != 2 else {
            // If two tabs, take up the screen
            let insets = self.collectionView(collectionView, layout: collectionViewLayout, insetForSectionAt: indexPath.section)
            let width = (collectionView.bounds.width / 2.0) - insets.left - insets.right
            return CGSize(width: width, height: cellHeight)
        }
        guard let labelModel = tabsModel?.tabs[indexPath.row].label else {
            return .zero
        }
        return CGSize(width: max(minimumItemWidth, getLabelWidth(labelModel).width), height: cellHeight)
    }
    
    //pre calculate the width of the collection cell
    //when user select tabs, it will reload related collectionview, if we use autosize, it would relayout the width, need to keep the cell width constant.
    func getLabelWidth(_ labelModel: LabelModel?) -> CGSize {
        guard let labelModel = labelModel else { return .zero}
        widthLabel.set(with: labelModel, nil, nil)
        let cgSize = widthLabel.intrinsicContentSize
        widthLabel.reset()
        return cgSize
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        guard section == 0 else {
            return UIEdgeInsets(top: 0, left: itemSpacing, bottom: 0, right: 0)
        }
        guard paddingBeforeFirstTab else {
            return .zero
        }
        return UIEdgeInsets(top: 0, left: Padding.Component.horizontalPaddingForApplicationWidth, bottom: 0, right: 0)
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        // If two tabs, take up the screen, no space between items
        guard self.collectionView(collectionView, numberOfItemsInSection: section) != 2 else {
            return 0
        }
        return itemSpacing
    }
    
    public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
        return delegate?.shouldSelectItem(indexPath, tabs: self) ?? true
    }
    
    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        selectIndex(indexPath.row, animated: true)
    }
    
    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        guard let tabCell = cell as? TabItemCell else { return }
        if indexPath.row == selectedIndex {
            moveSelectionLine(toIndex: indexPath, animated: false, cell: tabCell)
        }
    }
    
    func deselect(indexPath:IndexPath) {
        collectionView?.deselectItem(at: indexPath, animated: false)
        collectionView?.reloadItems(at: [indexPath])
    }
    
    func selectItem(atIndexPath indexPath: IndexPath, animated: Bool) {
        
        guard let collect = collectionView, tabsModel?.tabs.count ?? 0 > 0 else { return }

        collect.selectItem(at: indexPath, animated: animated, scrollPosition: .centeredHorizontally)
        guard let tabCell = collect.cellForItem(at: indexPath) as? TabItemCell, let tabsModel = tabsModel else { return }
        moveSelectionLine(toIndex: indexPath, animated: animated, cell: tabCell)
        tabCell.label.textColor = tabsModel.selectedColor.uiColor
        tabCell.updateAccessibility(indexPath: indexPath, selected: true, tabsModel: tabsModel)
        tabCell.setNeedsDisplay()
        tabCell.setNeedsLayout()
        tabCell.layoutIfNeeded()
        if let delegate = delegate {
            delegate.didSelectItem(indexPath, tabs: self)
        } else if let action = tabsModel.tabs[selectedIndex].action {
            MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: [KeySourceModel: tabsModel], delegateObject:delegateObject)
        }
    }
}


extension Tabs: UIScrollViewDelegate {
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        /*bottomScrollview is subview of self, it's not belongs to collectionview.
        When collectionview is scrolling, bottomScrollView will stay without moving
        Adding collectionview's offset to bottomScrollView, will make the bottomScrollview looks like scrolling with the selected tab item.
         */
        guard let offsetX = collectionView?.contentOffset.x else { return }
        bottomScrollView.setContentOffset(CGPoint(x: offsetX, y: bottomScrollView.contentOffset.y), animated: false)
    }
}


//-------------------------------------------------
// MARK:- Bottom Line Methods
//-------------------------------------------------
extension Tabs {
    func moveSelectionLine(toIndex indexPath: IndexPath, animated: Bool, cell: TabItemCell) {
        guard let collect = collectionView else {return}
        
        let size = collectionView(collect, layout: layout, sizeForItemAt: indexPath)
        let animationBlock = {
            [weak self] in
            self?.selectionLineWidthConstraint?.constant = size.width
            self?.selectionLineLeftConstraint?.constant = cell.frame.origin.x
            self?.bottomContentView.layoutIfNeeded()
        }
        if animated {
            UIView.animate(withDuration: selectionLineMovingTime, animations: animationBlock)
        } else {
            animationBlock()
        }
    }
    
    /// Adjust the line based on the percentage
    func progress(from index: Int, toIndex: Int, percentage: CGFloat) {
        let fromIndexPath = IndexPath(row: index, section: 0)
        let toIndexPath = IndexPath(row: toIndex, section: 0)
        guard let collection = collectionView,
              let fromCell = collection.cellForItem(at: fromIndexPath),
              let toCell = collection.cellForItem(at: toIndexPath) else { return }

        // setting the width for percentage
        selectionLineWidthConstraint?.constant = (toCell.bounds.width - fromCell.bounds.width) * percentage + fromCell.bounds.width

        // setting the x for percentage
        let originalX = fromCell.frame.origin.x
        let toX = toCell.frame.origin.x
        let xDifference = toX - originalX
        let finalX = (xDifference * percentage) + originalX
        selectionLineLeftConstraint?.constant = finalX
        
        bottomContentView.layoutIfNeeded()
    }
}

@objcMembers public class TabItemCell: CollectionViewCell {
    public let label = Label()
    public var labelModel: LabelModel?
    
    public override func setupView() {
        super.setupView()
        contentView.addSubview(label)
        NSLayoutConstraint.constraintPinSubview(label, pinTop: false, topConstant: 0, pinBottom: true, bottomConstant: 6, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0)
        label.baselineAdjustment = .alignCenters
    }
    
    public func updateCell(labelModel: LabelModel, indexPath: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, selected: Bool, tabsModel: TabsModel?) {
        label.reset()
        label.set(with: labelModel, delegateObject, additionalData)
        self.labelModel = labelModel
        if selected {
            label.textColor = tabsModel?.selectedColor.uiColor ?? .black
        } else {
            label.textColor = .mvmCoolGray6
        }
        updateAccessibility(indexPath: indexPath, selected: selected, tabsModel: tabsModel)
    }
    
    public func updateAccessibility(indexPath: IndexPath, selected: Bool, tabsModel: TabsModel?) {
        //Accessibility
        isAccessibilityElement = false
        contentView.isAccessibilityElement = true
        let accKey = selected ? "toptabbar_tab_selected" : "AccTab"
        let accLabel = "\(label.text ?? "") \(MVMCoreUIUtility.hardcodedString(withKey: accKey) ?? "")"
        let accOrder = String(format: MVMCoreUIUtility.hardcodedString(withKey: "AccTabIndex") ?? "", indexPath.row + 1, tabsModel?.tabs.count ?? 0)
        contentView.accessibilityLabel = "\(accLabel) \(accOrder)"
        contentView.accessibilityHint = selected ? nil : MVMCoreUIUtility.hardcodedString(withKey: "AccTabHint")
    }
}


