import Foundation
import UIKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import TelegramStringFormatting
import TelegramNotices
import PresentationDataUtils
import TextFormat
import UrlHandling
import AccountContext
import ChatPresentationInterfaceState
import LegacyComponents
import LegacyUI
import AttachmentUI
import MediaPickerUI
import LegacyCamera
import LegacyMediaPickerUI
import LocationUI
import WebSearchUI
import WebUI
import UndoUI
import ICloudResources
import PhoneNumberFormat
import ChatEntityKeyboardInputNode
import PremiumUI
import PremiumGiftAttachmentScreen
import TelegramCallsUI
import AutomaticBusinessMessageSetupScreen
import MediaEditorScreen
import CameraScreen
import ShareController

extension ChatControllerImpl {
    enum AttachMenuSubject {
        case `default`
        case edit(mediaOptions: MessageMediaEditingOptions, mediaReference: AnyMediaReference)
        case bot(id: PeerId, payload: String?, justInstalled: Bool)
        case gift
    }
    
    func presentAttachmentMenu(subject: AttachMenuSubject) {
        guard self.audioRecorderValue == nil && self.videoRecorderValue == nil else {
            return
        }
        
        let context = self.context
        let inputIsActive = self.presentationInterfaceState.inputMode == .text
        
        self.chatDisplayNode.dismissInput()
        
        let canByPassRestrictions = canBypassRestrictions(chatPresentationInterfaceState: self.presentationInterfaceState)
        
        var banSendText: (Int32, Bool)?
        var bannedSendPhotos: (Int32, Bool)?
        var bannedSendVideos: (Int32, Bool)?
        var bannedSendFiles: (Int32, Bool)?
        
        var canSendPolls = true
        if let peer = self.presentationInterfaceState.renderedPeer?.peer {
            if let peer = peer as? TelegramUser, peer.botInfo == nil {
                canSendPolls = false
            } else if peer is TelegramSecretChat {
                canSendPolls = false
            } else if let channel = peer as? TelegramChannel {
                if let value = channel.hasBannedPermission(.banSendPhotos, ignoreDefault: canByPassRestrictions) {
                    bannedSendPhotos = value
                }
                if let value = channel.hasBannedPermission(.banSendVideos, ignoreDefault: canByPassRestrictions) {
                    bannedSendVideos = value
                }
                if let value = channel.hasBannedPermission(.banSendFiles, ignoreDefault: canByPassRestrictions) {
                    bannedSendFiles = value
                }
                if let value = channel.hasBannedPermission(.banSendText, ignoreDefault: canByPassRestrictions) {
                    banSendText = value
                }
                if channel.hasBannedPermission(.banSendPolls, ignoreDefault: canByPassRestrictions) != nil {
                    canSendPolls = false
                }
            } else if let group = peer as? TelegramGroup {
                if group.hasBannedPermission(.banSendPhotos) {
                    bannedSendPhotos = (Int32.max, false)
                }
                if group.hasBannedPermission(.banSendVideos) {
                    bannedSendVideos = (Int32.max, false)
                }
                if group.hasBannedPermission(.banSendFiles) {
                    bannedSendFiles = (Int32.max, false)
                }
                if group.hasBannedPermission(.banSendText) {
                    banSendText = (Int32.max, false)
                }
                if group.hasBannedPermission(.banSendPolls) {
                    canSendPolls = false
                }
            }
        } else {
            canSendPolls = false
        }
        
        var availableButtons: [AttachmentButtonType] = [.gallery, .file]
        if banSendText == nil {
            availableButtons.append(.location)
            availableButtons.append(.contact)
        }
        if canSendPolls {
            availableButtons.insert(.poll, at: max(0, availableButtons.count - 1))
        }
        
        let presentationData = self.presentationData
        
        var isScheduledMessages = false
        if case .scheduledMessages = self.presentationInterfaceState.subject {
            isScheduledMessages = true
        }
        
        var peerType: AttachMenuBots.Bot.PeerFlags = []
        if let peer = self.presentationInterfaceState.renderedPeer?.peer {
            if let user = peer as? TelegramUser {
                if let _ = user.botInfo {
                    peerType.insert(.bot)
                } else {
                    peerType.insert(.user)
                }
            } else if let _ = peer as? TelegramGroup {
                peerType = .group
            } else if let channel = peer as? TelegramChannel {
                if case .broadcast = channel.info {
                    peerType = .channel
                } else {
                    peerType = .group
                }
            }
        }
        
        let buttons: Signal<([AttachmentButtonType], [AttachmentButtonType], AttachmentButtonType?), NoError>
        if let peer = self.presentationInterfaceState.renderedPeer?.peer, !isScheduledMessages, !peer.isDeleted {
            buttons = combineLatest(
                self.context.engine.messages.attachMenuBots(),
                self.context.engine.accountData.shortcutMessageList(onlyRemote: true) |> take(1)
            )
            |> map { attachMenuBots, shortcutMessageList in
                var buttons = availableButtons
                var allButtons = availableButtons
                var initialButton: AttachmentButtonType?
                switch subject {
                case .default:
                    initialButton = .gallery
                case .edit:
                    break
                case .gift:
                    initialButton = .gift
                default:
                    break
                }
                
                for bot in attachMenuBots.reversed() {
                    var peerType = peerType
                    if bot.peer.id == peer.id {
                        peerType.insert(.sameBot)
                        peerType.remove(.bot)
                    }
                    let button: AttachmentButtonType = .app(bot)
                    if !bot.peerTypes.intersection(peerType).isEmpty {
                        buttons.insert(button, at: 1)
                        
                        if case let .bot(botId, _, _) = subject {
                            if initialButton == nil && bot.peer.id == botId {
                                initialButton = button
                            }
                        }
                    }
                    allButtons.insert(button, at: 1)
                }
                
                if let user = peer as? TelegramUser, user.botInfo == nil {
                    if let index = buttons.firstIndex(where: { $0 == .location }) {
                        buttons.insert(.quickReply, at: index + 1)
                    } else {
                        buttons.append(.quickReply)
                    }
                    if let index = allButtons.firstIndex(where: { $0 == .location }) {
                        allButtons.insert(.quickReply, at: index + 1)
                    } else {
                        allButtons.append(.quickReply)
                    }
                }
                
                return (buttons, allButtons, initialButton)
            }
        } else {
            buttons = .single((availableButtons, availableButtons, .gallery))
        }
                    
        let dataSettings = self.context.sharedContext.accountManager.transaction { transaction -> GeneratedMediaStoreSettings in
            let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings)?.get(GeneratedMediaStoreSettings.self)
            return entry ?? GeneratedMediaStoreSettings.defaultSettings
        }
        
        let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
        let premiumGiftOptions: [CachedPremiumGiftOption]
        
        var showPremiumGift = false
        if !premiumConfiguration.isPremiumDisabled {
            if premiumConfiguration.showPremiumGiftInAttachMenu || self.presentationInterfaceState.hasBirthdayToday {
                showPremiumGift = true
            }
        }
        
        if let peer = self.presentationInterfaceState.renderedPeer?.peer, showPremiumGift, let user = peer as? TelegramUser, !user.isPremium && !user.isDeleted && user.botInfo == nil && !user.flags.contains(.isSupport) {
            premiumGiftOptions = self.presentationInterfaceState.premiumGiftOptions
        } else {
            premiumGiftOptions = []
        }
        
        let _ = combineLatest(queue: Queue.mainQueue(), buttons, dataSettings).startStandalone(next: { [weak self] buttonsAndInitialButton, dataSettings in
            guard let strongSelf = self else {
                return
            }
            
            var (buttons, allButtons, initialButton) = buttonsAndInitialButton
            if !premiumGiftOptions.isEmpty {
                buttons.insert(.gift, at: 1)
            }
        
            guard let initialButton = initialButton else {
                if case let .bot(botId, botPayload, botJustInstalled) = subject {
                    if let button = allButtons.first(where: { button in
                        if case let .app(bot) = button, bot.peer.id == botId {
                            return true
                        } else {
                            return false
                        }
                    }), case let .app(bot) = button {
                        let content: UndoOverlayContent
                        if botJustInstalled {
                            if bot.flags.contains(.showInSettings) {
                                content = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsSettingsAdded(bot.shortName).string, timeout: 5.0, customUndoText: nil)
                            } else {
                                content = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsAdded(bot.shortName).string, timeout: 5.0, customUndoText: nil)
                            }
                        } else {
                            content = .info(title: nil, text: strongSelf.presentationData.strings.WebApp_AddToAttachmentAlreadyAddedError, timeout: nil, customUndoText: nil)
                        }
                        strongSelf.present(UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: false, position: .top, action: { _ in return false }), in: .current)
                    } else {
                        let _ = (context.engine.messages.getAttachMenuBot(botId: botId)
                        |> deliverOnMainQueue).startStandalone(next: { bot in
                            let controller = webAppTermsAlertController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, bot: bot, completion: { allowWrite in
                                let _ = (context.engine.messages.addBotToAttachMenu(botId: botId, allowWrite: allowWrite)
                                |> deliverOnMainQueue).startStandalone(error: { _ in
                                    
                                }, completed: {
                                    strongSelf.presentAttachmentBot(botId: botId, payload: botPayload, justInstalled: true)
                                })
                            })
                            strongSelf.present(controller, in: .window(.root))
                        }, error: { _ in
                            strongSelf.present(textAlertController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
                        })
                    }
                }
                return
            }
            
            let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
            
            let currentMediaController = Atomic<MediaPickerScreen?>(value: nil)
            let currentFilesController = Atomic<AttachmentFileControllerImpl?>(value: nil)
            let currentLocationController = Atomic<LocationPickerController?>(value: nil)
            
            strongSelf.canReadHistory.set(false)
            
            let attachmentController = AttachmentController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: strongSelf.chatLocation, isScheduledMessages: isScheduledMessages, buttons: buttons, initialButton: initialButton, makeEntityInputView: { [weak self] in
                guard let strongSelf = self else {
                    return nil
                }
                return EntityInputView(context: strongSelf.context, isDark: false, areCustomEmojiEnabled: strongSelf.presentationInterfaceState.customEmojiAvailable)
            })
            attachmentController.shouldMinimizeOnSwipe = { [weak attachmentController] button in
                if case .app = button {
                    attachmentController?.convertToStandalone()
                    return true
                }
                return false
            }
            attachmentController.didDismiss = { [weak self] in
                self?.attachmentController = nil
                self?.canReadHistory.set(true)
            }
            attachmentController.getSourceRect = { [weak self] in
                if let strongSelf = self {
                    return strongSelf.chatDisplayNode.frameForAttachmentButton()?.offsetBy(dx: strongSelf.chatDisplayNode.supernode?.frame.minX ?? 0.0, dy: 0.0)
                } else {
                    return nil
                }
            }
            attachmentController.requestController = { [weak self, weak attachmentController] type, completion in
                guard let strongSelf = self else {
                    return
                }
                switch type {
                case .gallery:
                    strongSelf.controllerNavigationDisposable.set(nil)
                    let existingController = currentMediaController.with { $0 }
                    if let controller = existingController {
                        completion(controller, controller.mediaPickerContext)
                        controller.prepareForReuse()
                        return
                    }
                    strongSelf.presentMediaPicker(saveEditedPhotos: dataSettings.storeEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, present: { controller, mediaPickerContext in
                        let _ = currentMediaController.swap(controller)
                        if !inputText.string.isEmpty {
                            mediaPickerContext?.setCaption(inputText)
                        }
                        completion(controller, mediaPickerContext)
                    }, updateMediaPickerContext: { [weak attachmentController] mediaPickerContext in
                        attachmentController?.mediaPickerContext = mediaPickerContext
                    }, completion: { [weak self] signals, silentPosting, scheduleTime, parameters, getAnimatedTransitionSource, completion in
                        if !inputText.string.isEmpty {
                            self?.clearInputText()
                        }
                        self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
                    })
                case .file:
                    strongSelf.controllerNavigationDisposable.set(nil)
                    let existingController = currentFilesController.with { $0 }
                    if let controller = existingController {
                        completion(controller, controller.mediaPickerContext)
                        controller.prepareForReuse()
                        return
                    }
                    let controller = strongSelf.context.sharedContext.makeAttachmentFileController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bannedSendMedia: bannedSendFiles, presentGallery: { [weak self, weak attachmentController] in
                        attachmentController?.dismiss(animated: true)
                        self?.presentFileGallery()
                    }, presentFiles: { [weak self, weak attachmentController] in
                        attachmentController?.dismiss(animated: true)
                        self?.presentICloudFileGallery()
                    }, send: { [weak self] mediaReference in
                        guard let strongSelf = self else {
                            return
                        }
                        
                        let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, threadId: strongSelf.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                        strongSelf.sendMessages([message], media: true)
                    })
                    if let controller = controller as? AttachmentFileControllerImpl {
                        let _ = currentFilesController.swap(controller)
                        completion(controller, controller.mediaPickerContext)
                    }
                case .location:
                    strongSelf.controllerNavigationDisposable.set(nil)
                    let existingController = currentLocationController.with { $0 }
                    if let controller = existingController {
                        completion(controller, controller.mediaPickerContext)
                        controller.prepareForReuse()
                        return
                    }
                    let selfPeerId: PeerId
                    if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                        if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
                            selfPeerId = peer.id
                        } else if let peer = peer as? TelegramChannel, case .group = peer.info, peer.hasPermission(.canBeAnonymous) {
                            selfPeerId = peer.id
                        } else {
                            selfPeerId = strongSelf.context.account.peerId
                        }
                    } else {
                        selfPeerId = strongSelf.context.account.peerId
                    }
                    let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: selfPeerId))
                    |> deliverOnMainQueue).startStandalone(next: { selfPeer in
                        guard let strongSelf = self, let selfPeer = selfPeer else {
                            return
                        }
                        let hasLiveLocation: Bool
                        if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                            hasLiveLocation = peer.id.namespace != Namespaces.Peer.SecretChat && peer.id != strongSelf.context.account.peerId && strongSelf.presentationInterfaceState.subject != .scheduledMessages
                        } else {
                            hasLiveLocation = false
                        }
                        let sharePeer = (strongSelf.presentationInterfaceState.renderedPeer?.peer).flatMap(EnginePeer.init)
                        let controller = LocationPickerController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, mode: .share(peer: sharePeer, selfPeer: selfPeer, hasLiveLocation: hasLiveLocation), completion: { location, _, _, _, _ in
                            guard let strongSelf = self else {
                                return
                            }
                            let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                            let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: location), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                            strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                                if let strongSelf = self {
                                    strongSelf.chatDisplayNode.collapseInput()
                                    
                                    strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                        $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                    })
                                }
                            }, nil)
                            strongSelf.sendMessages([message])
                        })
                        completion(controller, controller.mediaPickerContext)
                        
                        let _ = currentLocationController.swap(controller)
                    })
                case .contact:
                    let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true, requirePhoneNumbers: true))
                    contactsController.presentScheduleTimePicker = { [weak self] completion in
                        if let strongSelf = self {
                            strongSelf.presentScheduleTimePicker(completion: completion)
                        }
                    }
                    contactsController.navigationPresentation = .modal
                    completion(contactsController, contactsController.mediaPickerContext)
                    strongSelf.controllerNavigationDisposable.set((contactsController.result
                    |> deliverOnMainQueue).startStrict(next: { [weak self] peers in
                        if let strongSelf = self, let (peers, _, silent, scheduleTime, text, parameters) = peers {
                            var textEnqueueMessage: EnqueueMessage?
                            if let text = text, text.length > 0 {
                                var attributes: [MessageAttribute] = []
                                let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
                                if !entities.isEmpty {
                                    attributes.append(TextEntitiesMessageAttribute(entities: entities))
                                }
                                textEnqueueMessage = .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: nil, threadId: strongSelf.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                            }
                            if peers.count > 1 {
                                var enqueueMessages: [EnqueueMessage] = []
                                if let textEnqueueMessage = textEnqueueMessage {
                                    enqueueMessages.append(textEnqueueMessage)
                                }
                                for peer in peers {
                                    var media: TelegramMediaContact?
                                    switch peer {
                                    case let .peer(contact, _, _):
                                        guard let contact = contact as? TelegramUser, let phoneNumber = contact.phone else {
                                            continue
                                        }
                                        let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName ?? "", lastName: contact.lastName ?? "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
                                        
                                        let phone = contactData.basicData.phoneNumbers[0].value
                                        media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: contact.id, vCardData: nil)
                                    case let .deviceContact(_, basicData):
                                        guard !basicData.phoneNumbers.isEmpty else {
                                            continue
                                        }
                                        let contactData = DeviceContactExtendedData(basicData: basicData, middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
                                        
                                        let phone = contactData.basicData.phoneNumbers[0].value
                                        media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: nil)
                                    }
                                    
                                    if let media = media {
                                        let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                                        strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                                            if let strongSelf = self {
                                                strongSelf.chatDisplayNode.collapseInput()
                                                
                                                strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                                    $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                                })
                                            }
                                        }, nil)
                                        let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                                        enqueueMessages.append(message)
                                    }
                                }
                                if !enqueueMessages.isEmpty {
                                    enqueueMessages[enqueueMessages.count - 1] = enqueueMessages[enqueueMessages.count - 1].withUpdatedAttributes { attributes in
                                        var attributes = attributes
                                        if let parameters {
                                            if let effect = parameters.effect {
                                                attributes.append(EffectMessageAttribute(id: effect.id))
                                            }
                                        }
                                        return attributes
                                    }
                                }
                                strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
                            } else if let peer = peers.first {
                                let dataSignal: Signal<(Peer?,  DeviceContactExtendedData?), NoError>
                                switch peer {
                                case let .peer(contact, _, _):
                                    guard let contact = contact as? TelegramUser, let phoneNumber = contact.phone else {
                                        return
                                    }
                                    let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName ?? "", lastName: contact.lastName ?? "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
                                    let context = strongSelf.context
                                    dataSignal = (strongSelf.context.sharedContext.contactDataManager?.basicData() ?? .single([:]))
                                    |> take(1)
                                    |> mapToSignal { basicData -> Signal<(Peer?,  DeviceContactExtendedData?), NoError> in
                                        var stableId: String?
                                        let queryPhoneNumber = formatPhoneNumber(context: context, number: phoneNumber)
                                        outer: for (id, data) in basicData {
                                            for phoneNumber in data.phoneNumbers {
                                                if formatPhoneNumber(context: context, number: phoneNumber.value) == queryPhoneNumber {
                                                    stableId = id
                                                    break outer
                                                }
                                            }
                                        }
                                        
                                        if let stableId = stableId {
                                            return (context.sharedContext.contactDataManager?.extendedData(stableId: stableId) ?? .single(nil))
                                            |> take(1)
                                            |> map { extendedData -> (Peer?,  DeviceContactExtendedData?) in
                                                return (contact, extendedData)
                                            }
                                        } else {
                                            return .single((contact, contactData))
                                        }
                                    }
                                case let .deviceContact(id, _):
                                    dataSignal = (strongSelf.context.sharedContext.contactDataManager?.extendedData(stableId: id) ?? .single(nil))
                                    |> take(1)
                                    |> map { extendedData -> (Peer?,  DeviceContactExtendedData?) in
                                        return (nil, extendedData)
                                    }
                                }
                                strongSelf.controllerNavigationDisposable.set((dataSignal
                                |> deliverOnMainQueue).startStrict(next: { peerAndContactData in
                                    if let strongSelf = self, let contactData = peerAndContactData.1, contactData.basicData.phoneNumbers.count != 0 {
                                        if contactData.isPrimitive {
                                            let phone = contactData.basicData.phoneNumbers[0].value
                                            let media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: peerAndContactData.0?.id, vCardData: nil)
                                            let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                                            strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                                                if let strongSelf = self {
                                                    strongSelf.chatDisplayNode.collapseInput()
                                                    
                                                    strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                                        $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                                    })
                                                }
                                            }, nil)
                                            
                                            var enqueueMessages: [EnqueueMessage] = []
                                            if let textEnqueueMessage = textEnqueueMessage {
                                                enqueueMessages.append(textEnqueueMessage)
                                            }
                                            var attributes: [MessageAttribute] = []
                                            if let parameters {
                                                if let effect = parameters.effect {
                                                    attributes.append(EffectMessageAttribute(id: effect.id))
                                                }
                                            }
                                            enqueueMessages.append(.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
                                            strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
                                        } else {
                                            let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
                                                guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
                                                    return
                                                }
                                                let phone = contactData.basicData.phoneNumbers[0].value
                                                if let vCardData = contactData.serializedVCard() {
                                                    let media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: peer?.id, vCardData: vCardData)
                                                    let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                                                    strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                                                        if let strongSelf = self {
                                                            strongSelf.chatDisplayNode.collapseInput()
                                                            
                                                            strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                                                $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                                            })
                                                        }
                                                    }, nil)
                                                    
                                                    var enqueueMessages: [EnqueueMessage] = []
                                                    if let textEnqueueMessage = textEnqueueMessage {
                                                        enqueueMessages.append(textEnqueueMessage)
                                                    }
                                                    enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
                                                    strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
                                                }
                                            }), completed: nil, cancelled: nil)
                                            strongSelf.effectiveNavigationController?.pushViewController(contactController)
                                        }
                                    }
                                }))
                            }
                        }
                    }))
                case .poll:
                    if let controller = strongSelf.configurePollCreation() as? AttachmentContainable {
                        completion(controller, controller.mediaPickerContext)
                        strongSelf.controllerNavigationDisposable.set(nil)
                    }
                case .gift:
                    if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                        let premiumGiftOptions = strongSelf.presentationInterfaceState.premiumGiftOptions
                        if !premiumGiftOptions.isEmpty {
                            let controller = PremiumGiftAttachmentScreen(context: context, peerIds: [peer.id], options: premiumGiftOptions, source: .attachMenu, pushController: { [weak self] c in
                                if let strongSelf = self {
                                    strongSelf.push(c)
                                }
                            }, completion: { [weak self] in
                                if let strongSelf = self {
                                    strongSelf.hintPlayNextOutgoingGift()
                                    strongSelf.attachmentController?.dismiss(animated: true)
                                }
                            })
                            completion(controller, controller.mediaPickerContext)
                            strongSelf.controllerNavigationDisposable.set(nil)
                            
                            let _ = ApplicationSpecificNotice.incrementDismissedPremiumGiftSuggestion(accountManager: context.sharedContext.accountManager, peerId: peer.id, timestamp: Int32(Date().timeIntervalSince1970)).startStandalone()
                        }
                    }
                case let .app(bot):
                    if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                        var payload: String?
                        var fromAttachMenu = true
                        if case let .bot(_, botPayload, _) = subject {
                            payload = botPayload
                            fromAttachMenu = false
                        }
                        let params = WebAppParameters(source: fromAttachMenu ? .attachMenu : .generic, peerId: peer.id, botId: bot.peer.id, botName: bot.shortName, botVerified: bot.peer.isVerified, url: nil, queryId: nil, payload: payload, buttonText: nil, keepAliveSignal: nil, forceHasSettings: false, fullSize: false)
                        let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                        let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageSubject?.messageId, threadId: strongSelf.chatLocation.threadId)
                        controller.openUrl = { [weak self] url, concealed, commit in
                            self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
                        }
                        controller.getNavigationController = { [weak self] in
                            return self?.effectiveNavigationController
                        }
                        controller.completion = { [weak self] in
                            if let strongSelf = self {
                                strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                    $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                })
                                strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
                            }
                        }
                        completion(controller, controller.mediaPickerContext)
                        strongSelf.controllerNavigationDisposable.set(nil)
                        
                        if bot.flags.contains(.notActivated) {
                            let alertController = webAppTermsAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bot: bot, completion: { [weak self] allowWrite in
                                guard let self else {
                                    return
                                }
                                if bot.flags.contains(.showInSettingsDisclaimer) {
                                    let _ = self.context.engine.messages.acceptAttachMenuBotDisclaimer(botId: bot.peer.id).startStandalone()
                                }
                                let _ = (self.context.engine.messages.addBotToAttachMenu(botId: bot.peer.id, allowWrite: allowWrite)
                                         |> deliverOnMainQueue).startStandalone(error: { _ in
                                }, completed: { [weak controller] in
                                    controller?.refresh()
                                })
                            },
                                                                             dismissed: {
                                strongSelf.attachmentController?.dismiss(animated: true)
                            })
                            strongSelf.present(alertController, in: .window(.root))
                        }
                    }
                case .quickReply:
                    let _ = (strongSelf.context.sharedContext.makeQuickReplySetupScreenInitialData(context: strongSelf.context)
                    |> take(1)
                    |> deliverOnMainQueue).start(next: { [weak strongSelf] initialData in
                        guard let strongSelf else {
                            return
                        }
                        
                        let controller = QuickReplySetupScreen(context: strongSelf.context, initialData: initialData as! QuickReplySetupScreen.InitialData, mode: .select(completion: { [weak strongSelf] shortcutId in
                            guard let strongSelf else {
                                return
                            }
                            strongSelf.attachmentController?.dismiss(animated: true)
                            strongSelf.interfaceInteraction?.sendShortcut(shortcutId)
                        }))
                        completion(controller, controller.mediaPickerContext)
                        strongSelf.controllerNavigationDisposable.set(nil)
                    })
                default:
                    break
                }
            }
            let present = {
                attachmentController.navigationPresentation = .flatModal
                strongSelf.push(attachmentController)
                strongSelf.attachmentController = attachmentController
                
                if case let .bot(botId, _, botJustInstalled) = subject, botJustInstalled {
                    if let button = allButtons.first(where: { button in
                        if case let .app(bot) = button, bot.peer.id == botId {
                            return true
                        } else {
                            return false
                        }
                    }), case let .app(bot) = button {
                        Queue.mainQueue().after(0.3) {
                            let content: UndoOverlayContent
                            if bot.flags.contains(.showInSettings) {
                                content = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsSettingsAdded(bot.shortName).string, timeout: 5.0, customUndoText: nil)
                            } else {
                                content = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsAdded(bot.shortName).string, timeout: 5.0, customUndoText: nil)
                            }
                            attachmentController.present(UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: false, position: .top, action: { _ in return false }), in: .current)
                        }
                    }
                }
            }
            
            if inputIsActive {
                Queue.mainQueue().after(0.15, {
                    present()
                })
            } else {
                present()
            }
        })
    }
    
    func oldPresentAttachmentMenu(editMediaOptions: MessageMediaEditingOptions?, editMediaReference: AnyMediaReference?) {
        let _ = (self.context.sharedContext.accountManager.transaction { transaction -> GeneratedMediaStoreSettings in
            let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings)?.get(GeneratedMediaStoreSettings.self)
            return entry ?? GeneratedMediaStoreSettings.defaultSettings
        }
        |> deliverOnMainQueue).startStandalone(next: { [weak self] settings in
            guard let strongSelf = self else {
                return
            }
            strongSelf.chatDisplayNode.dismissInput()

            var bannedSendMedia: (Int32, Bool)?
            var canSendPolls = true
            if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                if let channel = peer as? TelegramChannel {
                    if let value = channel.hasBannedPermission(.banSendMedia) {
                        bannedSendMedia = value
                    }
                    if channel.hasBannedPermission(.banSendPolls) != nil {
                        canSendPolls = false
                    }
                } else if let group = peer as? TelegramGroup {
                    if group.hasBannedPermission(.banSendMedia) {
                        bannedSendMedia = (Int32.max, false)
                    }
                    if group.hasBannedPermission(.banSendPolls) {
                        canSendPolls = false
                    }
                }
            }
        
            if editMediaOptions == nil, let (untilDate, personal) = bannedSendMedia {
                let banDescription: String
                if untilDate != 0 && untilDate != Int32.max {
                    banDescription = strongSelf.presentationInterfaceState.strings.Conversation_RestrictedMediaTimed(stringForFullDate(timestamp: untilDate, strings: strongSelf.presentationInterfaceState.strings, dateTimeFormat: strongSelf.presentationInterfaceState.dateTimeFormat)).string
                } else if personal {
                    banDescription = strongSelf.presentationInterfaceState.strings.Conversation_RestrictedMedia
                } else {
                    banDescription = strongSelf.presentationInterfaceState.strings.Conversation_DefaultRestrictedMedia
                }
                
                let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
                var items: [ActionSheetItem] = []
                items.append(ActionSheetTextItem(title: banDescription))
                items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_Location, color: .accent, action: { [weak actionSheet] in
                    actionSheet?.dismissAnimated()
                    self?.presentLocationPicker()
                }))
                if canSendPolls {
                    items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.AttachmentMenu_Poll, color: .accent, action: { [weak actionSheet] in
                        actionSheet?.dismissAnimated()
                        if let controller = self?.configurePollCreation() {
                            self?.effectiveNavigationController?.pushViewController(controller)
                        }
                    }))
                }
                items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_Contact, color: .accent, action: { [weak actionSheet] in
                    actionSheet?.dismissAnimated()
                    self?.presentContactPicker()
                }))
                actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
                    ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
                        actionSheet?.dismissAnimated()
                    })
                ])])
                strongSelf.present(actionSheet, in: .window(.root))
                
                return
            }
        
            let legacyController = LegacyController(presentation: .custom, theme: strongSelf.presentationData.theme, initialLayout: strongSelf.validLayout)
            legacyController.blocksBackgroundWhenInOverlay = true
            legacyController.acceptsFocusWhenInOverlay = true
            legacyController.statusBar.statusBarStyle = .Ignore
            legacyController.controllerLoaded = { [weak legacyController] in
                legacyController?.view.disablesInteractiveTransitionGestureRecognizer = true
            }
        
            let emptyController = LegacyEmptyController(context: legacyController.context)!
            let navigationController = makeLegacyNavigationController(rootController: emptyController)
            navigationController.setNavigationBarHidden(true, animated: false)
            legacyController.bind(controller: navigationController)
        
            legacyController.enableSizeClassSignal = true
            
            let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
            let menuEditMediaOptions = editMediaOptions.flatMap { options -> LegacyAttachmentMenuMediaEditing in
                var result: LegacyAttachmentMenuMediaEditing = .none
                if options.contains(.imageOrVideo) {
                    result = .imageOrVideo(editMediaReference)
                }
                return result
            }
            
            var slowModeEnabled = false
            var hasSchedule = false
            if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                if let channel = peer as? TelegramChannel, channel.isRestrictedBySlowmode {
                    slowModeEnabled = true
                }
                hasSchedule = strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat
            }
            
            let controller = legacyAttachmentMenu(context: strongSelf.context, peer: strongSelf.presentationInterfaceState.renderedPeer?.peer, threadTitle: strongSelf.threadInfo?.title, chatLocation: strongSelf.chatLocation, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, hasSchedule: hasSchedule, canSendPolls: canSendPolls, updatedPresentationData: strongSelf.updatedPresentationData, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText, openGallery: {
                self?.presentOldMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting, scheduleTime in
                    if !inputText.string.isEmpty {
                        strongSelf.clearInputText()
                    }
                    if editMediaOptions != nil {
                        self?.editMessageMediaWithLegacySignals(signals)
                    } else {
                        self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
                    }
                })
            }, openCamera: { [weak self] cameraView, menuController in
                if let strongSelf = self {
                    var enablePhoto = true
                    var enableVideo = true
                    
                    if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveCall {
                        enableVideo = false
                    }
                    
                    var bannedSendPhotos: (Int32, Bool)?
                    var bannedSendVideos: (Int32, Bool)?
                    
                    if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                        if let channel = peer as? TelegramChannel {
                            if let value = channel.hasBannedPermission(.banSendPhotos) {
                                bannedSendPhotos = value
                            }
                            if let value = channel.hasBannedPermission(.banSendVideos) {
                                bannedSendVideos = value
                            }
                        } else if let group = peer as? TelegramGroup {
                            if group.hasBannedPermission(.banSendPhotos) {
                                bannedSendPhotos = (Int32.max, false)
                            }
                            if group.hasBannedPermission(.banSendVideos) {
                                bannedSendVideos = (Int32.max, false)
                            }
                        }
                    }
                    
                    if bannedSendPhotos != nil {
                        enablePhoto = false
                    }
                    if bannedSendVideos != nil {
                        enableVideo = false
                    }
                    
                    var storeCapturedPhotos = false
                    var hasSchedule = false
                    if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                        storeCapturedPhotos = peer.id.namespace != Namespaces.Peer.SecretChat
                        
                        hasSchedule = strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat
                    }
                    
                    presentedLegacyCamera(context: strongSelf.context, peer: strongSelf.presentationInterfaceState.renderedPeer?.peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: storeCapturedPhotos, mediaGrouping: true, initialCaption: inputText, hasSchedule: hasSchedule, enablePhoto: enablePhoto, enableVideo: enableVideo, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
                        if let strongSelf = self {
                            if editMediaOptions != nil {
                                strongSelf.editMessageMediaWithLegacySignals(signals!)
                            } else {
                                strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
                            }
                            if !inputText.string.isEmpty {
                                strongSelf.clearInputText()
                            }
                        }
                    }, recognizedQRCode: { [weak self] code in
                        if let strongSelf = self {
                            if let (host, port, username, password, secret) = parseProxyUrl(sharedContext: strongSelf.context.sharedContext, url: code) {
                                strongSelf.openResolved(result: ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret), sourceMessageId: nil)
                            }
                        }
                    }, presentSchedulePicker: { [weak self] _, done in
                        if let strongSelf = self {
                            strongSelf.presentScheduleTimePicker(style: .media, completion: { [weak self] time in
                                if let strongSelf = self {
                                    done(time)
                                    if strongSelf.presentationInterfaceState.subject != .scheduledMessages && time != scheduleWhenOnlineTimestamp {
                                        strongSelf.openScheduledMessages()
                                    }
                                }
                            })
                        }
                    }, presentTimerPicker: { [weak self] done in
                        if let strongSelf = self {
                            strongSelf.presentTimerPicker(style: .media, completion: { time in
                                done(time)
                            })
                        }
                    }, getCaptionPanelView: { [weak self] in
                        return self?.getCaptionPanelView(isFile: false)
                    })
                }
            }, openFileGallery: {
                self?.presentFileMediaPickerOptions(editingMessage: editMediaOptions != nil)
            }, openWebSearch: { [weak self] in
                self?.presentWebSearch(editingMessage: editMediaOptions != nil, attachment: false, present: { [weak self] c, a in
                    self?.present(c, in: .window(.root), with: a)
                })
            }, openMap: {
                self?.presentLocationPicker()
            }, openContacts: {
                self?.presentContactPicker()
            }, openPoll: {
                if let controller = self?.configurePollCreation() {
                    self?.effectiveNavigationController?.pushViewController(controller)
                }
            }, presentSelectionLimitExceeded: {
                guard let strongSelf = self else {
                    return
                }
                let text: String
                if slowModeEnabled {
                    text = strongSelf.presentationData.strings.Chat_SlowmodeAttachmentLimitReached
                } else {
                    text = strongSelf.presentationData.strings.Chat_AttachmentLimitReached
                }
                strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
            }, presentCantSendMultipleFiles: {
                guard let strongSelf = self else {
                    return
                }
                strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentMultipleFilesDisabled, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
            }, presentJpegConversionAlert: { completion in
                strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.MediaPicker_JpegConversionText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.MediaPicker_KeepHeic, action: {
                    completion(false)
                }), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.MediaPicker_ConvertToJpeg, action: {
                    completion(true)
                })], actionLayout: .vertical), in: .window(.root))
            }, presentSchedulePicker: { [weak self] _, done in
                if let strongSelf = self {
                    strongSelf.presentScheduleTimePicker(style: .media, completion: { [weak self] time in
                        if let strongSelf = self {
                            done(time)
                            if strongSelf.presentationInterfaceState.subject != .scheduledMessages && time != scheduleWhenOnlineTimestamp {
                                strongSelf.openScheduledMessages()
                            }
                         }
                    })
                }
            }, presentTimerPicker: { [weak self] done in
                if let strongSelf = self {
                    strongSelf.presentTimerPicker(style: .media, completion: { time in
                        done(time)
                    })
                }
            }, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime, getAnimatedTransitionSource, completion in
                guard let strongSelf = self else {
                    completion()
                    return
                }
                if !inputText.string.isEmpty {
                    strongSelf.clearInputText()
                }
                if editMediaOptions != nil {
                    strongSelf.editMessageMediaWithLegacySignals(signals!)
                    completion()
                } else {
                    let immediateCompletion = getAnimatedTransitionSource == nil
                    strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: {
                        if !immediateCompletion {
                            completion()
                        }
                    })
                    if immediateCompletion {
                        completion()
                    }
                }
            }, selectRecentlyUsedInlineBot: { [weak self] peer in
                if let strongSelf = self, let addressName = peer.addressName {
                    strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                        $0.updatedInterfaceState({ $0.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: "@" + addressName + " "))) }).updatedInputMode({ _ in
                            return .text
                        })
                    })
                }
            }, getCaptionPanelView: { [weak self] in
                return self?.getCaptionPanelView(isFile: false)
            }, present: { [weak self] c, a in
                self?.present(c, in: .window(.root), with: a)
            })
            controller.didDismiss = { [weak legacyController] _ in
                legacyController?.dismiss()
            }
            controller.customRemoveFromParentViewController = { [weak legacyController] in
                legacyController?.dismiss()
            }
        
            legacyController.blocksBackgroundWhenInOverlay = true
            strongSelf.present(legacyController, in: .window(.root))
            controller.present(in: emptyController, sourceView: nil, animated: true)
            
            let presentationDisposable = strongSelf.updatedPresentationData.1.startStrict(next: { [weak controller] presentationData in
                if let controller = controller {
                    controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme, forceDark: false)
                }
            })
            legacyController.disposables.add(presentationDisposable)
        })
    }
    
    func presentFileGallery(editingMessage: Bool = false) {
        self.presentOldMediaPicker(fileMode: true, editingMedia: editingMessage, completion: { [weak self] signals, silentPosting, scheduleTime in
            if editingMessage {
                self?.editMessageMediaWithLegacySignals(signals)
            } else {
                self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
            }
        })
    }
    
    func presentICloudFileGallery(editingMessage: Bool = false) {
        let _ = (self.context.engine.data.get(
            TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId),
            TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
            TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
        )
        |> deliverOnMainQueue).startStandalone(next: { [weak self] result in
            guard let strongSelf = self else {
                return
            }
            let (accountPeer, limits, premiumLimits) = result
            let isPremium = accountPeer?.isPremium ?? false
            
            strongSelf.present(legacyICloudFilePicker(theme: strongSelf.presentationData.theme, completion: { [weak self] urls in
                if let strongSelf = self, !urls.isEmpty {
                    var signals: [Signal<ICloudFileDescription?, NoError>] = []
                    for url in urls {
                        signals.append(iCloudFileDescription(url))
                    }
                    strongSelf.enqueueMediaMessageDisposable.set((combineLatest(signals)
                    |> deliverOnMainQueue).startStrict(next: { results in
                        if let strongSelf = self {
                            let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                            
                            for item in results {
                                if let item = item {
                                    if item.fileSize > Int64(premiumLimits.maxUploadFileParts) * 512 * 1024 {
                                        let controller = PremiumLimitScreen(context: strongSelf.context, subject: .files, count: 4, action: {
                                            return true
                                        })
                                        strongSelf.push(controller)
                                        return
                                    } else if item.fileSize > Int64(limits.maxUploadFileParts) * 512 * 1024 && !isPremium {
                                        let context = strongSelf.context
                                        var replaceImpl: ((ViewController) -> Void)?
                                        let controller = PremiumLimitScreen(context: context, subject: .files, count: 2, action: {
                                            replaceImpl?(PremiumIntroScreen(context: context, source: .upload))
                                            return true
                                        })
                                        replaceImpl = { [weak controller] c in
                                            controller?.replace(with: c)
                                        }
                                        strongSelf.push(controller)
                                        return
                                    }
                                }
                            }
                            
                            var groupingKey: Int64?
                            var fileTypes: (music: Bool, other: Bool) = (false, false)
                            if results.count > 1 {
                                for item in results {
                                    if let item = item {
                                        let pathExtension = (item.fileName as NSString).pathExtension.lowercased()
                                        if ["mp3", "m4a"].contains(pathExtension) {
                                            fileTypes.music = true
                                        } else {
                                            fileTypes.other = true
                                        }
                                    }
                                }
                            }
                            if fileTypes.music != fileTypes.other {
                                groupingKey = Int64.random(in: Int64.min ... Int64.max)
                            }
                            
                            var messages: [EnqueueMessage] = []
                            for item in results {
                                if let item = item {
                                    let fileId = Int64.random(in: Int64.min ... Int64.max)
                                    let mimeType = guessMimeTypeByFileExtension((item.fileName as NSString).pathExtension)
                                    var previewRepresentations: [TelegramMediaImageRepresentation] = []
                                    if mimeType.hasPrefix("image/") || mimeType == "application/pdf" {
                                        previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 320, height: 320), resource: ICloudFileResource(urlData: item.urlData, thumbnail: true), progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
                                    }
                                    var attributes: [TelegramMediaFileAttribute] = []
                                    attributes.append(.FileName(fileName: item.fileName))
                                    if let audioMetadata = item.audioMetadata {
                                        attributes.append(.Audio(isVoice: false, duration: audioMetadata.duration, title: audioMetadata.title, performer: audioMetadata.performer, waveform: nil))
                                    }
                                    
                                    let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData, thumbnail: false), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int64(item.fileSize), attributes: attributes)
                                    let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: groupingKey, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                                    messages.append(message)
                                }
                                if let _ = groupingKey, messages.count % 10 == 0 {
                                    groupingKey = Int64.random(in: Int64.min ... Int64.max)
                                }
                            }
                            
                            if !messages.isEmpty {
                                if editingMessage {
                                    strongSelf.editMessageMediaWithMessages(messages)
                                } else {
                                    strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                                        if let strongSelf = self {
                                            strongSelf.chatDisplayNode.collapseInput()
                                            
                                            strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                                $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                            })
                                        }
                                    }, nil)
                                    strongSelf.sendMessages(messages)
                                }
                            }
                        }
                    }))
                }
            }), in: .window(.root))
        })
    }
    
    func presentFileMediaPickerOptions(editingMessage: Bool) {
        let actionSheet = ActionSheetController(presentationData: self.presentationData)
        actionSheet.setItemGroups([ActionSheetItemGroup(items: [
            ActionSheetButtonItem(title: self.presentationData.strings.Conversation_FilePhotoOrVideo, action: { [weak self, weak actionSheet] in
                actionSheet?.dismissAnimated()
                if let strongSelf = self {
                    strongSelf.presentFileGallery(editingMessage: editingMessage)
                }
            }),
            ActionSheetButtonItem(title: self.presentationData.strings.Conversation_FileICloudDrive, action: { [weak self, weak actionSheet] in
                actionSheet?.dismissAnimated()
                if let strongSelf = self {
                    strongSelf.presentICloudFileGallery(editingMessage: editingMessage)
                }
            })
        ]), ActionSheetItemGroup(items: [
            ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
                actionSheet?.dismissAnimated()
            })
        ])])
        self.chatDisplayNode.dismissInput()
        self.present(actionSheet, in: .window(.root))
    }
    
    func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil, .default), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, ChatSendMessageActionSheetController.SendParameters?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
        var isScheduledMessages = false
        if case .scheduledMessages = self.presentationInterfaceState.subject {
            isScheduledMessages = true
        }
        var paidMediaAllowed = false
        if let cachedData = self.peerView?.cachedData as? CachedChannelData, cachedData.flags.contains(.paidMediaAllowed) {
            paidMediaAllowed = true
        }
        let controller = MediaPickerScreen(
            context: self.context,
            updatedPresentationData: self.updatedPresentationData,
            peer: (self.presentationInterfaceState.renderedPeer?.peer).flatMap(EnginePeer.init),
            threadTitle: self.threadInfo?.title,
            chatLocation: self.chatLocation,
            isScheduledMessages: isScheduledMessages, 
            bannedSendPhotos: bannedSendPhotos,
            bannedSendVideos: bannedSendVideos,
            canBoostToUnrestrict: (self.presentationInterfaceState.boostsToUnrestrict ?? 0) > 0 && bannedSendPhotos?.1 != true && bannedSendVideos?.1 != true,
            paidMediaAllowed: paidMediaAllowed,
            subject: subject,
            saveEditedPhotos: saveEditedPhotos
        )
        controller.openBoost = { [weak self, weak controller] in
            if let self {
                controller?.dismiss()
                self.interfaceInteraction?.openBoostToUnrestrict()
            }
        }
        let mediaPickerContext = controller.mediaPickerContext
        controller.openCamera = { [weak self] cameraView in
            if let cameraView = cameraView as? TGAttachmentCameraView {
                self?.openCamera(cameraView: cameraView)
            } else {
                self?.openCamera(cameraView: nil)
            }
        }
        controller.presentWebSearch = { [weak self, weak controller] mediaGroups, activateOnDisplay in
            self?.presentWebSearch(editingMessage: false, attachment: true, activateOnDisplay: activateOnDisplay, present: { [weak controller] c, a in
                controller?.present(c, in: .current)
                if let webSearchController = c as? WebSearchController {
                    webSearchController.searchingUpdated = { [weak mediaGroups] searching in
                        if let mediaGroups = mediaGroups, mediaGroups.isNodeLoaded {
                            let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
                            transition.updateAlpha(node: mediaGroups.displayNode, alpha: searching ? 0.0 : 1.0)
                            mediaGroups.displayNode.isUserInteractionEnabled = !searching
                        }
                    }
                    webSearchController.present(mediaGroups, in: .current)
                    webSearchController.dismissed = {
                        updateMediaPickerContext(mediaPickerContext)
                    }
                    controller?.webSearchController = webSearchController
                    updateMediaPickerContext(webSearchController.mediaPickerContext)
                }
            })
        }
        controller.presentSchedulePicker = { [weak self] media, done in
            if let strongSelf = self {
                strongSelf.presentScheduleTimePicker(style: media ? .media : .default, completion: { [weak self] time in
                    if let strongSelf = self {
                        done(time)
                        if strongSelf.presentationInterfaceState.subject != .scheduledMessages && time != scheduleWhenOnlineTimestamp {
                            strongSelf.openScheduledMessages()
                        }
                    }
                })
            }
        }
        controller.presentTimerPicker = { [weak self] done in
            if let strongSelf = self {
                strongSelf.presentTimerPicker(style: .media, completion: { time in
                    done(time)
                })
            }
        }
        controller.getCaptionPanelView = { [weak self] in
            return self?.getCaptionPanelView(isFile: false)
        }
        controller.legacyCompletion = { signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion in
            completion(signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion)
        }
        present(controller, mediaPickerContext)
    }
    
    func presentOldMediaPicker(fileMode: Bool, editingMedia: Bool, completion: @escaping ([Any], Bool, Int32) -> Void) {
        let engine = self.context.engine
        let _ = (self.context.sharedContext.accountManager.transaction { transaction -> Signal<(GeneratedMediaStoreSettings, EngineConfiguration.SearchBots), NoError> in
            let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings)?.get(GeneratedMediaStoreSettings.self)
            
            return engine.data.get(TelegramEngine.EngineData.Item.Configuration.SearchBots())
            |> map { configuration -> (GeneratedMediaStoreSettings, EngineConfiguration.SearchBots) in
                return (entry ?? GeneratedMediaStoreSettings.defaultSettings, configuration)
            }
        }
        |> switchToLatest
        |> deliverOnMainQueue).startStandalone(next: { [weak self] settings, searchBotsConfiguration in
            guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
                return
            }
            let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
            var selectionLimit: Int = 100
            var slowModeEnabled = false
            if let channel = peer as? TelegramChannel, channel.isRestrictedBySlowmode {
                selectionLimit = 10
                slowModeEnabled = true
            }
            
            let _ = legacyAssetPicker(context: strongSelf.context, presentationData: strongSelf.presentationData, editingMedia: editingMedia, fileMode: fileMode, peer: peer, threadTitle: strongSelf.threadInfo?.title, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, selectionLimit: selectionLimit).startStandalone(next: { generator in
                if let strongSelf = self {
                    let legacyController = LegacyController(presentation: fileMode ? .navigation : .custom, theme: strongSelf.presentationData.theme, initialLayout: strongSelf.validLayout)
                    legacyController.navigationPresentation = .modal
                    legacyController.statusBar.statusBarStyle = strongSelf.presentationData.theme.rootController.statusBarStyle.style
                    legacyController.controllerLoaded = { [weak legacyController] in
                        legacyController?.view.disablesInteractiveTransitionGestureRecognizer = true
                        legacyController?.view.disablesInteractiveModalDismiss = true
                    }
                    let controller = generator(legacyController.context)
                    
                    legacyController.bind(controller: controller)
                    legacyController.deferScreenEdgeGestures = [.top]
                                        
                    configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, presentWebSearch: editingMedia ? nil : { [weak self, weak legacyController] in
                        if let strongSelf = self {
                            let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: EnginePeer(peer), chatLocation: strongSelf.chatLocation, configuration: searchBotsConfiguration, mode: .media(attachment: false, completion: { results, selectionState, editingState, silentPosting in
                                if let legacyController = legacyController {
                                    legacyController.dismiss()
                                }
                                legacyEnqueueWebSearchMessages(selectionState, editingState, enqueueChatContextResult: { result in
                                    if let strongSelf = self {
                                        strongSelf.enqueueChatContextResult(results, result, hideVia: true)
                                    }
                                }, enqueueMediaMessages: { signals in
                                    if let strongSelf = self {
                                        if editingMedia {
                                            strongSelf.editMessageMediaWithLegacySignals(signals)
                                        } else {
                                            strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
                                        }
                                    }
                                })
                            }))
                            controller.getCaptionPanelView = { [weak self] in
                                return self?.getCaptionPanelView(isFile: fileMode)
                            }
                            strongSelf.effectiveNavigationController?.pushViewController(controller)
                        }
                    }, presentSelectionLimitExceeded: {
                        guard let strongSelf = self else {
                            return
                        }
                        
                        let text: String
                        if slowModeEnabled {
                            text = strongSelf.presentationData.strings.Chat_SlowmodeAttachmentLimitReached
                        } else {
                            text = strongSelf.presentationData.strings.Chat_AttachmentLimitReached
                        }
                        
                        strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
                    }, presentSchedulePicker: { [weak self] media, done in
                        if let strongSelf = self {
                            strongSelf.presentScheduleTimePicker(style: media ? .media : .default, completion: { [weak self] time in
                                if let strongSelf = self {
                                     done(time)
                                     if strongSelf.presentationInterfaceState.subject != .scheduledMessages && time != scheduleWhenOnlineTimestamp {
                                         strongSelf.openScheduledMessages()
                                     }
                                 }
                            })
                        }
                    }, presentTimerPicker: { [weak self] done in
                        if let strongSelf = self {
                            strongSelf.presentTimerPicker(style: .media, completion: { time in
                                done(time)
                            })
                        }
                    }, getCaptionPanelView: { [weak self] in
                        return self?.getCaptionPanelView(isFile: fileMode)
                    })
                    controller.descriptionGenerator = legacyAssetPickerItemGenerator()
                    controller.completionBlock = { [weak legacyController] signals, silentPosting, scheduleTime in
                        if let legacyController = legacyController {
                            legacyController.dismiss(animated: true)
                            completion(signals!, silentPosting, scheduleTime)
                        }
                    }
                    controller.dismissalBlock = { [weak legacyController] in
                        if let legacyController = legacyController {
                            legacyController.dismiss(animated: true)
                        }
                    }
                    strongSelf.chatDisplayNode.dismissInput()
                    strongSelf.effectiveNavigationController?.pushViewController(legacyController)
                }
            })
        })
    }
    
    func presentWebSearch(editingMessage: Bool, attachment: Bool, activateOnDisplay: Bool = true, present: @escaping (ViewController, Any?) -> Void) {
        guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
            return
        }
        
        let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.SearchBots())
        |> deliverOnMainQueue).startStandalone(next: { [weak self] configuration in
            if let strongSelf = self {
                let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: EnginePeer(peer), chatLocation: strongSelf.chatLocation, configuration: configuration, mode: .media(attachment: attachment, completion: { [weak self] results, selectionState, editingState, silentPosting in
                    self?.attachmentController?.dismiss(animated: true, completion: nil)
                    legacyEnqueueWebSearchMessages(selectionState, editingState, enqueueChatContextResult: { [weak self] result in
                        if let strongSelf = self {
                            strongSelf.enqueueChatContextResult(results, result, hideVia: true)
                        }
                    }, enqueueMediaMessages: { [weak self] signals in
                        if let strongSelf = self, !signals.isEmpty {
                            if editingMessage {
                                strongSelf.editMessageMediaWithLegacySignals(signals)
                            } else {
                                strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
                            }
                        }
                    })
                }), activateOnDisplay: activateOnDisplay)
                controller.attemptItemSelection = { [weak strongSelf] item in
                    guard let strongSelf, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
                        return false
                    }
                    
                    enum ItemType {
                        case gif
                        case image
                        case video
                    }
                    
                    var itemType: ItemType?
                    switch item {
                    case let .internalReference(reference):
                        if reference.type == "gif" {
                            itemType = .gif
                        } else if reference.type == "photo" {
                            itemType = .image
                        } else if reference.type == "video" {
                            itemType = .video
                        }
                    case let .externalReference(reference):
                        if reference.type == "gif" {
                            itemType = .gif
                        } else if reference.type == "photo" {
                            itemType = .image
                        } else if reference.type == "video" {
                            itemType = .video
                        }
                    }
                    
                    var bannedSendPhotos: (Int32, Bool)?
                    var bannedSendVideos: (Int32, Bool)?
                    var bannedSendGifs: (Int32, Bool)?
                    
                    if let channel = peer as? TelegramChannel {
                        if let value = channel.hasBannedPermission(.banSendPhotos) {
                            bannedSendPhotos = value
                        }
                        if let value = channel.hasBannedPermission(.banSendVideos) {
                            bannedSendVideos = value
                        }
                        if let value = channel.hasBannedPermission(.banSendGifs) {
                            bannedSendGifs = value
                        }
                    } else if let group = peer as? TelegramGroup {
                        if group.hasBannedPermission(.banSendPhotos) {
                            bannedSendPhotos = (Int32.max, false)
                        }
                        if group.hasBannedPermission(.banSendVideos) {
                            bannedSendVideos = (Int32.max, false)
                        }
                        if group.hasBannedPermission(.banSendGifs) {
                            bannedSendGifs = (Int32.max, false)
                        }
                    }
                    
                    if let itemType {
                        switch itemType {
                        case .image:
                            if bannedSendPhotos != nil {
                                strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
                                
                                return false
                            }
                        case .video:
                            if bannedSendVideos != nil {
                                strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
                                
                                return false
                            }
                        case .gif:
                            if bannedSendGifs != nil {
                                strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
                                
                                return false
                            }
                        }
                    }
                    
                    return true
                }
                controller.getCaptionPanelView = { [weak strongSelf] in
                    return strongSelf?.getCaptionPanelView(isFile: false)
                }
                present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
            }
        })
    }
      
    func presentLocationPicker() {
        guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
            return
        }
        let selfPeerId: PeerId
        if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
            selfPeerId = peer.id
        } else if let peer = peer as? TelegramChannel, case .group = peer.info, peer.hasPermission(.canBeAnonymous) {
            selfPeerId = peer.id
        } else {
            selfPeerId = self.context.account.peerId
        }
        let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: selfPeerId))
        |> deliverOnMainQueue).startStandalone(next: { [weak self] selfPeer in
            guard let strongSelf = self, let selfPeer = selfPeer else {
                return
            }
            let hasLiveLocation = peer.id.namespace != Namespaces.Peer.SecretChat && peer.id != strongSelf.context.account.peerId && strongSelf.presentationInterfaceState.subject != .scheduledMessages
            let controller = LocationPickerController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, mode: .share(peer: EnginePeer(peer), selfPeer: selfPeer, hasLiveLocation: hasLiveLocation), completion: { [weak self] location, _, _, _, _ in
                guard let strongSelf = self else {
                    return
                }
                let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: location), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                    if let strongSelf = self {
                        strongSelf.chatDisplayNode.collapseInput()
                        
                        strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                            $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                        })
                    }
                }, nil)
                strongSelf.sendMessages([message])
            })
            strongSelf.effectiveNavigationController?.pushViewController(controller)
            strongSelf.chatDisplayNode.dismissInput()
        })
    }
    
    func presentContactPicker() {
        let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true))
        contactsController.navigationPresentation = .modal
        self.chatDisplayNode.dismissInput()
        self.effectiveNavigationController?.pushViewController(contactsController)
        self.controllerNavigationDisposable.set((contactsController.result
        |> deliverOnMainQueue).startStrict(next: { [weak self] peers in
            if let strongSelf = self, let (peers, _, _, _, _, _) = peers {
                if peers.count > 1 {
                    var enqueueMessages: [EnqueueMessage] = []
                    for peer in peers {
                        var media: TelegramMediaContact?
                        switch peer {
                            case let .peer(contact, _, _):
                                guard let contact = contact as? TelegramUser, let phoneNumber = contact.phone else {
                                    continue
                                }
                                let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName ?? "", lastName: contact.lastName ?? "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
                                
                                let phone = contactData.basicData.phoneNumbers[0].value
                                media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: contact.id, vCardData: nil)
                            case let .deviceContact(_, basicData):
                                guard !basicData.phoneNumbers.isEmpty else {
                                    continue
                                }
                                let contactData = DeviceContactExtendedData(basicData: basicData, middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
                                
                                let phone = contactData.basicData.phoneNumbers[0].value
                                media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: nil)
                        }

                        if let media = media {
                            let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                            strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                                if let strongSelf = self {
                                    strongSelf.chatDisplayNode.collapseInput()
                                    
                                    strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                        $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                    })
                                }
                            }, nil)
                            let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                            enqueueMessages.append(message)
                        }
                    }
                    strongSelf.sendMessages(enqueueMessages)
                } else if let peer = peers.first {
                    let dataSignal: Signal<(Peer?,  DeviceContactExtendedData?), NoError>
                    switch peer {
                        case let .peer(contact, _, _):
                            guard let contact = contact as? TelegramUser, let phoneNumber = contact.phone else {
                                return
                            }
                            let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName ?? "", lastName: contact.lastName ?? "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
                            let context = strongSelf.context
                            dataSignal = (strongSelf.context.sharedContext.contactDataManager?.basicData() ?? .single([:]))
                            |> take(1)
                            |> mapToSignal { basicData -> Signal<(Peer?,  DeviceContactExtendedData?), NoError> in
                                var stableId: String?
                                let queryPhoneNumber = formatPhoneNumber(context: context, number: phoneNumber)
                                outer: for (id, data) in basicData {
                                    for phoneNumber in data.phoneNumbers {
                                        if formatPhoneNumber(context: context, number: phoneNumber.value) == queryPhoneNumber {
                                            stableId = id
                                            break outer
                                        }
                                    }
                                }
                                
                                if let stableId = stableId {
                                    return (context.sharedContext.contactDataManager?.extendedData(stableId: stableId) ?? .single(nil))
                                    |> take(1)
                                    |> map { extendedData -> (Peer?,  DeviceContactExtendedData?) in
                                        return (contact, extendedData)
                                    }
                                } else {
                                    return .single((contact, contactData))
                                }
                            }
                        case let .deviceContact(id, _):
                            dataSignal = (strongSelf.context.sharedContext.contactDataManager?.extendedData(stableId: id) ?? .single(nil))
                            |> take(1)
                            |> map { extendedData -> (Peer?,  DeviceContactExtendedData?) in
                                return (nil, extendedData)
                            }
                    }
                    strongSelf.controllerNavigationDisposable.set((dataSignal
                    |> deliverOnMainQueue).startStrict(next: { peerAndContactData in
                        if let strongSelf = self, let contactData = peerAndContactData.1, contactData.basicData.phoneNumbers.count != 0 {
                            if contactData.isPrimitive {
                                let phone = contactData.basicData.phoneNumbers[0].value
                                let media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: peerAndContactData.0?.id, vCardData: nil)
                                let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                                strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                                    if let strongSelf = self {
                                        strongSelf.chatDisplayNode.collapseInput()
                                        
                                        strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                            $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                        })
                                    }
                                }, nil)
                                let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                                strongSelf.sendMessages([message])
                            } else {
                                let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
                                    guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
                                        return
                                    }
                                    let phone = contactData.basicData.phoneNumbers[0].value
                                    if let vCardData = contactData.serializedVCard() {
                                        let media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: peer?.id, vCardData: vCardData)
                                        let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
                                        strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
                                            if let strongSelf = self {
                                                strongSelf.chatDisplayNode.collapseInput()
                                                
                                                strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                                                    $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }
                                                })
                                            }
                                        }, nil)
                                        let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
                                        strongSelf.sendMessages([message])
                                    }
                                }), completed: nil, cancelled: nil)
                                strongSelf.effectiveNavigationController?.pushViewController(contactController)
                            }
                        }
                    }))
                }
            }
        }))
    }
    
    func getCaptionPanelView(isFile: Bool) -> TGCaptionPanelView? {
        var isScheduledMessages = false
        if case .scheduledMessages = self.presentationInterfaceState.subject {
            isScheduledMessages = true
        }
        return self.context.sharedContext.makeGalleryCaptionPanelView(context: self.context, chatLocation: self.presentationInterfaceState.chatLocation, isScheduledMessages: isScheduledMessages, isFile: isFile, customEmojiAvailable: self.presentationInterfaceState.customEmojiAvailable, present: { [weak self] c in
            self?.present(c, in: .window(.root))
        }, presentInGlobalOverlay: { [weak self] c in
            guard let self else {
                return
            }
            self.presentInGlobalOverlay(c)
        }) as? TGCaptionPanelView
    }
    
    func openCamera(cameraView: TGAttachmentCameraView? = nil) {
        let _ = (self.context.sharedContext.accountManager.transaction { transaction -> GeneratedMediaStoreSettings in
            let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings)?.get(GeneratedMediaStoreSettings.self)
            return entry ?? GeneratedMediaStoreSettings.defaultSettings
        }
        |> deliverOnMainQueue).startStandalone(next: { [weak self] settings in
            guard let strongSelf = self else {
                return
            }
            
            var enablePhoto = true
            var enableVideo = true
            
            if let callManager = strongSelf.context.sharedContext.callManager as? PresentationCallManagerImpl, callManager.hasActiveCall {
                enableVideo = false
            }
            
            var bannedSendPhotos: (Int32, Bool)?
            var bannedSendVideos: (Int32, Bool)?
            
            if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                if let channel = peer as? TelegramChannel {
                    if let value = channel.hasBannedPermission(.banSendPhotos) {
                        bannedSendPhotos = value
                    }
                    if let value = channel.hasBannedPermission(.banSendVideos) {
                        bannedSendVideos = value
                    }
                } else if let group = peer as? TelegramGroup {
                    if group.hasBannedPermission(.banSendPhotos) {
                        bannedSendPhotos = (Int32.max, false)
                    }
                    if group.hasBannedPermission(.banSendVideos) {
                        bannedSendVideos = (Int32.max, false)
                    }
                }
            }
            
            if bannedSendPhotos != nil {
                enablePhoto = false
            }
            if bannedSendVideos != nil {
                enableVideo = false
            }
            
            var storeCapturedMedia = false
            var hasSchedule = false
            if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
                storeCapturedMedia = peer.id.namespace != Namespaces.Peer.SecretChat
                hasSchedule = strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat
            }
            let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
            
            presentedLegacyCamera(context: strongSelf.context, peer: strongSelf.presentationInterfaceState.renderedPeer?.peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: nil, parentController: strongSelf, attachmentController: self?.attachmentController, editingMedia: false, saveCapturedPhotos: storeCapturedMedia, mediaGrouping: true, initialCaption: inputText, hasSchedule: hasSchedule, enablePhoto: enablePhoto, enableVideo: enableVideo, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
                if let strongSelf = self {
                    strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
                    if !inputText.string.isEmpty {
                        strongSelf.clearInputText()
                    }
                }
            }, recognizedQRCode: { [weak self] code in
                if let strongSelf = self {
                    if let (host, port, username, password, secret) = parseProxyUrl(sharedContext: strongSelf.context.sharedContext, url: code) {
                        strongSelf.openResolved(result: ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret), sourceMessageId: nil)
                    }
                }
            }, presentSchedulePicker: { [weak self] _, done in
                if let strongSelf = self {
                    strongSelf.presentScheduleTimePicker(style: .media, completion: { [weak self] time in
                        if let strongSelf = self {
                            done(time)
                            if strongSelf.presentationInterfaceState.subject != .scheduledMessages && time != scheduleWhenOnlineTimestamp {
                                strongSelf.openScheduledMessages()
                            }
                        }
                    })
                }
            }, presentTimerPicker: { [weak self] done in
                if let strongSelf = self {
                    strongSelf.presentTimerPicker(style: .media, completion: { time in
                        done(time)
                    })
                }
            }, getCaptionPanelView: { [weak self] in
                return self?.getCaptionPanelView(isFile: false)
            }, dismissedWithResult: { [weak self] in
                self?.attachmentController?.dismiss(animated: false, completion: nil)
            }, finishedTransitionIn: { [weak self] in
                self?.attachmentController?.scrollToTop?()
            })
        })
    }
    
    func openStickerEditor() {
        self.chatDisplayNode.dismissInput()
        
        var dismissImpl: (() -> Void)?
        let mainController = self.context.sharedContext.makeStickerMediaPickerScreen(
            context: self.context,
            getSourceRect: { return nil },
            completion: { [weak self] result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in
                guard let self else {
                    return
                }
                let subject: Signal<MediaEditorScreen.Subject?, NoError>
                if let asset = result as? PHAsset {
                    subject = .single(.asset(asset))
                } else if let image = result as? UIImage {
                    subject = .single(.image(image, PixelDimensions(image.size), nil, .bottomRight))
                } else if let result = result as? Signal<CameraScreen.Result, NoError> {
                    subject = result
                    |> map { value -> MediaEditorScreen.Subject? in
                        switch value {
                        case .pendingImage:
                            return nil
                        case let .image(image):
                            return .image(image.image, PixelDimensions(image.image.size), nil, .topLeft)
                        default:
                            return nil
                        }
                    }
                } else {
                    subject = .single(.empty(PixelDimensions(width: 1080, height: 1920)))
                }
                
                let editorController = MediaEditorScreen(
                    context: self.context,
                    mode: .stickerEditor(mode: .generic),
                    subject: subject,
                    transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery(
                        MediaEditorScreen.TransitionIn.GalleryTransitionIn(
                            sourceView: $0,
                            sourceRect: transitionRect,
                            sourceImage: transitionImage
                        )
                    ) }),
                    transitionOut: { finished, isNew in
                        if !finished, let transitionView {
                            return MediaEditorScreen.TransitionOut(
                                destinationView: transitionView,
                                destinationRect: transitionView.bounds,
                                destinationCornerRadius: 0.0
                            )
                        }
                        return nil
                    }, completion: { [weak self] result, commit in
                        dismissImpl?()
                        self?.chatDisplayNode.dismissInput()
                        
                        Queue.mainQueue().after(0.1) {
                            commit({})
                            if case let .sticker(file, _) = result.media {
                                self?.enqueueStickerFile(file)
                            }
                        }
                    } as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void
                )
                editorController.cancelled = { _ in
                    cancelled()
                }
                editorController.sendSticker = { [weak self] file, sourceView, sourceRect in
                    return self?.interfaceInteraction?.sendSticker(file, true, sourceView, sourceRect, nil, []) ?? false
                }
                self.push(editorController)
            },
            dismissed: {}
        )
        dismissImpl = { [weak mainController] in
            if let mainController, let navigationController = mainController.navigationController {
                var viewControllers = navigationController.viewControllers
                viewControllers = viewControllers.filter { c in
                    return !(c is CameraScreen) && c !== mainController
                }
                navigationController.setViewControllers(viewControllers, animated: false)
            }
        }
        mainController.navigationPresentation = .flatModal
        mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
        self.push(mainController)
    }
}