<commit_before>//
//  ChatTextCell.swift
//  Rocket.Chat
//
//  Created by <NAME> on 7/25/16.
//  Copyright © 2016 Rocket.Chat. All rights reserved.
//

import UIKit

protocol ChatMessageCellProtocol: ChatMessageURLViewProtocol, ChatMessageVideoViewProtocol, ChatMessageImageViewProtocol, ChatMessageTextViewProtocol {
    func openURL(url: URL)
    func handleLongPressMessageCell(_ message: Message, view: UIView, recognizer: UIGestureRecognizer)
    func handleUsernameTapMessageCell(_ message: Message, view: UIView, recognizer: UIGestureRecognizer)
    func handleLongPress(reactionListView: ReactionListView, reactionView: ReactionView)
}

final class ChatMessageCell: UICollectionViewCell {

    static let minimumHeight = CGFloat(55)
    static let identifier = "ChatMessageCell"

    weak var longPressGesture: UILongPressGestureRecognizer?
    weak var usernameTapGesture: UITapGestureRecognizer?
    weak var avatarTapGesture: UITapGestureRecognizer?
    weak var delegate: ChatMessageCellProtocol? {
        didSet {
            labelText.delegate = delegate
        }
    }

    var message: Message! {
        didSet {
            if oldValue != nil && oldValue.identifier == message?.identifier {
                if oldValue.updatedAt?.timeIntervalSince1970 == message.updatedAt?.timeIntervalSince1970 {
                    Log.debug("message is cached")
                    return
                }
            }

            updateMessage()
        }
    }

    @IBOutlet weak var avatarViewContainer: UIView! {
        didSet {
            avatarViewContainer.layer.cornerRadius = 4
            if let avatarView = AvatarView.instantiateFromNib() {
                avatarView.frame = avatarViewContainer.bounds
                avatarViewContainer.addSubview(avatarView)
                self.avatarView = avatarView
            }
        }
    }

    weak var avatarView: AvatarView! {
        didSet {
            avatarView.layer.cornerRadius = 4
            avatarView.layer.masksToBounds = true
        }
    }

    @IBOutlet weak var labelDate: UILabel!
    @IBOutlet weak var labelUsername: UILabel!
    @IBOutlet weak var labelText: RCTextView!

    @IBOutlet weak var mediaViews: UIStackView!
    @IBOutlet weak var mediaViewsHeightConstraint: NSLayoutConstraint!

    @IBOutlet weak var reactionsListView: ReactionListView! {
        didSet {
            reactionsListView.reactionTapRecognized = { view, sender in
                MessageManager.react(self.message, emoji: view.model.emoji, completion: { _ in })

                if self.isAddingReaction(emoji: view.model.emoji) {
                    UserReviewManager.shared.requestReview()
                }
            }

            reactionsListView.reactionLongPressRecognized = { view, sender in
                self.delegate?.handleLongPress(reactionListView: self.reactionsListView, reactionView: view)
            }
        }
    }

    private func isAddingReaction(emoji tappedEmoji: String) -> Bool {
        guard let currentUser = AuthManager.currentUser()?.username else {
            return false
        }

        if message.reactions.first(where: { $0.emoji == tappedEmoji && $0.usernames.contains(currentUser) }) != nil {
            return false
        }

        return true
    }

    @IBOutlet weak var reactionsListViewConstraint: NSLayoutConstraint!

    static func cellMediaHeightFor(message: Message, width: CGFloat, sequential: Bool = true) -> CGFloat {
        let fullWidth = width
        let attributedString = MessageTextCacheManager.shared.message(for: message)

        var total = (CGFloat)(sequential ? 8 : 29) + (message.reactions.count > 0 ? 40 : 0)
        if attributedString?.string ?? "" != "" {
            total += (attributedString?.heightForView(withWidth: fullWidth - 55) ?? 0)
        }

        for url in message.urls {
            guard url.isValid() else { continue }
            total += ChatMessageURLView.defaultHeight
        }

        for attachment in message.attachments {
            let type = attachment.type

            if type == .textAttachment {
                total += ChatMessageTextView.heightFor(collapsed: attachment.collapsed, withText: attachment.text)
            }

            if type == .image {
                total += ChatMessageImageView.heightFor(withText: attachment.descriptionText)
            }

            if type == .video {
                total += ChatMessageVideoView.heightFor(withText: attachment.descriptionText)
            }

            if type == .audio {
                total += ChatMessageAudioView.heightFor(withText: attachment.descriptionText)
            }

            if !attachment.collapsed {
                for field in attachment.fields {
                    total += ChatMessageTextView.heightFor(collapsed: false, withText: field.value)
                }
            }
        }

        return total
    }

    // MARK: Sequential
    @IBOutlet weak var labelUsernameHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var labelDateHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var avatarContainerHeightConstraint: NSLayoutConstraint!

    var sequential: Bool = false {
        didSet {
            avatarContainerHeightConstraint.constant = sequential ? 0 : 35<commit_msg>for in -> forEach<commit_after>//
//  ChatTextCell.swift
//  Rocket.Chat
//
//  Created by <NAME> on 7/25/16.
//  Copyright © 2016 Rocket.Chat. All rights reserved.
//

import UIKit

protocol ChatMessageCellProtocol: ChatMessageURLViewProtocol, ChatMessageVideoViewProtocol, ChatMessageImageViewProtocol, ChatMessageTextViewProtocol {
    func openURL(url: URL)
    func handleLongPressMessageCell(_ message: Message, view: UIView, recognizer: UIGestureRecognizer)
    func handleUsernameTapMessageCell(_ message: Message, view: UIView, recognizer: UIGestureRecognizer)
    func handleLongPress(reactionListView: ReactionListView, reactionView: ReactionView)
}

final class ChatMessageCell: UICollectionViewCell {

    static let minimumHeight = CGFloat(55)
    static let identifier = "ChatMessageCell"

    weak var longPressGesture: UILongPressGestureRecognizer?
    weak var usernameTapGesture: UITapGestureRecognizer?
    weak var avatarTapGesture: UITapGestureRecognizer?
    weak var delegate: ChatMessageCellProtocol? {
        didSet {
            labelText.delegate = delegate
        }
    }

    var message: Message! {
        didSet {
            if oldValue != nil && oldValue.identifier == message?.identifier {
                if oldValue.updatedAt?.timeIntervalSince1970 == message.updatedAt?.timeIntervalSince1970 {
                    Log.debug("message is cached")
                    return
                }
            }

            updateMessage()
        }
    }

    @IBOutlet weak var avatarViewContainer: UIView! {
        didSet {
            avatarViewContainer.layer.cornerRadius = 4
            if let avatarView = AvatarView.instantiateFromNib() {
                avatarView.frame = avatarViewContainer.bounds
                avatarViewContainer.addSubview(avatarView)
                self.avatarView = avatarView
            }
        }
    }

    weak var avatarView: AvatarView! {
        didSet {
            avatarView.layer.cornerRadius = 4
            avatarView.layer.masksToBounds = true
        }
    }

    @IBOutlet weak var labelDate: UILabel!
    @IBOutlet weak var labelUsername: UILabel!
    @IBOutlet weak var labelText: RCTextView!

    @IBOutlet weak var mediaViews: UIStackView!
    @IBOutlet weak var mediaViewsHeightConstraint: NSLayoutConstraint!

    @IBOutlet weak var reactionsListView: ReactionListView! {
        didSet {
            reactionsListView.reactionTapRecognized = { view, sender in
                MessageManager.react(self.message, emoji: view.model.emoji, completion: { _ in })

                if self.isAddingReaction(emoji: view.model.emoji) {
                    UserReviewManager.shared.requestReview()
                }
            }

            reactionsListView.reactionLongPressRecognized = { view, sender in
                self.delegate?.handleLongPress(reactionListView: self.reactionsListView, reactionView: view)
            }
        }
    }

    private func isAddingReaction(emoji tappedEmoji: String) -> Bool {
        guard let currentUser = AuthManager.currentUser()?.username else {
            return false
        }

        if message.reactions.first(where: { $0.emoji == tappedEmoji && $0.usernames.contains(currentUser) }) != nil {
            return false
        }

        return true
    }

    @IBOutlet weak var reactionsListViewConstraint: NSLayoutConstraint!

    static func cellMediaHeightFor(message: Message, width: CGFloat, sequential: Bool = true) -> CGFloat {
        let fullWidth = width
        let attributedString = MessageTextCacheManager.shared.message(for: message)

        var total = (CGFloat)(sequential ? 8 : 29) + (message.reactions.count > 0 ? 40 : 0)
        if attributedString?.string ?? "" != "" {
            total += (attributedString?.heightForView(withWidth: fullWidth - 55) ?? 0)
        }

        for url in message.urls {
            guard url.isValid() else { continue }
            total += ChatMessageURLView.defaultHeight
        }

        for attachment in message.attachments {
            let type = attachment.type

            if type == .textAttachment {
                total += ChatMessageTextView.heightFor(collapsed: attachment.collapsed, withText: attachment.text)
            }

            if type == .image {
                total += ChatMessageImageView.heightFor(withText: attachment.descriptionText)
            }

            if type == .video {
                total += ChatMessageVideoView.heightFor(withText: attachment.descriptionText)
            }

            if type == .audio {
                total += ChatMessageAudioView.heightFor(withText: attachment.descriptionText)
            }

            if !attachment.collapsed {
                attachment.fields.forEach {
                    total += ChatMessageTextView.heightFor(collapsed: false, withText: $0.value)
                }
            }
        }

        return total
    }

    // MARK: Sequential
    @IBOutlet weak var labelUsernameHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var labelDateHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var avatarContainerHeightConstraint: NSLayoutConstraint!

    var sequential: Bool = false {
        didSet {
            avatarContainerHeightConstraint.constant = sequential ? 0 : 35
{'fraction_non_alphanumeric': 0.07865380979391189, 'fraction_numerical': 0.006901115522783135, 'mean_word_length': 2.3049047172758512, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '365592', 'n_tokens_mistral': 2919, 'n_tokens_neox': 2676, 'n_words': 630}