import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import AvatarNode
import ContextUI
import TelegramPresentationData
import TelegramUniversalVideoContent
import UniversalMediaPlayer
import GalleryUI
import HierarchyTrackingLayer
import AccountContext
import ComponentFlow
import EmojiStatusComponent
import AvatarVideoNode
import AvatarStoryIndicatorComponent
import ComponentDisplayAdapters

private let normalFont = avatarPlaceholderFont(size: 16.0)
private let smallFont = avatarPlaceholderFont(size: 12.0)

public final class ChatAvatarNavigationNode: ASDisplayNode {
    private var context: AccountContext?
    
    private let containerNode: ContextControllerSourceNode
    public let avatarNode: AvatarNode
    private var avatarVideoNode: AvatarVideoNode?
    
    public private(set) var avatarStoryView: ComponentView<Empty>?
    public var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)?
    
    public let statusView: ComponentView<Empty>
    
    private var cachedDataDisposable = MetaDisposable()
    private var hierarchyTrackingLayer: HierarchyTrackingLayer?
    
    private var trackingIsInHierarchy: Bool = false {
        didSet {
            if self.trackingIsInHierarchy != oldValue {
                Queue.mainQueue().justDispatch {
                    self.updateVideoVisibility()
                }
            }
        }
    }
    
    public var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
    public var contextActionIsEnabled: Bool = false {
        didSet {
            if self.contextActionIsEnabled != oldValue {
                self.containerNode.isGestureEnabled = self.contextActionIsEnabled
            }
        }
    }
        
    override public init() {
        self.containerNode = ContextControllerSourceNode()
        self.containerNode.isGestureEnabled = false
        self.avatarNode = AvatarNode(font: normalFont)
        self.statusView = ComponentView()
        
        super.init()
        
        self.addSubnode(self.containerNode)
        self.containerNode.addSubnode(self.avatarNode)
        
        self.containerNode.activated = { [weak self] gesture, _ in
            guard let strongSelf = self else {
                return
            }
            strongSelf.contextAction?(strongSelf.containerNode, gesture)
        }
        
        self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 37.0, height: 37.0)).offsetBy(dx: 10.0, dy: 1.0)
        self.avatarNode.frame = self.containerNode.bounds
        
        #if DEBUG
        //self.hasUnseenStories = true
        #endif
    }
    
    deinit {
        self.cachedDataDisposable.dispose()
    }
    
    override public func didLoad() {
        super.didLoad()
        self.view.isOpaque = false
    }
    
    public func setStatus(context: AccountContext, content: EmojiStatusComponent.Content) {
        let statusSize = self.statusView.update(
            transition: .immediate,
            component: AnyComponent(EmojiStatusComponent(
                context: context,
                animationCache: context.animationCache,
                animationRenderer: context.animationRenderer,
                content: content,
                isVisibleForAnimations: true,
                action: nil
            )),
            environment: {},
            containerSize: CGSize(width: 32.0, height: 32.0)
        )
        if let statusComponentView = self.statusView.view {
            if statusComponentView.superview == nil {
                self.containerNode.view.addSubview(statusComponentView)
            }
            
            statusComponentView.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - statusSize.width) / 2.0), y: floor((self.containerNode.bounds.height - statusSize.height) / 2.0)), size: statusSize)
        }
        
        self.avatarNode.isHidden = true
    }
    
    public func setPeer(context: AccountContext, theme: PresentationTheme, peer: EnginePeer?, authorOfMessage: MessageReference? = nil, overrideImage: AvatarNodeImageOverride? = nil, emptyColor: UIColor? = nil, clipStyle: AvatarNodeClipStyle = .round, synchronousLoad: Bool = false, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), storeUnrounded: Bool = false) {
        self.context = context
        self.avatarNode.setPeer(context: context, theme: theme, peer: peer, authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, clipStyle: clipStyle, synchronousLoad: synchronousLoad, displayDimensions: displayDimensions, storeUnrounded: storeUnrounded)
        
        if let peer = peer, peer.isPremium {
            self.cachedDataDisposable.set((context.account.postbox.peerView(id: peer.id)
            |> deliverOnMainQueue).start(next: { [weak self] peerView in
                guard let strongSelf = self else {
                    return
                }
                let cachedPeerData = peerView.cachedData as? CachedUserData
                var personalPhoto: TelegramMediaImage?
                var profilePhoto: TelegramMediaImage?
                var isKnown = false
                
                if let cachedPeerData = cachedPeerData {
                    if case let .known(maybePersonalPhoto) = cachedPeerData.personalPhoto {
                        personalPhoto = maybePersonalPhoto
                        isKnown = true
                    }
                    if case let .known(maybePhoto) = cachedPeerData.photo {
                        profilePhoto = maybePhoto
                        isKnown = true
                    }
                }
                
                if isKnown {
                    let photo = personalPhoto ?? profilePhoto
                    if let photo = photo, !photo.videoRepresentations.isEmpty || photo.emojiMarkup != nil {
                        let videoNode: AvatarVideoNode
                        if let current = strongSelf.avatarVideoNode {
                            videoNode = current
                        } else {
                            videoNode = AvatarVideoNode(context: context)
                            strongSelf.avatarNode.contentNode.addSubnode(videoNode)
                            strongSelf.avatarVideoNode = videoNode
                        }
                        videoNode.update(peer: peer, photo: photo, size: CGSize(width: 37.0, height: 37.0))
                        
                        if strongSelf.hierarchyTrackingLayer == nil {
                            let hierarchyTrackingLayer = HierarchyTrackingLayer()
                            hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in
                                guard let strongSelf = self else {
                                    return
                                }
                                strongSelf.trackingIsInHierarchy = true
                            }
                            
                            hierarchyTrackingLayer.didExitHierarchy = { [weak self] in
                                guard let strongSelf = self else {
                                    return
                                }
                                strongSelf.trackingIsInHierarchy = false
                            }
                            strongSelf.hierarchyTrackingLayer = hierarchyTrackingLayer
                            strongSelf.layer.addSublayer(hierarchyTrackingLayer)
                        }
                    } else {
                        if let avatarVideoNode = strongSelf.avatarVideoNode {
                            avatarVideoNode.removeFromSupernode()
                            strongSelf.avatarVideoNode = nil
                        }
                        strongSelf.hierarchyTrackingLayer?.removeFromSuperlayer()
                        strongSelf.hierarchyTrackingLayer = nil
                    }
                    strongSelf.updateVideoVisibility()
                } else {
                    if let photo = peer.largeProfileImage, photo.hasVideo {
                        let _ = context.engine.peers.fetchAndUpdateCachedPeerData(peerId: peer.id).start()
                    }
                }
            }))
        } else {
            self.cachedDataDisposable.set(nil)
            
            self.avatarVideoNode?.removeFromSupernode()
            self.avatarVideoNode = nil
            
            self.hierarchyTrackingLayer?.removeFromSuperlayer()
            self.hierarchyTrackingLayer = nil
        }
    }
    
    public func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme) {
        if let storyData = self.storyData {
            let avatarStoryView: ComponentView<Empty>
            if let current = self.avatarStoryView {
                avatarStoryView = current
            } else {
                avatarStoryView = ComponentView()
                self.avatarStoryView = avatarStoryView
            }
            
            let _ = avatarStoryView.update(
                transition: Transition(transition),
                component: AnyComponent(AvatarStoryIndicatorComponent(
                    hasUnseen: storyData.hasUnseen,
                    hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
                    colors: AvatarStoryIndicatorComponent.Colors(theme: theme),
                    activeLineWidth: 1.0,
                    inactiveLineWidth: 1.0,
                    counters: nil
                )),
                environment: {},
                containerSize: self.avatarNode.bounds.insetBy(dx: 2.0, dy: 2.0).size
            )
            if let avatarStoryComponentView = avatarStoryView.view {
                if avatarStoryComponentView.superview == nil {
                    self.containerNode.view.insertSubview(avatarStoryComponentView, at: 0)
                }
                avatarStoryComponentView.frame = self.avatarNode.frame
            }
        } else {
            if let avatarStoryView = self.avatarStoryView {
                self.avatarStoryView = nil
                avatarStoryView.view?.removeFromSuperview()
            }
        }
    }
    
    override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
        return CGSize(width: 37.0, height: 37.0)
    }
    
    public func onLayout() {
    }

    public final class SnapshotState {
        fileprivate let snapshotView: UIView?

        fileprivate init(snapshotView: UIView?) {
            self.snapshotView = snapshotView
        }
    }

    public func prepareSnapshotState() -> SnapshotState {
        let snapshotView = self.avatarNode.view.snapshotView(afterScreenUpdates: false)
        return SnapshotState(
            snapshotView: snapshotView
        )
    }

    public func animateFromSnapshot(_ snapshotState: SnapshotState) {
        self.avatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
        self.avatarNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true)

        if let snapshotView = snapshotState.snapshotView {
            snapshotView.frame = self.frame
            self.containerNode.view.addSubview(snapshotView)

            snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
                snapshotView?.removeFromSuperview()
            })
            snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
        }
    }
    
    private func updateVideoVisibility() {
        let isVisible = self.trackingIsInHierarchy
        self.avatarVideoNode?.updateVisibility(isVisible)
      
        if let videoNode = self.avatarVideoNode {
            videoNode.updateLayout(size: self.avatarNode.frame.size, cornerRadius: self.avatarNode.frame.size.width / 2.0, transition: .immediate)
            videoNode.frame = self.avatarNode.bounds
        }
    }
}