//
//  CarouselIndicator.swift
//  MVMCoreUI
//
//  Created by Kevin Christiano on 1/30/20.
//  Copyright © 2020 Verizon Wireless. All rights reserved.
//


open class CarouselIndicator: Control, CarouselPageControlProtocol {
    //--------------------------------------------------
    // MARK: - Constraints
    //--------------------------------------------------
    
    public var topConstraint: NSLayoutConstraint?
    public var bottomConstraint: NSLayoutConstraint?
    
    //--------------------------------------------------
    // MARK: - Properties
    //--------------------------------------------------
    
    public var uiGestures: Set<UIGestureRecognizer> = []
    
    /// Convenience to access the model.
    public var carouselIndicatorModel: CarouselIndicatorModel? {
        model as? CarouselIndicatorModel
    }
    
    /// Set this closure to perform an action when a different indicator was selected.
    /// Passes through oldIndex and newIndex, respectively.
    public var indicatorTouchAction: ((CarouselPageControlProtocol) -> ())?
    
    open override var isEnabled: Bool {
        didSet { isUserInteractionEnabled = isEnabled }
    }
    
    //--------------------------------------------------
    // MARK: - Computed Properties
    //--------------------------------------------------
    
    private(set) var previousIndex = 0
    
    public var currentIndex: Int {
        get { carouselIndicatorModel?.currentIndex ?? 0 }
        set (newIndex) {
            previousIndex = currentIndex
            carouselIndicatorModel?.currentIndex = newIndex
            
            updateUI(previousIndex: previousIndex,
                     newIndex: newIndex,
                     totalCount: numberOfPages,
                     isAnimated: carouselIndicatorModel?.animated ?? true)
        }
    }
    
    /// Holds the total number of pages displayed by the carousel.
    /// Updating this property will potentially update the UI.
    public var numberOfPages: Int {
        get { carouselIndicatorModel?.numberOfPages ?? 0 }
        set {
            guard numberOfPages != newValue else { return }
            
            carouselIndicatorModel?.numberOfPages = newValue
            reset()
            isHidden = (carouselIndicatorModel?.hidesForSinglePage ?? false) && newValue <= 1
            updateUI(previousIndex: previousIndex,
                     newIndex: currentIndex,
                     totalCount: newValue,
                     isAnimated: carouselIndicatorModel?.animated ?? true)
        }
    }
    
    public var disabledIndicatorColor: UIColor {
        get { carouselIndicatorModel?.disabledIndicatorColor.uiColor ?? .mvmCoolGray3 }
        set { carouselIndicatorModel?.disabledIndicatorColor = Color(uiColor: newValue) }
    }
    
    public var indicatorColor: UIColor {
        get {
            guard let model = carouselIndicatorModel else { return .mvmBlack }
            return model.inverted ? model.indicatorColor_inverted.uiColor : model.indicatorColor.uiColor
        }
        set {
            if let model = carouselIndicatorModel, model.inverted {
                model.indicatorColor_inverted = Color(uiColor: newValue)
            } else {
                carouselIndicatorModel?.indicatorColor = Color(uiColor: newValue)
            }
        }
    }
    
    var accessibilityValueFormat: String? {
        MVMCoreUIUtility.hardcodedString(withKey: (carouselIndicatorModel?.accessibilityHasSlidesInsteadOfPage ?? false) ? "MVMCoreUIPageControlslides_currentpage_index" : "MVMCoreUIPageControl_currentpage_index")
    }
    
    //--------------------------------------------------
    // MARK: - Initializers
    //--------------------------------------------------
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    convenience override init() {
        self.init(frame: .zero)
    }
    
    required public init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    //--------------------------------------------------
    // MARK: - Lifecycle
    //--------------------------------------------------
    
    open override func setupView() {
        super.setupView()
        
        isAccessibilityElement = true
        accessibilityTraits = .adjustable
        setupGestures()
    }
    
    //--------------------------------------------------
    // MARK: - UITouch
    //--------------------------------------------------
    
    private func setupGestures() {
        
        let tap = UITapGestureRecognizer(target: self, action: #selector(indicatorTapped))
        let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeft))
        let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeRight))
        
        leftSwipe.direction = .left
        rightSwipe.direction = .right
        
        addGestureRecognizer(tap)
        addGestureRecognizer(leftSwipe)
        addGestureRecognizer(rightSwipe)
        
        uiGestures.insert(tap)
        uiGestures.insert(leftSwipe)
        uiGestures.insert(rightSwipe)
    }
    
    func incrementCurrentIndex() {
        currentIndex = (currentIndex + 1) % numberOfPages
        performAction()
    }
    
    func decrementCurrentIndex() {
        let newIndex = currentIndex - 1
        currentIndex = newIndex < 0 ? numberOfPages - 1 : newIndex
        performAction()
    }
    
    /// Increments the currentIndex value.
    @objc func swipeLeft() {
        incrementCurrentIndex()
    }
    
    /// Decrement the currentIndex value
    @objc func swipeRight() {
        decrementCurrentIndex()
    }
    
    /// Handles tap logic for Indicator
    @objc func indicatorTapped(_ tapGesture: UITapGestureRecognizer?) {
        
        let touchPoint = tapGesture?.location(in: self)
        let touchPoint_X = touchPoint?.x ?? 0.0
        
        assessTouchOf(touchPoint_X)
    }
    
    /// Determines where a touch in the indicator translate to an index selection.
    func assessTouchOf(_ touchPoint_X: CGFloat) { }
    
    //--------------------------------------------------
    // MARK: - Methods
    //--------------------------------------------------
    
    /// Called to update the Indicator UI with the latest values.
    open func updateUI(previousIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool) { }
    
    public func performAction() {
        
        sendActions(for: .valueChanged)
        indicatorTouchAction?(self)
    }
    
    public func scrollViewDidScroll(_ collectionView: UICollectionView) { }
    
    //--------------------------------------------------
    // MARK: - MoleculeViewProtocol
    //--------------------------------------------------
    
    open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
        super.set(with: model, delegateObject, additionalData)
        
        guard let model = model as? CarouselIndicatorModel else { return }
        
        previousIndex = 0
        indicatorColor = model.inverted ? model.indicatorColor_inverted.uiColor : model.indicatorColor.uiColor
        disabledIndicatorColor = model.disabledIndicatorColor.uiColor
        currentIndex = model.currentIndex
        isEnabled = model.enabled
        
        formatAccessibilityValue(index: currentIndex + 1, total: numberOfPages)
        isHidden = model.hidesForSinglePage && numberOfPages <= 1
    }
    
    //--------------------------------------------------
    // MARK: - Accessibility
    //--------------------------------------------------
    
    open override func accessibilityIncrement() {
        adjustAccessibility(toPage: currentIndex + 1)
    }
    
    open override func accessibilityDecrement() {
        adjustAccessibility(toPage: currentIndex - 1)
    }
    
    func formatAccessibilityValue(index: Int, total: Int) {
        
        guard let accessibleFormat = accessibilityValueFormat,
              let accessibleIndex = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: index))
        else { return }
        
        accessibilityValue = String(format: accessibleFormat, accessibleIndex, total)
    }
    
    func adjustAccessibility(toPage index: Int) {
        
        formatAccessibilityValue(index: index, total: numberOfPages)
        
        if (index < numberOfPages && index >= 0) || carouselIndicatorModel?.alwaysSendAction ?? false {
            carouselIndicatorModel?.animated = false
            previousIndex = currentIndex
            currentIndex = index
            performAction()
        }
    }
    
    func setTopBottomSpace(constant: CGFloat) {
        
        bottomConstraint?.constant = constant
        topConstraint?.constant = constant
    }
}
