//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Mapbox

class UserAnnotationCalloutView: UIView, MGLCalloutView, Themable {
    // MARK: - Constants
    
    private enum Constants {
        static let animationDuration: TimeInterval = 0.2
        static let bottomMargin: CGFloat = 3.0
    }
 
    // MARK: - Properties
    
    // MARK: Overrides
    
    var representedObject: MGLAnnotation
    
    lazy var leftAccessoryView = UIView()
    
    lazy var rightAccessoryView = UIView()
    
    var delegate: MGLCalloutViewDelegate?
    
    // Allow the callout to remain open during panning.
    let dismissesAutomatically = false
    
    let isAnchoredToAnnotation = true
    
    // https://github.com/mapbox/mapbox-gl-native/issues/9228
    override var center: CGPoint {
        set {
            var newCenter = newValue
            newCenter.y -= bounds.midY + Constants.bottomMargin
            super.center = newCenter
        }
        get {
            super.center
        }
    }
    
    // MARK: Private
    
    lazy var contentView = UserAnnotationCalloutContentView.instantiate()
    
    // MARK: - Setup
    
    required init(userLocationAnnotation: UserLocationAnnotation) {
        representedObject = userLocationAnnotation
        
        super.init(frame: .zero)
                        
        vc_addSubViewMatchingParent(contentView)
        
        update(theme: ThemeService.shared().theme)
        
        let size = UserAnnotationCalloutContentView.contentViewSize()

        frame = CGRect(origin: .zero, size: size)
    }
    
    @available(*, unavailable)
    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Public
    
    func update(theme: Theme) {
        contentView.update(theme: theme)
    }
    
    // MARK: - Overrides

    func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
        // Set callout above the marker view
        
        center = view.center.applying(CGAffineTransform(translationX: 0, y: view.bounds.height / 2 + bounds.height))
        
        delegate?.calloutViewWillAppear?(self)
        
        view.addSubview(self)
        
        if isCalloutTappable() {
            // Handle taps and eventually try to send them to the delegate (usually the map view).
            contentView.shareButton.addTarget(self, action: #selector(UserAnnotationCalloutView.calloutTapped), for: .touchUpInside)
        } else {
            // Disable tapping and highlighting.
            contentView.shareButton.isUserInteractionEnabled = false
        }
        
        if animated {
            alpha = 0
            
            UIView.animate(withDuration: Constants.animationDuration) { [weak self] in
                guard let strongSelf = self else {
                    return
                }
                
                strongSelf.alpha = 1
                strongSelf.delegate?.calloutViewDidAppear?(strongSelf)
            }
        } else {
            delegate?.calloutViewDidAppear?(self)
        }
    }
    
    func dismissCallout(animated: Bool) {
        if superview != nil {
            if animated {
                UIView.animate(withDuration: Constants.animationDuration, animations: { [weak self] in
                    self?.alpha = 0
                }, completion: { [weak self] _ in
                    self?.removeFromSuperview()
                })
            } else {
                removeFromSuperview()
            }
        }
    }

    // MARK: - Callout interaction handlers

    func isCalloutTappable() -> Bool {
        if let delegate = delegate {
            if delegate.responds(to: #selector(MGLCalloutViewDelegate.calloutViewShouldHighlight)) {
                return delegate.calloutViewShouldHighlight!(self)
            }
        }
        return false
    }

    @objc func calloutTapped() {
        if isCalloutTappable(), delegate!.responds(to: #selector(MGLCalloutViewDelegate.calloutViewTapped)) {
            delegate!.calloutViewTapped!(self)
        }
    }
}
