import Foundation
import UIKit
import AsyncDisplayKit
import Display
import TelegramCore
import Postbox
import SwiftSignalKit
import TelegramPresentationData
import AvatarNode
import PeerOnlineMarkerNode
import LegacyComponents
import ContextUI
import LocalizedPeerData
import AccountContext
import CheckNode
import ComponentFlow
import EmojiStatusComponent
import AnimationCache
import MultiAnimationRenderer
import TelegramUIPreferences

private let avatarFont = avatarPlaceholderFont(size: 24.0)
private let textFont = Font.regular(11.0)

public final class SelectablePeerNodeTheme {
    let textColor: UIColor
    let secretTextColor: UIColor
    let selectedTextColor: UIColor
    let checkBackgroundColor: UIColor
    let checkFillColor: UIColor
    let checkColor: UIColor
    let avatarPlaceholderColor: UIColor
    
    public init(textColor: UIColor, secretTextColor: UIColor, selectedTextColor: UIColor, checkBackgroundColor: UIColor, checkFillColor: UIColor, checkColor: UIColor, avatarPlaceholderColor: UIColor) {
        self.textColor = textColor
        self.secretTextColor = secretTextColor
        self.selectedTextColor = selectedTextColor
        self.checkBackgroundColor = checkBackgroundColor
        self.checkFillColor = checkFillColor
        self.checkColor = checkColor
        self.avatarPlaceholderColor = avatarPlaceholderColor
    }
    
    public func isEqual(to: SelectablePeerNodeTheme) -> Bool {
        if self === to {
            return true
        }
        if !self.textColor.isEqual(to.textColor) {
            return false
        }
        if !self.secretTextColor.isEqual(to.secretTextColor) {
            return false
        }
        if !self.selectedTextColor.isEqual(to.selectedTextColor) {
            return false
        }
        if !self.checkBackgroundColor.isEqual(to.checkBackgroundColor) {
            return false
        }
        if !self.checkFillColor.isEqual(to.checkFillColor) {
            return false
        }
        if !self.checkColor.isEqual(to.checkColor) {
            return false
        }
        if !self.avatarPlaceholderColor.isEqual(to.avatarPlaceholderColor) {
            return false
        }
        return true
    }
}

public final class SelectablePeerNode: ASDisplayNode {
    private let contextContainer: ContextControllerSourceNode
    private let avatarSelectionNode: ASImageNode
    private let avatarNodeContainer: ASDisplayNode
    private let avatarNode: AvatarNode
    private var avatarBadgeBackground: UIImageView?
    private var avatarBadge: UIImageView?
    private let onlineNode: PeerOnlineMarkerNode
    private var checkNode: CheckNode?
    private let textNode: ImmediateTextNode
    
    private let iconView: ComponentView<Empty>

    public var toggleSelection: ((Bool) -> Void)?
    public var contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? {
        didSet {
            self.contextContainer.isGestureEnabled = self.contextAction != nil
        }
    }
    
    private var currentSelected = false
    
    private var peer: EngineRenderedPeer?
    private var requiresPremiumForMessaging: Bool = false
    
    public var compact = false
    
    public var theme: SelectablePeerNodeTheme = SelectablePeerNodeTheme(textColor: .black, secretTextColor: .green, selectedTextColor: .blue, checkBackgroundColor: .white, checkFillColor: .blue, checkColor: .white, avatarPlaceholderColor: .white) {
        didSet {
            if !self.theme.isEqual(to: oldValue) {
                if let peer = self.peer, let mainPeer = peer.chatMainPeer {
                    self.textNode.attributedText = NSAttributedString(string: mainPeer.debugDisplayTitle, font: textFont, textColor: self.currentSelected ? self.theme.selectedTextColor : (peer.peerId.namespace == Namespaces.Peer.SecretChat ? self.theme.secretTextColor : self.theme.textColor), paragraphAlignment: .center)
                }
            }
        }
    }
    
    override public init() {
        self.contextContainer = ContextControllerSourceNode()
        self.contextContainer.isGestureEnabled = false
        
        self.avatarNodeContainer = ASDisplayNode()
        
        self.avatarSelectionNode = ASImageNode()
        self.avatarSelectionNode.isLayerBacked = true
        self.avatarSelectionNode.displayWithoutProcessing = true
        self.avatarSelectionNode.displaysAsynchronously = false
        self.avatarSelectionNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 60.0, height: 60.0))
        self.avatarSelectionNode.alpha = 0.0
        
        self.avatarNode = AvatarNode(font: avatarFont)
        self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 60.0, height: 60.0))
        
        self.textNode = ImmediateTextNode()
        self.textNode.isUserInteractionEnabled = false
        self.textNode.displaysAsynchronously = false
        
        self.onlineNode = PeerOnlineMarkerNode()
        
        self.iconView = ComponentView<Empty>()
        
        super.init()
        
        self.addSubnode(self.contextContainer)
        self.avatarNodeContainer.addSubnode(self.avatarSelectionNode)
        self.avatarNodeContainer.addSubnode(self.avatarNode)
        self.contextContainer.addSubnode(self.avatarNodeContainer)
        self.contextContainer.addSubnode(self.textNode)
        self.contextContainer.addSubnode(self.onlineNode)
        
        self.contextContainer.activated = { [weak self] gesture, _ in
            guard let strongSelf = self, let contextAction = strongSelf.contextAction else {
                gesture.cancel()
                return
            }
            contextAction(strongSelf.contextContainer, gesture, nil)
        }
    }
    
    public func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, requiresPremiumForMessaging: Bool, customTitle: String? = nil, iconId: Int64? = nil, iconColor: Int32? = nil, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) {
        self.setup(
            accountPeerId: context.account.peerId,
            postbox: context.account.postbox,
            network: context.account.network,
            energyUsageSettings: context.sharedContext.energyUsageSettings,
            contentSettings: context.currentContentSettings.with { $0 },
            animationCache: context.animationCache,
            animationRenderer: context.animationRenderer,
            resolveInlineStickers: { fileIds in
                return context.engine.stickers.resolveInlineStickers(fileIds: fileIds)
            },
            theme: theme,
            strings: strings,
            peer: peer,
            requiresPremiumForMessaging: requiresPremiumForMessaging,
            customTitle: customTitle,
            iconId: iconId,
            iconColor: iconColor,
            online: online,
            numberOfLines: numberOfLines,
            synchronousLoad: synchronousLoad
        )
    }
    
    public func setupStoryRepost(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, theme: PresentationTheme, strings: PresentationStrings, synchronousLoad: Bool, isMessage: Bool) {
        self.peer = nil
        
        self.textNode.maximumNumberOfLines = 2
        self.textNode.attributedText = NSAttributedString(string: isMessage ? strings.Share_RepostToStory : strings.Share_RepostStory, font: textFont, textColor: self.theme.textColor, paragraphAlignment: .center)
        self.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: ContentSettings.default, theme: theme, peer: nil, overrideImage: .repostIcon, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: .round, synchronousLoad: synchronousLoad)
        
        self.avatarNode.playRepostAnimation()
    }
    
    public func setup(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, energyUsageSettings: EnergyUsageSettings, contentSettings: ContentSettings, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, requiresPremiumForMessaging: Bool, customTitle: String? = nil, iconId: Int64? = nil, iconColor: Int32? = nil, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) {
        let isFirstTime = self.peer == nil
        self.peer = peer
        guard let mainPeer = peer.chatMainPeer else {
            return
        }
        
        self.requiresPremiumForMessaging = requiresPremiumForMessaging
        
        let defaultColor: UIColor
        if requiresPremiumForMessaging {
            defaultColor = self.theme.textColor.withMultipliedAlpha(0.4)
        } else {
            defaultColor = peer.peerId.namespace == Namespaces.Peer.SecretChat ? self.theme.secretTextColor : self.theme.textColor
        }
        
        var isForum = false
        if let peer = peer.chatMainPeer, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
            isForum = true
        }
        
        let text: String
        var overrideImage: AvatarNodeImageOverride?
        if peer.peerId == accountPeerId {
            text = self.compact ? strings.DeleteAccount_SavedMessages : strings.DialogList_SavedMessages
            overrideImage = .savedMessagesIcon
        } else if peer.peerId.isReplies {
            text = strings.DialogList_Replies
            overrideImage = .repliesIcon
        } else {
            text = mainPeer.compactDisplayTitle
            if mainPeer.isDeleted {
                overrideImage = .deletedIcon
            }
        }
        self.textNode.maximumNumberOfLines = numberOfLines
        self.textNode.attributedText = NSAttributedString(string: customTitle ?? text, font: textFont, textColor: self.currentSelected ? self.theme.selectedTextColor : defaultColor, paragraphAlignment: .center)
        self.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: contentSettings, theme: theme, peer: mainPeer, overrideImage: overrideImage, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: isForum ? .roundedRect : .round, synchronousLoad: synchronousLoad)
        
        if requiresPremiumForMessaging {
            let avatarBadgeBackground: UIImageView
            if let current = self.avatarBadgeBackground {
                avatarBadgeBackground = current
            } else {
                avatarBadgeBackground = UIImageView()
                avatarBadgeBackground.image = PresentationResourcesChatList.shareAvatarPremiumLockBadgeBackground(theme)
                avatarBadgeBackground.tintColor = theme.chatList.itemBackgroundColor
                self.avatarBadgeBackground = avatarBadgeBackground
                self.avatarNode.view.addSubview(avatarBadgeBackground)
            }
            
            let avatarBadge: UIImageView
            if let current = self.avatarBadge {
                avatarBadge = current
            } else {
                avatarBadge = UIImageView()
                avatarBadge.image = PresentationResourcesChatList.shareAvatarPremiumLockBadge(theme)
                self.avatarBadge = avatarBadge
                self.avatarNode.view.addSubview(avatarBadge)
            }
            
            let avatarFrame = self.avatarNode.frame
            let badgeFrame = CGRect(origin: CGPoint(x: avatarFrame.width - 20.0, y: avatarFrame.height - 20.0), size: CGSize(width: 20.0, height: 20.0))
            let badgeBackgroundFrame = badgeFrame.insetBy(dx: -2.0 + UIScreenPixel, dy: -2.0 + UIScreenPixel)
            
            avatarBadgeBackground.frame = badgeBackgroundFrame
            avatarBadge.frame = badgeFrame
        } else {
            if let avatarBadgeBackground = self.avatarBadgeBackground {
                self.avatarBadgeBackground = nil
                avatarBadgeBackground.removeFromSuperview()
            }
            if let avatarBadge = self.avatarBadge {
                self.avatarBadge = nil
                avatarBadge.removeFromSuperview()
            }
        }
        
        let onlineLayout = self.onlineNode.asyncLayout()
        let (onlineSize, onlineApply) = onlineLayout(online, false)
        let _ = onlineApply(!isFirstTime)
        
        self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(theme, state: .panel), color: nil, transition: .immediate)
        self.onlineNode.frame = CGRect(origin: CGPoint(), size: onlineSize)
        
        let iconContent: EmojiStatusComponent.Content?
        if let fileId = iconId {
            iconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.actionSheet.disabledActionTextColor, themeColor: theme.actionSheet.primaryTextColor, loopMode: .count(0))
        } else if let customTitle = customTitle {
            iconContent = .topic(title: String(customTitle.prefix(1)), color: iconColor ?? 0, size: CGSize(width: 32.0, height: 32.0))
        } else {
            iconContent = nil
        }
                
        if let iconContent = iconContent {
            let iconSize = self.iconView.update(
                transition: .easeInOut(duration: 0.2),
                component: AnyComponent(EmojiStatusComponent(
                    postbox: postbox,
                    energyUsageSettings: energyUsageSettings,
                    resolveInlineStickers: resolveInlineStickers,
                    animationCache: animationCache,
                    animationRenderer: animationRenderer,
                    content: iconContent,
                    isVisibleForAnimations: true,
                    action: nil
                )),
                environment: {},
                containerSize: CGSize(width: 18.0, height: 18.0)
            )
            
            if let iconComponentView = self.iconView.view {
                if iconComponentView.superview == nil {
                    self.view.addSubview(iconComponentView)
                }
                iconComponentView.frame = CGRect(origin: .zero, size: iconSize)
            }
        } else if let iconComponentView = self.iconView.view {
            iconComponentView.removeFromSuperview()
        }
        
        self.setNeedsLayout()
    }
    
    public func updateSelection(selected: Bool, animated: Bool) {
        if selected != self.currentSelected {
            self.currentSelected = selected
            
            if let attributedText = self.textNode.attributedText {
                self.textNode.attributedText = NSAttributedString(string: attributedText.string, font: textFont, textColor: selected ? self.theme.selectedTextColor : (self.peer?.peerId.namespace == Namespaces.Peer.SecretChat ? self.theme.secretTextColor : self.theme.textColor), paragraphAlignment: .center)
            }
            
            var isForum = false
            if let peer = self.peer?.chatMainPeer, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
                isForum = true
            }
            
            if selected {
                self.avatarNode.transform = CATransform3DMakeScale(0.866666, 0.866666, 1.0)
                self.avatarSelectionNode.alpha = 1.0
                self.avatarSelectionNode.image = generateImage(CGSize(width: 60.0 + 4.0, height: 60.0 + 4.0), rotatedContext: { size, context in
                    context.clear(CGRect(origin: CGPoint(), size: size))
                    let bounds = CGRect(origin: .zero, size: size)
                    if isForum {
                        context.setStrokeColor(self.theme.selectedTextColor.cgColor)
                        context.setLineWidth(2.0)
                        context.addPath(UIBezierPath(roundedRect: bounds.insetBy(dx: 1.0, dy: 1.0), cornerRadius: floorToScreenPixels(bounds.size.width * 0.26)).cgPath)
                        context.strokePath()
                    } else {
                        context.setFillColor(self.theme.selectedTextColor.cgColor)
                        context.fillEllipse(in: bounds)
                        context.setBlendMode(.copy)
                        context.setFillColor(UIColor.clear.cgColor)
                        context.fillEllipse(in: bounds.insetBy(dx: 2.0, dy: 2.0))
                    }
                })
                if animated {
                    self.avatarNode.layer.animateScale(from: 1.0, to: 0.866666, duration: 0.2, timingFunction: kCAMediaTimingFunctionSpring)
                    self.avatarSelectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
                }
            } else {
                self.avatarNode.transform = CATransform3DIdentity
                self.avatarSelectionNode.alpha = 0.0
                if animated {
                    self.avatarNode.layer.animateScale(from: 0.866666, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
                    self.avatarSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.28, completion: { [weak avatarSelectionNode] _ in
                        avatarSelectionNode?.image = nil
                    })
                } else {
                    self.avatarSelectionNode.image = nil
                }
            }
            
            if selected {
                if self.checkNode == nil {
                    let checkNode = CheckNode(theme: CheckNodeTheme(backgroundColor: self.theme.checkFillColor, strokeColor: self.theme.checkColor, borderColor: self.theme.checkBackgroundColor, overlayBorder: true, hasInset: false, hasShadow: false, filledBorder: true, borderWidth: 2.0))
                    self.checkNode = checkNode
                    checkNode.isUserInteractionEnabled = false
                    self.addSubnode(checkNode)
                    
                    let avatarFrame = self.avatarNode.frame
                    let checkSize = CGSize(width: 24.0, height: 24.0)
                    checkNode.frame = CGRect(origin: CGPoint(x: avatarFrame.maxX - 10.0, y: avatarFrame.maxY - 18.0), size: checkSize)
                    checkNode.setSelected(true, animated: animated)
                }
            } else if let checkNode = self.checkNode {
                self.checkNode = nil
                checkNode.setSelected(false, animated: animated)
            }
            self.setNeedsLayout()
        }
    }
    
    override public func didLoad() {
        super.didLoad()
        
        self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
    }
    
    @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
        if case .ended = recognizer.state {
            self.toggleSelection?(self.requiresPremiumForMessaging)
        }
    }
    
    override public func layout() {
        super.layout()
        
        let bounds = self.bounds
        
        self.contextContainer.frame = bounds
        
        self.avatarNodeContainer.frame = CGRect(origin: CGPoint(x: floor((bounds.size.width - 60.0) / 2.0), y: 4.0), size: CGSize(width: 60.0, height: 60.0))
        
        let iconSize = CGSize(width: 18.0, height: 18.0)
        let textSize = self.textNode.updateLayout(bounds.size)
        var totalWidth = textSize.width
        var leftOrigin = floorToScreenPixels((bounds.width - textSize.width) / 2.0)
        if let iconView = self.iconView.view, iconView.superview != nil {
            totalWidth += iconView.frame.width + 2.0
            leftOrigin = floorToScreenPixels((bounds.width - totalWidth) / 2.0)
            iconView.frame = CGRect(origin: CGPoint(x: leftOrigin, y: 4.0 + 60.0 + 4.0 + floorToScreenPixels((textSize.height - iconSize.height) / 2.0)), size: iconSize)
            leftOrigin += iconSize.width + 2.0
        }
        self.textNode.frame = CGRect(origin: CGPoint(x: leftOrigin, y: 4.0 + 60.0 + 4.0), size: textSize)
        
        let avatarFrame = self.avatarNode.frame
        let avatarContainerFrame = self.avatarNodeContainer.frame
        
        self.onlineNode.frame = CGRect(origin: CGPoint(x: avatarContainerFrame.maxX - self.onlineNode.frame.width - 2.0, y: avatarContainerFrame.maxY - self.onlineNode.frame.height - 2.0), size: self.onlineNode.frame.size)
        
        if let checkNode = self.checkNode {
            let checkSize = CGSize(width: 24.0, height: 24.0)
            checkNode.frame = CGRect(origin: CGPoint(x: avatarFrame.maxX - 10.0, y: avatarFrame.maxY - 18.0), size: checkSize)
        }
    }
}