// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.

import UIKit
import Combine
import Photos
import SignalUtilitiesKit
import SessionUIKit
import SessionUtilitiesKit

class SendMediaNavigationController: UINavigationController {
    public override var preferredStatusBarStyle: UIStatusBarStyle {
        return ThemeManager.currentTheme.statusBarStyle
    }
    
    // This is a sensitive constant, if you change it make sure to check
    // on iPhone5, 6, 6+, X, layouts.
    static let bottomButtonsCenterOffset: CGFloat = -50
    
    private let dependencies: Dependencies
    private let threadId: String
    private let threadVariant: SessionThread.Variant
    private var disposables: Set<AnyCancellable> = Set()
    
    // MARK: - Initialization
    
    init(threadId: String, threadVariant: SessionThread.Variant, using dependencies: Dependencies) {
        self.dependencies = dependencies
        self.threadId = threadId
        self.threadVariant = threadVariant
        
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Overrides
    
    override func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self

        let bottomButtonsCenterOffset = SendMediaNavigationController.bottomButtonsCenterOffset

        view.addSubview(batchModeButton)
        batchModeButton.setCompressionResistance(to: .required)
        batchModeButton.centerYAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: bottomButtonsCenterOffset).isActive = true
        batchModeButton.centerXAnchor
            .constraint(equalTo: view.layoutMarginsGuide.trailingAnchor, constant: -20)
            .isActive = true

        view.addSubview(doneButton)
        doneButton.setCompressionResistance(to: .required)
        doneButton.centerYAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: bottomButtonsCenterOffset).isActive = true
        doneButton.pin(.trailing, toMargin: .trailing, of: view)

        view.addSubview(cameraModeButton)
        cameraModeButton.setCompressionResistance(to: .required)
        cameraModeButton.centerYAnchor
            .constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: bottomButtonsCenterOffset)
            .isActive = true
        cameraModeButton.centerXAnchor
            .constraint(equalTo: view.layoutMarginsGuide.leadingAnchor, constant: 20)
            .isActive = true

        view.addSubview(mediaLibraryModeButton)
        mediaLibraryModeButton.setCompressionResistance(to: .required)
        mediaLibraryModeButton.centerYAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: bottomButtonsCenterOffset).isActive = true
        mediaLibraryModeButton.centerXAnchor
            .constraint(equalTo: view.layoutMarginsGuide.leadingAnchor, constant: 20)
            .isActive = true
    }

    // MARK: -

    public weak var sendMediaNavDelegate: SendMediaNavDelegate?

    public class func showingCameraFirst(threadId: String, threadVariant: SessionThread.Variant, using dependencies: Dependencies) -> SendMediaNavigationController {
        let navController = SendMediaNavigationController(threadId: threadId, threadVariant: threadVariant, using: dependencies)
        navController.viewControllers = [navController.captureViewController]

        return navController
    }

    public class func showingMediaLibraryFirst(threadId: String, threadVariant: SessionThread.Variant, using dependencies: Dependencies) -> SendMediaNavigationController {
        let navController = SendMediaNavigationController(threadId: threadId, threadVariant: threadVariant, using: dependencies)
        navController.viewControllers = [navController.mediaLibraryViewController]

        return navController
    }

    var isInBatchSelectMode = false {
        didSet {
            if oldValue != isInBatchSelectMode {
                mediaLibraryViewController.batchSelectModeDidChange()
                
                guard let topViewController = viewControllers.last else { return }
                
                updateButtons(topViewController: topViewController)
            }
        }
    }

    func updateButtons(topViewController: UIViewController) {
        switch topViewController {
            case is AttachmentApprovalViewController:
                batchModeButton.isHidden = true
                doneButton.isHidden = true
                cameraModeButton.isHidden = true
                mediaLibraryModeButton.isHidden = true
                
            case is ImagePickerGridController:
                batchModeButton.isHidden = isInBatchSelectMode
                doneButton.isHidden = !isInBatchSelectMode || (attachmentDraftCollection.count == 0 && mediaLibrarySelections.count == 0)
                cameraModeButton.isHidden = false
                mediaLibraryModeButton.isHidden = true
                
            case is PhotoCaptureViewController:
                batchModeButton.isHidden = isInBatchSelectMode
                doneButton.isHidden = !isInBatchSelectMode || (attachmentDraftCollection.count == 0 && mediaLibrarySelections.count == 0)
                cameraModeButton.isHidden = true
                mediaLibraryModeButton.isHidden = false
                
            default:
                Log.error("[SendMediaNavigationController] unexpected topViewController: \(topViewController)")
        }

        doneButton.updateCount()
    }

    func fadeTo(viewControllers: [UIViewController]) {
        let transition: CATransition = CATransition()
        transition.duration = 0.1
        transition.type = CATransitionType.fade
        view.layer.add(transition, forKey: nil)
        setViewControllers(viewControllers, animated: false)
    }

    // MARK: - Events

    private func didTapBatchModeButton() {
        // There's no way to _disable_ batch mode.
        isInBatchSelectMode = true
    }

    private func didTapCameraModeButton() {
        Permissions.requestCameraPermissionIfNeeded { [weak self] in
            DispatchQueue.main.async {
                self?.fadeTo(viewControllers: ((self?.captureViewController).map { [$0] } ?? []))
            }
        }
    }

    private func didTapMediaLibraryModeButton() {
        Permissions.requestLibraryPermissionIfNeeded(isSavingMedia: false) { [weak self] in
            DispatchQueue.main.async {
                self?.fadeTo(viewControllers: ((self?.mediaLibraryViewController).map { [$0] } ?? []))
            }
        }
    }

    // MARK: Views
    public static let bottomButtonWidth: CGFloat = 44

    private lazy var doneButton: DoneButton = {
        let button = DoneButton()
        button.delegate = self

        return button
    }()

    private lazy var batchModeButton: InputViewButton = {
        let result: InputViewButton = InputViewButton(
            icon: UIImage(named: "media_send_batch_mode_disabled")?
                .withRenderingMode(.alwaysTemplate)
        ) { [weak self] in self?.didTapBatchModeButton() }
        
        return result
    }()

    private lazy var cameraModeButton: InputViewButton = {
        let result: InputViewButton = InputViewButton(
            icon: UIImage(named: "settings-avatar-camera-2")?
                .withRenderingMode(.alwaysTemplate)
        ) { [weak self] in self?.didTapCameraModeButton() }

        return result
    }()

    private lazy var mediaLibraryModeButton: InputViewButton = {
        let result: InputViewButton = InputViewButton(
            icon: UIImage(named: "actionsheet_camera_roll_black")?
                .withRenderingMode(.alwaysTemplate)
        ) { [weak self] in self?.didTapMediaLibraryModeButton() }

        return result
    }()

    // MARK: State

    private lazy var attachmentDraftCollection = AttachmentDraftCollection.empty // Lazy to avoid https://bugs.swift.org/browse/SR-6657

    private var attachments: [SignalAttachment] {
        return attachmentDraftCollection.attachmentDrafts.map { $0.attachment }
    }

    private lazy var mediaLibrarySelections = OrderedDictionary<PHAsset, MediaLibrarySelection>() // Lazy to avoid https://bugs.swift.org/browse/SR-6657

    // MARK: Child VC's

    private lazy var captureViewController: PhotoCaptureViewController = {
        let vc = PhotoCaptureViewController()
        vc.delegate = self

        return vc
    }()

    private lazy var mediaLibraryViewController: ImagePickerGridController = {
        let vc = ImagePickerGridController()
        vc.delegate = self
        vc.collectionView.accessibilityLabel = "Images"

        return vc
    }()

    private func pushApprovalViewController() -> Bool {
        guard let sendMediaNavDelegate = self.sendMediaNavDelegate else {
            Log.error("[SendMediaNavigationController] sendMediaNavDelegate was unexpectedly nil")
            return false
        }

        guard
            let approvalViewController = AttachmentApprovalViewController(
                mode: .sharedNavigation,
                threadId: self.threadId,
                threadVariant: self.threadVariant,
                attachments: self.attachments,
                using: dependencies
            )
        else { return false }
        
        approvalViewController.approvalDelegate = self
        approvalViewController.messageText = sendMediaNavDelegate.sendMediaNavInitialMessageText(self)

        pushViewController(approvalViewController, animated: true)
        return true
    }

    private func didRequestExit() {
        self.sendMediaNavDelegate?.sendMediaNavDidCancel(self)
    }
}

extension SendMediaNavigationController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        switch viewController {
            case is PhotoCaptureViewController:
                if attachmentDraftCollection.count == 1 && !isInBatchSelectMode {
                    // User is navigating "back" to the previous view, indicating
                    // they want to discard the previously captured item
                    discardDraft()
                }
            
            case is ImagePickerGridController:
                if attachmentDraftCollection.count == 1 && !isInBatchSelectMode {
                    isInBatchSelectMode = true
                    self.mediaLibraryViewController.batchSelectModeDidChange()
                }
            
            default:
                break
        }

        self.updateButtons(topViewController: viewController)
    }

    // In case back navigation was canceled, we re-apply whatever is showing.
    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        self.updateButtons(topViewController: viewController)
    }
}

extension SendMediaNavigationController: PhotoCaptureViewControllerDelegate {
    func photoCaptureViewController(_ photoCaptureViewController: PhotoCaptureViewController, didFinishProcessingAttachment attachment: SignalAttachment) {
        attachmentDraftCollection.append(.camera(attachment: attachment))
        if isInBatchSelectMode {
            updateButtons(topViewController: photoCaptureViewController)
        }
        else {
            // Try push the approval controller, otherwise show an error
            if !pushApprovalViewController() {
                let modal: ConfirmationModal = ConfirmationModal(
                    info: ConfirmationModal.Info(
                        title: "attachmentsErrorMediaSelection".localized(),
                        cancelTitle: "okay".localized(),
                        cancelStyle: .alert_text
                    )
                )
                present(modal, animated: true)
            }
        }
    }

    func photoCaptureViewControllerDidCancel(_ photoCaptureViewController: PhotoCaptureViewController) {
        didRequestExit()
    }

    func discardDraft() {
        assert(attachmentDraftCollection.attachmentDrafts.count <= 1)
        if let lastAttachmentDraft = attachmentDraftCollection.attachmentDrafts.last {
            attachmentDraftCollection.remove(attachment: lastAttachmentDraft.attachment)
        }
        assert(attachmentDraftCollection.attachmentDrafts.count == 0)
    }
}

extension SendMediaNavigationController: ImagePickerGridControllerDelegate {

    func imagePickerDidCompleteSelection(_ imagePicker: ImagePickerGridController) {
        showApprovalAfterProcessingAnyMediaLibrarySelections()
    }

    func imagePickerDidCancel(_ imagePicker: ImagePickerGridController) {
        didRequestExit()
    }

    func showApprovalAfterProcessingAnyMediaLibrarySelections() {
        let mediaLibrarySelections: [MediaLibrarySelection] = self.mediaLibrarySelections.orderedValues

        let backgroundBlock: (ModalActivityIndicatorViewController) -> Void = { [weak self] modal in
            guard let strongSelf = self else { return }
            
            Publishers
                .MergeMany(mediaLibrarySelections.map { $0.publisher })
                .collect()
                .sink(
                    receiveCompletion: { result in
                        switch result {
                            case .finished: break
                            case .failure(let error):
                                Log.error("[SendMediaNavigationController] Failed to prepare attachments. error: \(error)")
                                modal.dismiss { [weak self] in
                                    let modal: ConfirmationModal = ConfirmationModal(
                                        targetView: self?.view,
                                        info: ConfirmationModal.Info(
                                            title: "attachmentsErrorMediaSelection".localized(),
                                            cancelTitle: "okay".localized(),
                                            cancelStyle: .alert_text
                                        )
                                    )
                                    self?.present(modal, animated: true)
                                }
                        }
                    },
                    receiveValue: { attachments in
                        Log.debug("[SendMediaNavigationController] Built all attachments")
                        modal.dismiss {
                            self?.attachmentDraftCollection.selectedFromPicker(attachments: attachments)
                            
                            guard self?.pushApprovalViewController() == true else {
                                let modal: ConfirmationModal = ConfirmationModal(
                                    info: ConfirmationModal.Info(
                                        title: "attachmentsErrorMediaSelection".localized(),
                                        cancelTitle: "okay".localized(),
                                        cancelStyle: .alert_text
                                    )
                                )
                                self?.present(modal, animated: true)
                                return
                            }
                        }
                    }
                )
                .store(in: &strongSelf.disposables)
        }

        ModalActivityIndicatorViewController.present(
            fromViewController: self,
            canCancel: false,
            onAppear: backgroundBlock
        )
    }

    func imagePicker(_ imagePicker: ImagePickerGridController, isAssetSelected asset: PHAsset) -> Bool {
        return mediaLibrarySelections.hasValue(forKey: asset)
    }

    func imagePicker(_ imagePicker: ImagePickerGridController, didSelectAsset asset: PHAsset, attachmentPublisher: AnyPublisher<SignalAttachment, Error>) {
        guard !mediaLibrarySelections.hasValue(forKey: asset) else { return }

        let libraryMedia = MediaLibrarySelection(
            asset: asset,
            signalAttachmentPublisher: attachmentPublisher
        )
        mediaLibrarySelections.append(key: asset, value: libraryMedia)
        updateButtons(topViewController: imagePicker)
    }

    func imagePicker(_ imagePicker: ImagePickerGridController, didDeselectAsset asset: PHAsset) {
        guard mediaLibrarySelections.hasValue(forKey: asset) else { return }
        
        mediaLibrarySelections.remove(key: asset)
        updateButtons(topViewController: imagePicker)
    }

    func imagePickerCanSelectAdditionalItems(_ imagePicker: ImagePickerGridController) -> Bool {
        return attachmentDraftCollection.count <= SignalAttachment.maxAttachmentsAllowed
    }
    
    func imagePicker(_ imagePicker: ImagePickerGridController, failedToRetrieveAssetAt index: Int, forCount count: Int) {
        let modal: ConfirmationModal = ConfirmationModal(
            targetView: self.view,
            info: ConfirmationModal.Info(
                title: "attachmentsErrorMediaSelection".localized(),
                cancelTitle: "okay".localized(),
                cancelStyle: .alert_text
            )
        )
        self.present(modal, animated: true)
    }
}

extension SendMediaNavigationController: AttachmentApprovalViewControllerDelegate {
    func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didChangeMessageText newMessageText: String?) {
        sendMediaNavDelegate?.sendMediaNav(self, didChangeMessageText: newMessageText)
    }

    func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didRemoveAttachment attachment: SignalAttachment) {
        guard let removedDraft = attachmentDraftCollection.attachmentDrafts.first(where: { $0.attachment == attachment}) else {
            Log.error("[SendMediaNavigationController] removedDraft was unexpectedly nil")
            return
        }

        switch removedDraft.source {
        case .picker(attachment: let pickerAttachment):
            mediaLibrarySelections.remove(key: pickerAttachment.asset)
        case .camera(attachment: _):
            break
        }

        attachmentDraftCollection.remove(attachment: attachment)
    }

    func attachmentApproval(
        _ attachmentApproval: AttachmentApprovalViewController,
        didApproveAttachments attachments: [SignalAttachment],
        forThreadId threadId: String,
        threadVariant: SessionThread.Variant,
        messageText: String?
    ) {
        sendMediaNavDelegate?.sendMediaNav(
            self,
            didApproveAttachments: attachments,
            forThreadId: threadId,
            threadVariant: threadVariant,
            messageText: messageText
        )
    }

    func attachmentApprovalDidCancel(_ attachmentApproval: AttachmentApprovalViewController) {
        sendMediaNavDelegate?.sendMediaNavDidCancel(self)
    }

    func attachmentApprovalDidTapAddMore(_ attachmentApproval: AttachmentApprovalViewController) {
        // Current design dicates we'll go "back" to the single thing before us.
        assert(viewControllers.count == 2)

        // regardless of which VC we're going "back" to, we're in "batch" mode at this point.
        isInBatchSelectMode = true
        mediaLibraryViewController.batchSelectModeDidChange()

        popViewController(animated: true)
    }
}

private enum AttachmentDraft {
    case camera(attachment: SignalAttachment)
    case picker(attachment: MediaLibraryAttachment)
}

private extension AttachmentDraft {
    var attachment: SignalAttachment {
        switch self {
        case .camera(let cameraAttachment):
            return cameraAttachment
        case .picker(let pickerAttachment):
            return pickerAttachment.signalAttachment
        }
    }

    var source: AttachmentDraft {
        return self
    }
}

private final class AttachmentDraftCollection {
    lazy var attachmentDrafts = [AttachmentDraft]() // Lazy to avoid https://bugs.swift.org/browse/SR-6657

    static var empty: AttachmentDraftCollection {
        return AttachmentDraftCollection(attachmentDrafts: [])
    }
    
    init(attachmentDrafts: [AttachmentDraft]) {
        self.attachmentDrafts = attachmentDrafts
    }
    
    // MARK: -

    var count: Int {
        return attachmentDrafts.count
    }

    var pickerAttachments: [MediaLibraryAttachment] {
        return attachmentDrafts.compactMap { attachmentDraft in
            switch attachmentDraft.source {
            case .picker(let pickerAttachment):
                return pickerAttachment
            case .camera:
                return nil
            }
        }
    }

    var cameraAttachments: [SignalAttachment] {
        return attachmentDrafts.compactMap { attachmentDraft in
            switch attachmentDraft.source {
            case .picker:
                return nil
            case .camera(let cameraAttachment):
                return cameraAttachment
            }
        }
    }

    func append(_ element: AttachmentDraft) {
        attachmentDrafts.append(element)
    }

    func remove(attachment: SignalAttachment) {
        attachmentDrafts.removeAll { $0.attachment == attachment }
    }

    func selectedFromPicker(attachments: [MediaLibraryAttachment]) {
        let pickedAttachments: Set<MediaLibraryAttachment> = Set(attachments)
        let oldPickerAttachments: Set<MediaLibraryAttachment> = Set(self.pickerAttachments)

        for removedAttachment in oldPickerAttachments.subtracting(pickedAttachments) {
            remove(attachment: removedAttachment.signalAttachment)
        }

        // enumerate over new attachments to maintain order from picker
        for attachment in attachments {
            guard !oldPickerAttachments.contains(attachment) else {
                continue
            }
            append(.picker(attachment: attachment))
        }
    }
}

private struct MediaLibrarySelection: Hashable, Equatable {
    let asset: PHAsset
    let signalAttachmentPublisher: AnyPublisher<SignalAttachment, Error>

    func hash(into hasher: inout Hasher) {
        asset.hash(into: &hasher)
    }

    var publisher: AnyPublisher<MediaLibraryAttachment, Error> {
        let asset = self.asset
        return signalAttachmentPublisher
            .map { MediaLibraryAttachment(asset: asset, signalAttachment: $0) }
            .eraseToAnyPublisher()
    }

    static func ==(lhs: MediaLibrarySelection, rhs: MediaLibrarySelection) -> Bool {
        return lhs.asset == rhs.asset
    }
}

private struct MediaLibraryAttachment: Hashable, Equatable {
    let asset: PHAsset
    let signalAttachment: SignalAttachment

    func hash(into hasher: inout Hasher) {
        asset.hash(into: &hasher)
    }

    public static func == (lhs: MediaLibraryAttachment, rhs: MediaLibraryAttachment) -> Bool {
        return lhs.asset == rhs.asset
    }
}

extension SendMediaNavigationController: DoneButtonDelegate {
    var doneButtonCount: Int {
        return attachmentDraftCollection.count - attachmentDraftCollection.pickerAttachments.count + mediaLibrarySelections.count
    }

    fileprivate func doneButtonWasTapped(_ doneButton: DoneButton) {
        assert(attachmentDraftCollection.count > 0 || mediaLibrarySelections.count > 0)
        showApprovalAfterProcessingAnyMediaLibrarySelections()
    }
}

private protocol DoneButtonDelegate: AnyObject {
    func doneButtonWasTapped(_ doneButton: DoneButton)
    var doneButtonCount: Int { get }
}

private class DoneButton: UIView {
    weak var delegate: DoneButtonDelegate?
    let numberFormatter: NumberFormatter = NumberFormatter()
    
    private var didTouchDownInside: Bool = false
    
    // MARK: - UI
    
    private let container: UIView = {
        let result: UIView = UIView()
        result.themeBackgroundColor = .inputButton_background
        result.layer.cornerRadius = 20
        
        return result
    }()
    
    private lazy var badge: CircleView = {
        let result: CircleView = CircleView()
        result.themeBackgroundColor = .primary

        return result
    }()

    private lazy var badgeLabel: UILabel = {
        let result: UILabel = UILabel()
        result.font = UIFont.monospacedDigitSystemFont(
            ofSize: UIFont.preferredFont(forTextStyle: .subheadline).pointSize,
            weight: .regular
        )
        result.themeTextColor = .black   // Will render on the primary color so should always be black
        result.textAlignment = .center
        
        return result
    }()

    private lazy var chevron: UIView = {
        let image: UIImage = {
            guard Singleton.hasAppContext && Singleton.appContext.isRTL else { return #imageLiteral(resourceName: "small_chevron_right") }
            
            return #imageLiteral(resourceName: "small_chevron_left")
        }()
        let result: UIImageView = UIImageView(image: image.withRenderingMode(.alwaysTemplate))
        result.contentMode = .scaleAspectFit
        result.themeTintColor = .textPrimary
        result.set(.width, to: 10)
        result.set(.height, to: 18)

        return result
    }()
    
    // MARK: - Lifecycle

    init() {
        super.init(frame: .zero)

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(tapGesture:)))
        addGestureRecognizer(tapGesture)
        
        addSubview(container)
        container.pin(to: self)
        
        badge.addSubview(badgeLabel)
        badgeLabel.pin(to: badge, withInset: 4)
        
        // Constrain to be a pill that is at least a circle, and maybe wider.
        badgeLabel.set(.width, greaterThanOrEqualTo: .height, of: badgeLabel, multiplier: 1)
        badgeLabel.set(.width, to: .height, of: badgeLabel, multiplier: 1).setting(priority: .defaultLow)

        let stackView = UIStackView(arrangedSubviews: [badge, chevron])
        stackView.axis = .horizontal
        stackView.alignment = .center
        stackView.spacing = 9

        addSubview(stackView)
        stackView.pin(.top, to: .top, of: self, withInset: 7)
        stackView.pin(.leading, to: .leading, of: self, withInset: 8)
        stackView.pin(.trailing, to: .trailing, of: self, withInset: -8)
        stackView.pin(.bottom, to: .bottom, of: self, withInset: -7)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Functions
    
    func updateCount() {
        guard let delegate = delegate else { return }

        badge.themeBackgroundColor = (delegate.doneButtonCount > 0 ? .primary : .disabled)
        badgeLabel.text = numberFormatter.string(for: delegate.doneButtonCount)
    }
    
    // MARK: - Interaction
    
    private func animate(
        to scale: CGFloat,
        themeBackgroundColor: ThemeValue,
        themeBadgeBackgroundColor: ThemeValue,
        themeTintColor: ThemeValue
    ) {
        UIView.animate(withDuration: 0.25) {
            self.container.transform = CGAffineTransform.identity.scaledBy(x: scale, y: scale)
            self.badgeLabel.themeTextColor = themeTintColor
            self.badge.themeBackgroundColor = themeBadgeBackgroundColor
            self.container.themeBackgroundColor = themeBackgroundColor
        }
    }
    
    private func expand() {
        animate(
            to: (InputViewButton.expandedSize / InputViewButton.size),
            themeBackgroundColor: .primary,
            themeBadgeBackgroundColor: .inputButton_background,
            themeTintColor: .textPrimary
        )
    }
    
    private func collapse() {
        animate(
            to: 1,
            themeBackgroundColor: .inputButton_background,
            themeBadgeBackgroundColor: .primary,
            themeTintColor: .black
        )
    }

    @objc func didTap(tapGesture: UITapGestureRecognizer) {
        guard (delegate?.doneButtonCount ?? 0) > 0 else { return }
        
        delegate?.doneButtonWasTapped(self)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard
            (delegate?.doneButtonCount ?? 0) > 0,
            isUserInteractionEnabled,
            let location: CGPoint = touches.first?.location(in: self),
            bounds.contains(location)
        else { return }
        
        didTouchDownInside = true
        expand()
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard
            isUserInteractionEnabled,
            let location: CGPoint = touches.first?.location(in: self),
            bounds.contains(location),
            didTouchDownInside
        else {
            if didTouchDownInside {
                collapse()
            }
            return
        }
        
        expand()
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if didTouchDownInside {
            collapse()
        }
        
        didTouchDownInside = false
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        if didTouchDownInside {
            collapse()
        }
        
        didTouchDownInside = false
    }
}

// MARK: - SendMediaNavDelegate

protocol SendMediaNavDelegate: AnyObject {
    func sendMediaNavDidCancel(_ sendMediaNavigationController: SendMediaNavigationController?)
    func sendMediaNav(_ sendMediaNavigationController: SendMediaNavigationController, didApproveAttachments attachments: [SignalAttachment], forThreadId threadId: String, threadVariant: SessionThread.Variant, messageText: String?)

    func sendMediaNavInitialMessageText(_ sendMediaNavigationController: SendMediaNavigationController) -> String?
    func sendMediaNav(_ sendMediaNavigationController: SendMediaNavigationController, didChangeMessageText newMessageText: String?)
}
