import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import ContextUI
import ChatPresentationInterfaceState
import ChatMessageBackground
import ChatControllerInteraction
import AccountContext
import ChatTextInputMediaRecordingButton
import ChatSendButtonRadialStatusNode
import ChatSendMessageActionUI
import ComponentFlow

private final class EffectBadgeView: UIView {
    private let context: AccountContext
    private var currentEffectId: Int64?
    
    private let backgroundView: UIImageView
    
    private var theme: PresentationTheme?
    
    private var effect: AvailableMessageEffects.MessageEffect?
    private var effectIcon: ComponentView<Empty>?
    
    private let effectDisposable = MetaDisposable()
    
    init(context: AccountContext) {
        self.context = context
        self.backgroundView = UIImageView()
        
        super.init(frame: CGRect())
        
        self.isUserInteractionEnabled = false
        
        self.addSubview(self.backgroundView)
    }
    
    required init(coder: NSCoder) {
        preconditionFailure()
    }
    
    deinit {
        self.effectDisposable.dispose()
    }
    
    func update(size: CGSize, theme: PresentationTheme, effectId: Int64) {
        if self.theme !== theme {
            self.theme = theme
            self.backgroundView.image = generateFilledCircleImage(diameter: size.width, color: theme.list.plainBackgroundColor, strokeColor: nil, strokeWidth: nil, backgroundColor: nil)
            self.backgroundView.layer.shadowPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: size)).cgPath
            self.backgroundView.layer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor
            self.backgroundView.layer.shadowOpacity = 0.14
            self.backgroundView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
            self.backgroundView.layer.shadowRadius = 1.0
        }
        
        self.backgroundView.frame = CGRect(origin: CGPoint(), size: size)
        
        if self.currentEffectId != effectId {
            self.currentEffectId = effectId
            
            let messageEffect = self.context.engine.stickers.availableMessageEffects()
            |> take(1)
            |> map { availableMessageEffects -> AvailableMessageEffects.MessageEffect? in
                guard let availableMessageEffects else {
                    return nil
                }
                for messageEffect in availableMessageEffects.messageEffects {
                    if messageEffect.id == effectId || messageEffect.effectSticker.fileId.id == effectId {
                        return messageEffect
                    }
                }
                return nil
            }
            
            self.effectDisposable.set((messageEffect |> deliverOnMainQueue).start(next: { [weak self] effect in
                guard let self, let effect else {
                    return
                }
                self.effect = effect
                self.updateIcon()
            }))
        }
    }
    
    private func updateIcon() {
        guard let effect else {
            return
        }
        
        let effectIcon: ComponentView<Empty>
        if let current = self.effectIcon {
            effectIcon = current
        } else {
            effectIcon = ComponentView()
            self.effectIcon = effectIcon
        }
        let effectIconContent: ChatSendMessageScreenEffectIcon.Content
        if let staticIcon = effect.staticIcon {
            effectIconContent = .file(staticIcon)
        } else {
            effectIconContent = .text(effect.emoticon)
        }
        let effectIconSize = effectIcon.update(
            transition: .immediate,
            component: AnyComponent(ChatSendMessageScreenEffectIcon(
                context: self.context,
                content: effectIconContent
            )),
            environment: {},
            containerSize: CGSize(width: 8.0, height: 8.0)
        )
        
        let size = CGSize(width: 16.0, height: 16.0)
        if let effectIconView = effectIcon.view {
            if effectIconView.superview == nil {
                self.addSubview(effectIconView)
            }
            effectIconView.frame = CGRect(origin: CGPoint(x: floor((size.width - effectIconSize.width) * 0.5), y: floor((size.height - effectIconSize.height) * 0.5)), size: effectIconSize)
        }
    }
}

final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessageActionSheetControllerSourceSendButtonNode {
    private let context: AccountContext
    private let presentationContext: ChatPresentationContext?
    private let strings: PresentationStrings
    
    let micButton: ChatTextInputMediaRecordingButton
    let sendContainerNode: ASDisplayNode
    let backdropNode: ChatMessageBubbleBackdrop
    let backgroundNode: ASDisplayNode
    let sendButton: HighlightTrackingButtonNode
    var sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode?
    var sendButtonHasApplyIcon = false
    var animatingSendButton = false
    let expandMediaInputButton: HighlightableButtonNode
    private var effectBadgeView: EffectBadgeView?
    
    var sendButtonLongPressed: ((ASDisplayNode, ContextGesture) -> Void)?
    
    private var gestureRecognizer: ContextGesture?
    var sendButtonLongPressEnabled = false {
        didSet {
            self.gestureRecognizer?.isEnabled = self.sendButtonLongPressEnabled
        }
    }
    
    private var micButtonPointerInteraction: PointerInteraction?
    private var sendButtonPointerInteraction: PointerInteraction?
    
    private var validLayout: CGSize?
    
    init(context: AccountContext, presentationInterfaceState: ChatPresentationInterfaceState, presentationContext: ChatPresentationContext?, presentController: @escaping (ViewController) -> Void) {
        self.context = context
        self.presentationContext = presentationContext
        let theme = presentationInterfaceState.theme
        let strings = presentationInterfaceState.strings
        self.strings = strings
         
        self.micButton = ChatTextInputMediaRecordingButton(context: context, theme: theme, pause: true, strings: strings, presentController: presentController)
        
        self.sendContainerNode = ASDisplayNode()
        self.sendContainerNode.layer.allowsGroupOpacity = true
        
        self.backgroundNode = ASDisplayNode()
        self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor
        self.backgroundNode.clipsToBounds = true
        self.backdropNode = ChatMessageBubbleBackdrop()
        self.sendButton = HighlightTrackingButtonNode(pointerStyle: nil)
        
        self.expandMediaInputButton = HighlightableButtonNode(pointerStyle: .circle(36.0))
        
        super.init()
        
        self.isAccessibilityElement = true
        self.accessibilityTraits = [.button, .notEnabled]
        
        self.sendButton.highligthedChanged = { [weak self] highlighted in
            if let strongSelf = self {
                if !strongSelf.sendButtonLongPressEnabled {
                    if highlighted {
                        strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
                        strongSelf.sendContainerNode.alpha = 0.4
                    } else {
                        strongSelf.sendContainerNode.alpha = 1.0
                        strongSelf.sendContainerNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
                    }
                } else {
                    if highlighted {
                        strongSelf.sendContainerNode.layer.animateScale(from: 1.0, to: 0.75, duration: 0.4, removeOnCompletion: false)
                    } else if let presentationLayer = strongSelf.sendContainerNode.layer.presentation() {
                        strongSelf.sendContainerNode.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
                    }
                }
            }
        }
        
        self.micButton.layer.allowsGroupOpacity = true
        self.view.addSubview(self.micButton)
            
        self.addSubnode(self.sendContainerNode)
        self.sendContainerNode.addSubnode(self.backgroundNode)
        if let presentationContext = presentationContext {
            let graphics = PresentationResourcesChat.principalGraphics(theme: theme, wallpaper: presentationInterfaceState.chatWallpaper, bubbleCorners: presentationInterfaceState.bubbleCorners)
            self.backdropNode.setType(type: .outgoing(.None), theme: ChatPresentationThemeData(theme: theme, wallpaper: presentationInterfaceState.chatWallpaper), essentialGraphics: graphics, maskMode: true, backgroundNode: presentationContext.backgroundNode)
            self.backgroundNode.addSubnode(self.backdropNode)
        }
        self.sendContainerNode.addSubnode(self.sendButton)
        self.addSubnode(self.expandMediaInputButton)
    }
    
    override func didLoad() {
        super.didLoad()
        
        let gestureRecognizer = ContextGesture(target: nil, action: nil)
        self.gestureRecognizer = gestureRecognizer
        self.sendButton.view.addGestureRecognizer(gestureRecognizer)
        gestureRecognizer.activated = { [weak self] recognizer, _ in
            guard let strongSelf = self else {
                return
            }
            strongSelf.sendButtonLongPressed?(strongSelf, recognizer)
        }
        
        self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle(36.0))
        self.sendButtonPointerInteraction = PointerInteraction(view: self.sendButton.view, customInteractionView: self.backgroundNode.view, style: .lift)
    }
    
    func updateTheme(theme: PresentationTheme, wallpaper: TelegramWallpaper) {
        self.micButton.updateTheme(theme: theme)
        self.expandMediaInputButton.setImage(PresentationResourcesChat.chatInputPanelExpandButtonImage(theme), for: [])
        
        self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor
        
        if [.day, .night].contains(theme.referenceTheme.baseTheme) && !theme.chat.message.outgoing.bubble.withWallpaper.hasSingleFillColor {
            self.backdropNode.isHidden = false
        } else {
            self.backdropNode.isHidden = true
        }
        
        let graphics = PresentationResourcesChat.principalGraphics(theme: theme, wallpaper: wallpaper, bubbleCorners: .init(mainRadius: 1, auxiliaryRadius: 1, mergeBubbleCorners: false))
        self.backdropNode.setType(type: .outgoing(.None), theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), essentialGraphics: graphics, maskMode: false, backgroundNode: self.presentationContext?.backgroundNode)
    }
    
    private var absoluteRect: (CGRect, CGSize)?
    func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
        let previousContaierSize = self.absoluteRect?.1
        self.absoluteRect = (rect, containerSize)
        self.backdropNode.update(rect: rect, within: containerSize, transition: transition)
        
        if let previousContaierSize, previousContaierSize != containerSize {
            Queue.mainQueue().after(0.2) {
                self.micButton.reset()
            }
        }
    }
    
    func updateLayout(size: CGSize, isMediaInputExpanded: Bool, currentMessageEffectId: Int64?, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
        self.validLayout = size
        transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size))
        self.micButton.layoutItems()
        
        transition.updateFrame(layer: self.sendButton.layer, frame: CGRect(origin: CGPoint(), size: size))
        transition.updateFrame(node: self.sendContainerNode, frame: CGRect(origin: CGPoint(), size: size))
        
        let backgroundSize = CGSize(width: 33.0, height: 33.0)
        let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize)
        transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
        self.backgroundNode.cornerRadius = backgroundSize.width / 2.0
        
        transition.updateFrame(node: self.backdropNode, frame: CGRect(origin: CGPoint(x: -2.0, y: -2.0), size: CGSize(width: size.width + 12.0, height: size.height + 2.0)))
        if let (rect, containerSize) = self.absoluteRect {
            self.backdropNode.update(rect: rect, within: containerSize)
        }
        
        transition.updateFrame(node: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size))
        let expanded = isMediaInputExpanded
        transition.updateSublayerTransformScale(node: self.expandMediaInputButton, scale: CGPoint(x: 1.0, y: expanded ? 1.0 : -1.0))
        
        if let currentMessageEffectId {
            let effectBadgeView: EffectBadgeView
            if let current = self.effectBadgeView {
                effectBadgeView = current
            } else {
                effectBadgeView = EffectBadgeView(context: self.context)
                self.effectBadgeView = effectBadgeView
                self.sendContainerNode.view.addSubview(effectBadgeView)
                
                effectBadgeView.alpha = 0.0
                transition.updateAlpha(layer: effectBadgeView.layer, alpha: 1.0)
            }
            let badgeSize = CGSize(width: 16.0, height: 16.0)
            effectBadgeView.frame = CGRect(origin: CGPoint(x: backgroundFrame.minX + backgroundSize.width + 3.0 - badgeSize.width, y: backgroundFrame.minY + backgroundSize.height + 3.0 - badgeSize.height), size: badgeSize)
            effectBadgeView.update(size: badgeSize, theme: interfaceState.theme, effectId: currentMessageEffectId)
        } else if let effectBadgeView = self.effectBadgeView {
            self.effectBadgeView = nil
            transition.updateAlpha(layer: effectBadgeView.layer, alpha: 0.0, completion: { [weak effectBadgeView] _ in
                effectBadgeView?.removeFromSuperview()
            })
        }
    }
    
    func updateAccessibility() {
        self.accessibilityTraits = .button
        if !self.micButton.alpha.isZero {
            switch self.micButton.mode {
                case .audio:
                    self.accessibilityLabel = self.strings.VoiceOver_Chat_RecordModeVoiceMessage
                    self.accessibilityHint = self.strings.VoiceOver_Chat_RecordModeVoiceMessageInfo
                case .video:
                    self.accessibilityLabel = self.strings.VoiceOver_Chat_RecordModeVideoMessage
                    self.accessibilityHint = self.strings.VoiceOver_Chat_RecordModeVideoMessageInfo
            }
        } else {
            self.accessibilityLabel = self.strings.MediaPicker_Send
            self.accessibilityHint = nil
        }
    }
    
    func makeCustomContents() -> UIView? {
        if self.sendButtonHasApplyIcon || self.effectBadgeView != nil {
            let result = UIView()
            result.frame = self.bounds
            if let copyView = self.sendContainerNode.view.snapshotView(afterScreenUpdates: false) {
                copyView.frame = self.sendContainerNode.frame
                result.addSubview(copyView)
            }
            return result
        }
        return nil
    }
}