import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
import Display
import TelegramPresentationData
import ActivityIndicator
import RadialStatusNode
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import AppBundle
import TelegramUniversalVideoContent
import TelegramCore
import Postbox
import AccountContext

private func fileSize(_ path: String, useTotalFileAllocatedSize: Bool = false) -> Int64? {
    if useTotalFileAllocatedSize {
        let url = URL(fileURLWithPath: path)
        if let values = (try? url.resourceValues(forKeys: Set([.isRegularFileKey, .totalFileAllocatedSizeKey]))) {
            if values.isRegularFile ?? false {
                if let fileSize = values.totalFileAllocatedSize {
                    return Int64(fileSize)
                }
            }
        }
    }
    
    var value = stat()
    if stat(path, &value) == 0 {
        return value.st_size
    } else {
        return nil
    }
}

public enum ShareLoadingState {
    case preparing
    case progress(Float)
    case done
}

protocol ShareLoadingContainer: ASDisplayNode {
    var state: ShareLoadingState { get set }
}

public final class ShareLoadingContainerNode: ASDisplayNode, ShareContentContainerNode, ShareLoadingContainer {
    private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
    
    private var theme: PresentationTheme
    private let activityIndicator: ActivityIndicator
    private let statusNode: RadialStatusNode
    private let doneStatusNode: RadialStatusNode
    
    public var state: ShareLoadingState = .preparing {
        didSet {
            switch self.state {
                case .preparing:
                    self.activityIndicator.isHidden = false
                    self.statusNode.isHidden = true
                case let .progress(value):
                    self.activityIndicator.isHidden = true
                    self.statusNode.isHidden = false
                    self.statusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: max(0.12, CGFloat(value)), cancelEnabled: false, animateRotation: true), completion: {})
                case .done:
                    self.activityIndicator.isHidden = true
                    self.statusNode.isHidden = false
                    self.statusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: 1.0, cancelEnabled: false, animateRotation: true), completion: {})
                    self.doneStatusNode.transitionToState(.check(self.theme.actionSheet.controlAccentColor), completion: {})
            }
        }
    }
    
    public init(theme: PresentationTheme, forceNativeAppearance: Bool) {
        self.theme = theme
        self.activityIndicator = ActivityIndicator(type: .custom(theme.actionSheet.controlAccentColor, !forceNativeAppearance ? 22.0 : 50.0, 2.0, forceNativeAppearance))
        self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
        self.doneStatusNode = RadialStatusNode(backgroundNodeColor: .clear)
        
        super.init()
        
        self.addSubnode(self.activityIndicator)
        self.addSubnode(self.statusNode)
        self.addSubnode(self.doneStatusNode)
        self.doneStatusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: 0.0, cancelEnabled: false, animateRotation: true), completion: {})
    }
    
    public func activate() {
    }
    
    public func deactivate() {
    }
    
    public func setEnsurePeerVisibleOnLayout(_ peerId: EnginePeer.Id?) {
    }
    
    public func setDidBeginDragging(_ f: (() -> Void)?) {
    }
    
    public func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
        self.contentOffsetUpdated = f
    }
    
    public func updateTheme(_ theme: PresentationTheme) {
        self.theme = theme
    }
    
    public func updateLayout(size: CGSize, isLandscape: Bool, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) {
        let nodeHeight: CGFloat = 125.0
        
        let indicatorSize = self.activityIndicator.calculateSizeThatFits(size)
        let indicatorFrame = CGRect(origin: CGPoint(x: floor((size.width - indicatorSize.width) / 2.0), y: size.height - nodeHeight + floor((nodeHeight - indicatorSize.height) / 2.0)), size: indicatorSize)
        transition.updateFrame(node: self.activityIndicator, frame: indicatorFrame)
        let statusFrame = indicatorFrame
        transition.updateFrame(node: self.statusNode, frame: statusFrame)
        transition.updateFrame(node: self.doneStatusNode, frame: statusFrame)
        
        self.contentOffsetUpdated?(-size.height + 64.0, transition)
    }
    
    public func updateSelectedPeers(animated: Bool) {
    }
}

public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareContentContainerNode, ShareLoadingContainer {
    private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
    
    private var theme: PresentationTheme
    private let strings: PresentationStrings
    
    private let animationNode: AnimatedStickerNode
    private let doneAnimationNode: AnimatedStickerNode
    private let progressTextNode: ImmediateTextNode
    
    private let progressBackgroundNode: ASDisplayNode
    private let progressForegroundNode: ASDisplayNode
    
    private let animationStatusDisposable = MetaDisposable()
        
    private var progressValue: CGFloat = 0.0
    private var targetProgressValue: CGFloat = 0.0
    private var animator: ConstantDisplayLinkAnimator?
    
    private var randomCompletionStart: CGFloat = .random(in: 0.94...0.97)
    private var isDone: Bool = false
    
    private var startTimestamp: Double?
    
    private var videoNode: UniversalVideoNode?
    
    public var state: ShareLoadingState = .preparing {
        didSet {
            switch self.state {
                case .preparing:
                    break
                case let .progress(value):
                    let currentTimestamp = CACurrentMediaTime()
                    if self.startTimestamp == nil {
                        self.startTimestamp = currentTimestamp
                    } else if let startTimestamp = self.startTimestamp, currentTimestamp - startTimestamp < 1.0, value > 0.5 && value < 0.9 {
                        self.randomCompletionStart = 0.8
                    }
                                
                    self.targetProgressValue = CGFloat(value) * self.randomCompletionStart
                
                    if self.animator == nil {
                        self.animator = ConstantDisplayLinkAnimator(update: { [weak self] in
                            if let strongSelf = self, strongSelf.targetProgressValue > strongSelf.progressValue {
                                let updatedProgress = strongSelf.progressValue + 0.005
                                strongSelf.progressValue = min(1.0, updatedProgress)
                                
                                if strongSelf.progressValue == 1.0 && !strongSelf.isDone {
                                    strongSelf.isDone = true
                                    strongSelf.animator?.invalidate()
                                    
                                    if let snapshotView = strongSelf.progressTextNode.view.snapshotContentTree() {
                                        snapshotView.frame = strongSelf.progressTextNode.frame
                                        strongSelf.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.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -20.0), duration: 0.25, removeOnCompletion: false, additive: true)
                                        
                                        strongSelf.progressTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
                                        strongSelf.progressTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 20.0), to: CGPoint(), duration: 0.25, additive: true)
                                    }
                                }
                                
                                if let (size, isLandscape, bottomInset) = strongSelf.validLayout {
                                    strongSelf.updateLayout(size: size, isLandscape: isLandscape, bottomInset: bottomInset, transition: .immediate)
                                }
                            }
                        })
                        self.animator?.isPaused = false
                    }
                case .done:
                    if let (size, isLandscape, bottomInset) = self.validLayout {
                        self.updateLayout(size: size, isLandscape: isLandscape, bottomInset: bottomInset, transition: .animated(duration: 0.2, curve: .easeInOut))
                    }
                    self.animationNode.stopAtNearestLoop = true
                    self.animationNode.completed = { [weak self] _ in
                        if let strongSelf = self {
                            strongSelf.animationNode.visibility = false
                            strongSelf.doneAnimationNode.visibility = true
                            strongSelf.doneAnimationNode.isHidden = false
                        }
                    }
                    self.animationNode.frameUpdated = { [weak self] index, total in
                        if let strongSelf = self {
                            let progress = min(1.0, CGFloat(index) / CGFloat(total))
                            let delta = 1.0 - strongSelf.randomCompletionStart
                            strongSelf.targetProgressValue = strongSelf.randomCompletionStart + delta * progress * 0.5
                        }
                    }
                    self.doneAnimationNode.frameUpdated = { [weak self] index, total in
                        if let strongSelf = self {
                            let progress = min(1.0, CGFloat(index) / CGFloat(total) * 2.1)
                            let delta = 1.0 - strongSelf.randomCompletionStart
                            strongSelf.targetProgressValue = strongSelf.randomCompletionStart + delta * 0.5 + delta * progress * 0.5
                        }
                    }
                    self.doneAnimationNode.started = { [weak self] in
                        guard let strongSelf = self else {
                            return
                        }
                        strongSelf.animationNode.isHidden = true
                    }
            }
        }
    }
    
    private var elapsedTime: Double = 0.0
    public var completionDuration: Double {
        return self.elapsedTime + 3.0 + 0.15
    }
        
    public init(theme: PresentationTheme, strings: PresentationStrings, forceNativeAppearance: Bool, postbox: Postbox?, environment: ShareControllerEnvironment) {
        self.theme = theme
        self.strings = strings
        
        self.animationNode = DefaultAnimatedStickerNodeImpl()
        self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ShareProgress"), width: 384, height: 384, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
        self.animationNode.visibility = true
        
        self.doneAnimationNode = DefaultAnimatedStickerNodeImpl()
        self.doneAnimationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ShareDone"), width: 384, height: 384, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
        self.doneAnimationNode.visibility = false
        self.doneAnimationNode.isHidden = true
        
        self.progressTextNode = ImmediateTextNode()
        self.progressTextNode.textAlignment = .center
        
        self.progressBackgroundNode = ASDisplayNode()
        self.progressBackgroundNode.backgroundColor = theme.actionSheet.controlAccentColor.withMultipliedAlpha(0.2)
        self.progressBackgroundNode.cornerRadius = 3.0
        
        self.progressForegroundNode = ASDisplayNode()
        self.progressForegroundNode.backgroundColor = theme.actionSheet.controlAccentColor
        self.progressForegroundNode.cornerRadius = 3.0
        
        super.init()
        
        self.addSubnode(self.animationNode)
        self.addSubnode(self.doneAnimationNode)
        
        self.addSubnode(self.progressTextNode)
        
        self.addSubnode(self.progressBackgroundNode)
        self.addSubnode(self.progressForegroundNode)
        
        self.animationStatusDisposable.set((self.animationNode.status
        |> deliverOnMainQueue).start(next: { [weak self] status in
            if let strongSelf = self {
                strongSelf.elapsedTime = status.duration - status.timestamp
            }
        }))
        
        if let postbox, let mediaManager = environment.mediaManager, let path = getAppBundle().path(forResource: "BlankVideo", ofType: "m4v"), let size = fileSize(path) {
            let decoration = ChatBubbleVideoDecoration(corners: ImageCorners(), nativeSize: CGSize(width: 100.0, height: 100.0), contentMode: .aspectFit, backgroundColor: .black)
            
            let dummyFile = TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 1), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: 12345), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [], preloadSize: nil, coverTime: nil, videoCodec: nil)], alternativeRepresentations: [])
            
            let videoContent = NativeVideoContent(id: .message(1, EngineMedia.Id(namespace: 0, id: 1)), userLocation: .other, fileReference: .standalone(media: dummyFile), streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black, storeAfterDownload: nil)
            
            let videoNode = UniversalVideoNode(accountId: AccountRecordId(rawValue: 0), postbox: postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: decoration, content: videoContent, priority: .embedded)
            videoNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 2.0, height: 2.0))
            videoNode.alpha = 0.01
            self.videoNode = videoNode
            
            self.addSubnode(videoNode)
            videoNode.canAttachContent = true
            videoNode.play()
        }
    }
    
    deinit {
        self.animationStatusDisposable.dispose()
    }
    
    public func updateTheme(_ theme: PresentationTheme) {
        self.theme = theme
    }
    
    public func activate() {
    }
    
    public func deactivate() {
    }
    
    public func setEnsurePeerVisibleOnLayout(_ peerId: EnginePeer.Id?) {
    }
    
    public func setDidBeginDragging(_ f: (() -> Void)?) {
    }
    
    public func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) {
        self.contentOffsetUpdated = f
    }
    
    private var validLayout: (CGSize, Bool, CGFloat)?
    public func updateLayout(size: CGSize, isLandscape: Bool, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) {
        self.validLayout = (size, isLandscape, bottomInset)
        
        let nodeHeight: CGFloat = 450.0
        
        let inset: CGFloat = 24.0
        let progressHeight: CGFloat = 6.0
        let spacing: CGFloat = 16.0
        
        let progress = self.progressValue
        
        let progressFrame = CGRect(x: inset, y: size.height - inset - progressHeight, width: size.width - inset * 2.0, height: progressHeight)
        self.progressBackgroundNode.frame = progressFrame
        let progressForegroundFrame = CGRect(x: progressFrame.minX, y: progressFrame.minY, width: floorToScreenPixels(progressFrame.width * progress), height: progressHeight)
        if !self.progressForegroundNode.frame.origin.x.isZero {
            transition.updateFrame(node: self.progressForegroundNode, frame: progressForegroundFrame, beginWithCurrentState: true)
        } else {
            self.progressForegroundNode.frame = progressForegroundFrame
        }
        
        let progressText: String
        if self.isDone {
            progressText = self.strings.Share_UploadDone
        } else {
            progressText = self.strings.Share_UploadProgress(Int(progress * 100.0)).string
        }
        
        self.progressTextNode.attributedText = NSAttributedString(string: progressText, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.theme.actionSheet.primaryTextColor)
        let progressTextSize = self.progressTextNode.updateLayout(size)
        let progressTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - progressTextSize.width) / 2.0), y: progressFrame.minY - spacing - 9.0 - progressTextSize.height), size: progressTextSize)
        self.progressTextNode.frame = progressTextFrame
        
        let imageSide: CGFloat = 160.0
        let imageSize = CGSize(width: imageSide, height: imageSide)
        
        let animationFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: (progressTextFrame.minY - imageSize.height - 20.0)), size: imageSize)
        self.animationNode.frame = animationFrame
        self.animationNode.updateLayout(size: imageSize)
        
        self.doneAnimationNode.frame = animationFrame
        self.doneAnimationNode.updateLayout(size: imageSize)
        
        self.contentOffsetUpdated?(-size.height + nodeHeight * 0.5, transition)
    }
    
    public func updateSelectedPeers(animated: Bool) {
    }
}