// 
// 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 UIKit

/// Delegate for `PresenceIndicatorView`.
@objc protocol PresenceIndicatorViewDelegate: AnyObject {
    func presenceIndicatorViewDidUpdateVisibility(_ presenceIndicatorView: PresenceIndicatorView, isHidden: Bool)
}

/// `PresenceIndicatorView` is used to display a presence indicator over an avatar.
@objcMembers
@IBDesignable
final class PresenceIndicatorView: UIView {
    // MARK: - Internal Properties
    @IBInspectable var borderWidth: CGFloat = 0.0
    var borderColor: UIColor = ThemeService.shared().theme.backgroundColor
    
    // MARK: - Properties

    // MARK: Private
    private let borderLayer = CALayer()
    private var listener: PresenceIndicatorListener?

    // MARK: Internal
    weak var delegate: PresenceIndicatorViewDelegate?

    // MARK: Override
    override var isHidden: Bool {
        didSet {
            if oldValue != isHidden, let delegate = delegate {
                delegate.presenceIndicatorViewDidUpdateVisibility(self, isHidden: isHidden)
            }
        }
    }

    // MARK: - Private Constants
    private enum Constants {
        static let borderLayerOffset: CGFloat = 1.0
    }
    
    // MARK: - Init
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }
    
    // MARK: - Override
    override func layoutSubviews() {
        super.layoutSubviews()
        
        // This sets up a slightly larger border layer to avoid common iOS
        // issue of having a very thin but noticeable additional border of
        // backgroundColor when using corner radius + borderWidth.
        self.layer.cornerRadius = self.frame.width / 2.0
        self.borderLayer.borderWidth = self.borderWidth + Constants.borderLayerOffset
        self.borderLayer.cornerRadius = self.layer.cornerRadius + Constants.borderLayerOffset
        self.borderLayer.frame = self.frame.withOffset(Constants.borderLayerOffset)
    }
    
    // MARK: - Internal Methods
    /// Configures the view and starts listening Presence updates for given user.
    ///
    /// - Parameters:
    ///   - userId: the user id
    ///   - presence: the initial Presence of the user
    func configure(userId: String, presence: MXPresence) {
        setPresence(presence)
        self.listener = PresenceIndicatorListener(userId: userId,
                                                  presence: presence) { [weak self] presence in
            guard let self = self else { return }
            self.setPresence(presence)
        }
    }

    /// Stop listening to Presence updates and hides the indicator.
    /// This should be called before reuse or if current room moves from direct to non-direct.
    func stopListeningPresenceUpdates() {
        self.listener = nil
        self.isHidden = true
    }
}
    
// MARK: - Private Methods
private extension PresenceIndicatorView {
    func setup() {
        self.layer.addSublayer(borderLayer)
    }

    /// Updates presence indicator with given `MXPresence`.
    ///
    /// - Parameters:
    ///   - presence: `MXPresence` to display
    func setPresence(_ presence: MXPresence) {
        switch presence {
        case .online:
            self.backgroundColor = ThemeService.shared().theme.tintColor
            self.borderLayer.borderColor = self.borderColor.cgColor
            self.isHidden = false
        case .offline, .unavailable:
            self.backgroundColor = ThemeService.shared().theme.tabBarUnselectedItemTintColor
            self.borderLayer.borderColor = self.borderColor.cgColor
            self.isHidden = false
        default:
            self.backgroundColor = UIColor.clear
            self.borderLayer.borderColor = UIColor.clear.cgColor
            self.isHidden = true
        }
    }
}

// MARK: - CGRect Helper
private extension CGRect {
    /// Returns a `CGRect` with given offset on each side.
    ///
    /// - Parameters:
    ///   - offset: offset to apply
    /// - Returns: `CGRect` with given offset
    func withOffset(_ offset: CGFloat) -> CGRect {
        return CGRect(x: -offset, y: -offset,
                      width: self.width + 2.0 * offset,
                      height: self.height + 2.0 * offset)
    }
}
