import Foundation
import UIKit
import AsyncDisplayKit
import TelegramCore
import Postbox
import SwiftSignalKit
import Display
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import LocalizedPeerData
import PhotoResources
import TelegramStringFormatting
import TextFormat
import ChatPresentationInterfaceState
import TextNodeWithEntities
import AnimationCache
import MultiAnimationRenderer
import AccessoryPanelNode
import TelegramNotices
import AppBundle
import CompositeTextNode

public final class ReplyAccessoryPanelNode: AccessoryPanelNode {
    private let messageDisposable = MetaDisposable()
    public let chatPeerId: EnginePeer.Id
    public let messageId: MessageId
    public let quote: EngineMessageReplyQuote?
    
    private var previousMediaReference: AnyMediaReference?
    
    public let closeButton: HighlightableButtonNode
    public let lineNode: ASImageNode
    public let iconView: UIImageView
    public let titleNode: CompositeTextNode
    public let textNode: ImmediateTextNodeWithEntities
    public let imageNode: TransformImageNode
    
    private let actionArea: AccessibilityAreaNode
    
    private let context: AccountContext
    public var theme: PresentationTheme
    public var strings: PresentationStrings
    
    private var textIsOptions: Bool = false
    
    private var validLayout: (size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState)?
    
    public init(context: AccountContext, chatPeerId: EnginePeer.Id, messageId: MessageId, quote: EngineMessageReplyQuote?, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) {
        self.chatPeerId = chatPeerId
        self.messageId = messageId
        self.quote = quote
        
        self.context = context
        self.theme = theme
        self.strings = strings
        
        self.closeButton = HighlightableButtonNode()
        self.closeButton.accessibilityLabel = strings.VoiceOver_DiscardPreparedContent
        self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0)
        self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: [])
        self.closeButton.displaysAsynchronously = false
        
        self.lineNode = ASImageNode()
        self.lineNode.displayWithoutProcessing = true
        self.lineNode.displaysAsynchronously = false
        self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
        
        self.iconView = UIImageView()
        if quote != nil {
            self.iconView.image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/ReplyQuoteIcon")?.withRenderingMode(.alwaysTemplate)
        } else {
            self.iconView.image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/ReplySettingsIcon")?.withRenderingMode(.alwaysTemplate)
        }
        self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor
        
        self.titleNode = CompositeTextNode()
        
        self.textNode = ImmediateTextNodeWithEntities()
        self.textNode.maximumNumberOfLines = 1
        self.textNode.displaysAsynchronously = false
        self.textNode.insets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0)
        self.textNode.visibility = true
        self.textNode.spoilerColor = self.theme.chat.inputPanel.secondaryTextColor
        
        if let animationCache = animationCache, let animationRenderer = animationRenderer {
            self.textNode.arguments = TextNodeWithEntities.Arguments(
                context: context,
                cache: animationCache,
                renderer: animationRenderer,
                placeholderColor: theme.list.mediaPlaceholderColor,
                attemptSynchronous: false
            )
        }
        
        self.imageNode = TransformImageNode()
        self.imageNode.contentAnimations = [.subsequentUpdates]
        self.imageNode.isHidden = true
        
        self.actionArea = AccessibilityAreaNode()
        
        super.init()
        
        self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
        self.addSubnode(self.closeButton)
        
        self.addSubnode(self.lineNode)
        self.view.addSubview(self.iconView)
        self.addSubnode(self.titleNode)
        self.addSubnode(self.textNode)
        self.addSubnode(self.imageNode)
        self.addSubnode(self.actionArea)
        
        self.messageDisposable.set((context.account.postbox.messageView(messageId)
        |> deliverOnMainQueue).startStrict(next: { [weak self] messageView in
            if let strongSelf = self {
                if messageView.message == nil {
                    Queue.mainQueue().justDispatch {
                        strongSelf.interfaceInteraction?.setupReplyMessage(nil, { _, _ in })
                    }
                    return
                }

                let message = messageView.message

                var authorName = ""
                var text = ""
                var isText = true
                if let forwardInfo = message?.forwardInfo, forwardInfo.flags.contains(.isImported) {
                    if let author = forwardInfo.author {
                        authorName = EnginePeer(author).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
                    } else if let authorSignature = forwardInfo.authorSignature {
                        authorName = authorSignature
                    }
                } else if let author = message?.effectiveAuthor {
                    authorName = EnginePeer(author).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
                }
                
                let isMedia: Bool
                if let message = message {
                    switch messageContentKind(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: context.account.peerId) {
                        case .text:
                            isMedia = false
                        default:
                            isMedia = true
                    }
                    let (attributedText, _, isTextValue) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: context.account.peerId)
                    text = attributedText.string
                    isText = isTextValue
                } else {
                    isMedia = false
                }

                let textFont = Font.regular(15.0)
                let messageText: NSAttributedString
                if isText, let message = message {
                    let entities = (message.textEntitiesAttribute?.entities ?? []).filter { entity in
                        switch entity.type {
                        case .Spoiler, .CustomEmoji:
                            return true
                        case .Strikethrough:
                            return true
                        default:
                            return false
                        }
                    }
                    let textColor = strongSelf.theme.chat.inputPanel.primaryTextColor
                    if entities.count > 0 {
                        messageText = stringWithAppliedEntities(trimToLineCount(message.text, lineCount: 1), entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont,  underlineLinks: false, message: message)
                    } else {
                        messageText = NSAttributedString(string: text, font: textFont, textColor: isMedia ? strongSelf.theme.chat.inputPanel.secondaryTextColor : strongSelf.theme.chat.inputPanel.primaryTextColor)
                    }
                } else {
                    messageText = NSAttributedString(string: text, font: textFont, textColor: isMedia ? strongSelf.theme.chat.inputPanel.secondaryTextColor : strongSelf.theme.chat.inputPanel.primaryTextColor)
                }
                
                var updatedMediaReference: AnyMediaReference?
                var imageDimensions: CGSize?
                var isRoundImage = false
                if let message = message, !message.containsSecretMedia {
                    for media in message.media {
                        if let image = media as? TelegramMediaImage {
                            updatedMediaReference = .message(message: MessageReference(message), media: image)
                            if let representation = largestRepresentationForPhoto(image) {
                                imageDimensions = representation.dimensions.cgSize
                            }
                            break
                        } else if let file = media as? TelegramMediaFile {
                            updatedMediaReference = .message(message: MessageReference(message), media: file)
                            isRoundImage = file.isInstantVideo
                            if let representation = largestImageRepresentation(file.previewRepresentations), !file.isSticker && !file.isAnimatedSticker {
                                imageDimensions = representation.dimensions.cgSize
                            }
                            break
                        }
                    }
                }
                
                let imageNodeLayout = strongSelf.imageNode.asyncLayout()
                var applyImage: (() -> Void)?
                if let imageDimensions = imageDimensions {
                    let boundingSize = CGSize(width: 35.0, height: 35.0)
                    var radius: CGFloat = 2.0
                    var imageSize = imageDimensions.aspectFilled(boundingSize)
                    if isRoundImage {
                        radius = floor(boundingSize.width / 2.0)
                        imageSize.width += 2.0
                        imageSize.height += 2.0
                    }
                    applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: radius), imageSize: imageSize, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets()))
                }
                
                var mediaUpdated = false
                if let updatedMediaReference = updatedMediaReference, let previousMediaReference = strongSelf.previousMediaReference {
                    mediaUpdated = !updatedMediaReference.media.isEqual(to: previousMediaReference.media)
                } else if (updatedMediaReference != nil) != (strongSelf.previousMediaReference != nil) {
                    mediaUpdated = true
                }
                strongSelf.previousMediaReference = updatedMediaReference
                
                let hasSpoiler = message?.attributes.contains(where: { $0 is MediaSpoilerMessageAttribute }) ?? false
                
                var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
                if mediaUpdated {
                    if let updatedMediaReference = updatedMediaReference, imageDimensions != nil {
                        if let imageReference = updatedMediaReference.concrete(TelegramMediaImage.self) {
                            updateImageSignal = chatMessagePhotoThumbnail(account: context.account, userLocation: (message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, photoReference: imageReference, blurred: hasSpoiler)
                        } else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) {
                            if fileReference.media.isVideo {
                                updateImageSignal = chatMessageVideoThumbnail(account: context.account, userLocation: (message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, fileReference: fileReference, blurred: hasSpoiler)
                            } else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
                                updateImageSignal = chatWebpageSnippetFile(account: context.account, userLocation: (message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, mediaReference: fileReference.abstract, representation: iconImageRepresentation)
                            }
                        }
                    } else {
                        updateImageSignal = .single({ _ in return nil })
                    }
                }
                
                var titleText: [CompositeTextNode.Component] = []
                if let peer = message?.peers[strongSelf.messageId.peerId] as? TelegramChannel, case .broadcast = peer.info {
                    let icon: UIImage?
                    icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon")?.withRenderingMode(.alwaysTemplate)
                    
                    //TODO:localize
                    if let _ = strongSelf.quote {
                        if let icon {
                            let string = "Reply to Quote by "
                            titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor))]
                            titleText.append(.icon(icon))
                            titleText.append(.text(NSAttributedString(string: peer.debugDisplayTitle, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor)))
                        }
                    } else {
                        if let icon {
                            let string = "Reply to "
                            titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor))]
                            titleText.append(.icon(icon))
                            titleText.append(.text(NSAttributedString(string: peer.debugDisplayTitle, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor)))
                        }
                    }
                } else {
                    if let _ = strongSelf.quote {
                        //TODO:localize
                        let string = "Reply to Quote by \(authorName)"
                        titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor))]
                    } else {
                        let string = strongSelf.strings.Conversation_ReplyMessagePanelTitle(authorName).string
                        titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor))]
                    }
                    
                    if strongSelf.messageId.peerId != strongSelf.chatPeerId {
                        if let peer = message?.peers[strongSelf.messageId.peerId], (peer is TelegramChannel || peer is TelegramGroup) {
                            let icon: UIImage?
                            if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
                                icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon")?.withRenderingMode(.alwaysTemplate)
                            } else {
                                icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextGroupIcon")?.withRenderingMode(.alwaysTemplate)
                            }
                            if let iconImage = generateTintedImage(image: icon, color: strongSelf.theme.chat.inputPanel.panelControlAccentColor) {
                                titleText.append(.icon(iconImage))
                                titleText.append(.text(NSAttributedString(string: peer.debugDisplayTitle, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
                            }
                        }
                    }
                }
                
                strongSelf.textNode.attributedText = messageText
                
                if let quote = strongSelf.quote {
                    let textColor = strongSelf.theme.chat.inputPanel.primaryTextColor
                    let quoteText = stringWithAppliedEntities(trimToLineCount(quote.text, lineCount: 1), entities: quote.entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message)
                    
                    strongSelf.textNode.attributedText = quoteText
                }
                
                strongSelf.titleNode.components = titleText
                                
                let headerString: String
                if let message = message, message.flags.contains(.Incoming), let author = message.author {
                    headerString = "Reply to message. From: \(EnginePeer(author).displayTitle(strings: strings, displayOrder: nameDisplayOrder))"
                } else if let message = message, !message.flags.contains(.Incoming) {
                    headerString = "Reply to your message"
                } else {
                    headerString = "Reply to message"
                }
                strongSelf.actionArea.accessibilityLabel = "\(headerString).\n\(text)"
                
                if let applyImage = applyImage {
                    applyImage()
                    strongSelf.imageNode.isHidden = false
                } else {
                    strongSelf.imageNode.isHidden = true
                }
                
                if let updateImageSignal = updateImageSignal {
                    strongSelf.imageNode.setSignal(updateImageSignal)
                }
                
                if let (size, inset, interfaceState) = strongSelf.validLayout {
                    strongSelf.updateState(size: size, inset: inset, interfaceState: interfaceState)
                }
                
                let _ = (ApplicationSpecificNotice.getChatReplyOptionsTip(accountManager: strongSelf.context.sharedContext.accountManager)
                |> deliverOnMainQueue).start(next: { [weak self] count in
                    if let strongSelf = self, count < 3 {
                        Queue.mainQueue().after(3.0) {
                            if let snapshotView = strongSelf.textNode.view.snapshotContentTree() {
                                let text: String
                                //TODO:localize
                                if let (size, _, _) = strongSelf.validLayout, size.width > 320.0 {
                                    text = "Tap here for reply options"
                                } else {
                                    text = "Tap here for forwarding options"
                                }
                                strongSelf.textIsOptions = true
                                
                                strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor)
                                
                                strongSelf.view.addSubview(snapshotView)
                                
                                if let (size, inset, interfaceState) = strongSelf.validLayout {
                                    strongSelf.updateState(size: size, inset: inset, interfaceState: interfaceState)
                                }
                                
                                strongSelf.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
                                snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
                                    snapshotView?.removeFromSuperview()
                                })
                            }
                            
                            let _ = ApplicationSpecificNotice.incrementChatReplyOptionsTip(accountManager: strongSelf.context.sharedContext.accountManager).start()
                        }
                    }
                })
            }
        }))
    }
    
    deinit {
        self.messageDisposable.dispose()
    }
    
    override public func didLoad() {
        super.didLoad()
        
        self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
    }
    
    override public func animateIn() {
        self.iconView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
    }
    
    override public func animateOut() {
        self.iconView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false)
    }
    
    override public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
        self.updateThemeAndStrings(theme: theme, strings: strings, force: false)
    }
        
    private func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, force: Bool) {
        if self.theme !== theme || force {
            self.theme = theme
            
            self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: [])
            
            self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
            self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor
            
            self.titleNode.components = self.titleNode.components.map { item in
                switch item {
                case let .text(text):
                    let updatedText = NSMutableAttributedString(attributedString: text)
                    updatedText.addAttribute(.foregroundColor, value: theme.chat.inputPanel.panelControlAccentColor, range: NSRange(location: 0, length: updatedText.length))
                    return .text(updatedText)
                case let .icon(icon):
                    if let iconImage = generateTintedImage(image: icon, color: theme.chat.inputPanel.panelControlAccentColor) {
                        return .icon(iconImage)
                    } else {
                        return .icon(icon)
                    }
                }
            }
            
            if let text = self.textNode.attributedText {
                let updatedText = NSMutableAttributedString(attributedString: text)
                updatedText.addAttribute(.foregroundColor, value: self.textIsOptions ? self.theme.chat.inputPanel.secondaryTextColor : self.theme.chat.inputPanel.primaryTextColor, range: NSRange(location: 0, length: updatedText.length))
                self.textNode.attributedText = updatedText
            }
            self.textNode.spoilerColor = self.theme.chat.inputPanel.secondaryTextColor
            
            if let (size, inset, interfaceState) = self.validLayout {
                self.updateState(size: size, inset: inset, interfaceState: interfaceState)
            }
        }
    }
    
    override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
        return CGSize(width: constrainedSize.width, height: 45.0)
    }
    
    override public func updateState(size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState) {
        self.validLayout = (size, inset, interfaceState)
        
        let bounds = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 45.0))
        let leftInset: CGFloat = 55.0 + inset
        let textLineInset: CGFloat = 10.0
        let rightInset: CGFloat = 55.0
        let textRightInset: CGFloat = 20.0
        
        let closeButtonSize = CGSize(width: 44.0, height: bounds.height)
        let closeButtonFrame = CGRect(origin: CGPoint(x: bounds.width - closeButtonSize.width - inset, y: 2.0), size: closeButtonSize)
        self.closeButton.frame = closeButtonFrame
        
        self.actionArea.frame = CGRect(origin: CGPoint(x: leftInset, y: 2.0), size: CGSize(width: closeButtonFrame.minX - leftInset, height: bounds.height))

        if self.lineNode.supernode == self {
            self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0))
        }
        
        if let icon = self.iconView.image {
            self.iconView.frame = CGRect(origin: CGPoint(x: 7.0 + inset, y: 10.0), size: icon.size)
        }
        
        var imageTextInset: CGFloat = 0.0
        if !self.imageNode.isHidden {
            imageTextInset = 9.0 + 35.0
        }
        if self.imageNode.supernode == self {
            self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0))
        }
        
        let titleSize = self.titleNode.update(constrainedSize: CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
        if self.titleNode.supernode == self {
            self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize)
        }
        
        let textSize = self.textNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
        let textFrame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset - self.textNode.insets.left, y: 25.0 - self.textNode.insets.top), size: textSize)
        if self.textNode.supernode == self {
            self.textNode.frame = textFrame
        }
    }
    
    @objc private func closePressed() {
        if let dismiss = self.dismiss {
            dismiss()
        }
    }
    
    private var previousTapTimestamp: Double?
    @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
        if case .ended = recognizer.state {
            let timestamp = CFAbsoluteTimeGetCurrent()
            if let previousTapTimestamp = self.previousTapTimestamp, previousTapTimestamp + 1.0 > timestamp {
                return
            }
            self.previousTapTimestamp = CFAbsoluteTimeGetCurrent()
            self.interfaceInteraction?.presentReplyOptions(self)
            Queue.mainQueue().after(1.5) {
                self.updateThemeAndStrings(theme: self.theme, strings: self.strings, force: true)
            }
            
            let _ = ApplicationSpecificNotice.incrementChatReplyOptionsTip(accountManager: self.context.sharedContext.accountManager, count: 3).start()
        }
    }
    
    /*@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
        if case .ended = recognizer.state {
            self.interfaceInteraction?.navigateToMessage(self.messageId, false, true, .generic)
        }
    }*/
}