import Foundation
import UIKit
import AppBundle
import AsyncDisplayKit
import Display
import SolidRoundedButtonNode
import SwiftSignalKit
import OverlayStatusController
import AnimationUI
import AccountContext
import TelegramPresentationData
import PresentationDataUtils
import TelegramCore
import Markdown
import DeviceAccess

private let colorKeyRegex = try? NSRegularExpression(pattern: "\"k\":\\[[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\]")

private func transformedWithTheme(data: Data, theme: PresentationTheme) -> Data {
    if var string = String(data: data, encoding: .utf8) {
        var colors: [UIColor] = [0x333333, 0xFFFFFF, 0x50A7EA, 0x212121].map { UIColor(rgb: $0) }
        let replacementColors: [UIColor] = [theme.list.itemPrimaryTextColor.mixedWith(.white, alpha: 0.2), theme.list.plainBackgroundColor, theme.list.itemAccentColor, theme.list.plainBackgroundColor]
        
        func colorToString(_ color: UIColor) -> String {
            var r: CGFloat = 0.0
            var g: CGFloat = 0.0
            var b: CGFloat = 0.0
            if color.getRed(&r, green: &g, blue: &b, alpha: nil) {
                return "\"k\":[\(r),\(g),\(b),1]"
            }
            return ""
        }
        
        func match(_ a: Double, _ b: Double, eps: Double) -> Bool {
            return abs(a - b) < eps
        }
        
        var replacements: [(NSTextCheckingResult, String)] = []
        
        if let colorKeyRegex = colorKeyRegex {
            let results = colorKeyRegex.matches(in: string, range: NSRange(string.startIndex..., in: string))
            for result in results.reversed()  {
                if let range = Range(result.range, in: string) {
                    let substring = String(string[range])
                    let color = substring[substring.index(string.startIndex, offsetBy: "\"k\":[".count) ..< substring.index(before: substring.endIndex)]
                    let components = color.split(separator: ",")
                    if components.count == 4, let r = Double(components[0]), let g = Double(components[1]), let b = Double(components[2]), let a = Double(components[3]) {
                        if match(a, 1.0, eps: 0.01) {
                            for i in 0 ..< colors.count {
                                let color = colors[i]
                                var cr: CGFloat = 0.0
                                var cg: CGFloat = 0.0
                                var cb: CGFloat = 0.0
                                if color.getRed(&cr, green: &cg, blue: &cb, alpha: nil) {
                                    if match(r, Double(cr), eps: 0.01) && match(g, Double(cg), eps: 0.01) && match(b, Double(cb), eps: 0.01) {
                                        replacements.append((result, colorToString(replacementColors[i])))
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        for (result, text) in replacements {
            if let range = Range(result.range, in: string) {
                string = string.replacingCharacters(in: range, with: text)
            }
        }
        
        return string.data(using: .utf8) ?? data
    } else {
        return data
    }
}

public final class AuthDataTransferSplashScreen: ViewController {
    private let context: AccountContext
    private let activeSessionsContext: ActiveSessionsContext
    private var presentationData: PresentationData
    
    public init(context: AccountContext, activeSessionsContext: ActiveSessionsContext) {
        self.context = context
        self.activeSessionsContext = activeSessionsContext
        
        self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
        
        let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme)
        let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor)
        
        super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))
        
        self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
        self.navigationPresentation = .modalInLargeLayout
        self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
        self.navigationBar?.intrinsicCanTransitionInline = false
        
        self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
    }
    
    override public func loadDisplayNode() {
        self.displayNode = AuthDataTransferSplashScreenNode(context: self.context, presentationData: self.presentationData, action: { [weak self] in
            guard let strongSelf = self else {
                return
            }
            
            DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in
                guard let strongSelf = self else {
                    return
                }
                c.presentationArguments = a
                strongSelf.context.sharedContext.mainWindow?.present(c, on: .root)
            }, openSettings: {
                self?.context.sharedContext.applicationBindings.openSettings()
            }, { granted in
                guard let strongSelf = self else {
                    return
                }
                guard granted else {
                    return
                }
                (strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: AuthTransferScanScreen(context: strongSelf.context, activeSessionsContext: strongSelf.activeSessionsContext), animated: true)
            })
        })
        
        self.displayNodeDidLoad()
    }
    
    override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
        super.containerLayoutUpdated(layout, transition: transition)
        
        (self.displayNode as! AuthDataTransferSplashScreenNode).containerLayoutUpdated(layout: layout, navigationHeight: self.navigationHeight, transition: transition)
    }
}

private final class AuthDataTransferSplashScreenNode: ViewControllerTracingNode {
    private var presentationData: PresentationData
    
    private var animationSize: CGSize = CGSize()
    private var animationOffset: CGPoint = CGPoint()
    private let animationNode: AnimationNode?
    private let titleNode: ImmediateTextNode
    private let badgeBackgroundNodes: [ASImageNode]
    private let badgeTextNodes: [ImmediateTextNode]
    private let textNodes: [ImmediateTextNode]
    let buttonNode: SolidRoundedButtonNode
    
    private let hierarchyTrackingNode: HierarchyTrackingNode
    
    var inProgress: Bool = false {
        didSet {
            self.buttonNode.isUserInteractionEnabled = !self.inProgress
            self.buttonNode.alpha = self.inProgress ? 0.6 : 1.0
        }
    }
    
    private var validLayout: ContainerViewLayout?
    
    init(context: AccountContext, presentationData: PresentationData, action: @escaping () -> Void) {
        self.presentationData = presentationData
        
        if let url = getAppBundle().url(forResource: "anim_qr", withExtension: "json"), let data = try? Data(contentsOf: url) {
            self.animationNode = AnimationNode(animationData: transformedWithTheme(data: data, theme: presentationData.theme))
        } else {
            self.animationNode = nil
        }
        
        let buttonText: String
        
        let badgeFont = Font.with(size: 13.0, design: .round, traits: [.bold])
        let textFont = Font.regular(16.0)
        let textColor = self.presentationData.theme.list.itemPrimaryTextColor
        
        var badgeBackgroundNodes: [ASImageNode] = []
        var badgeTextNodes: [ImmediateTextNode] = []
        var textNodes: [ImmediateTextNode] = []
        
        let badgeBackground = generateFilledCircleImage(diameter: 20.0, color: self.presentationData.theme.list.itemCheckColors.fillColor)
        
        for i in 0 ..< 3 {
            let badgeBackgroundNode = ASImageNode()
            badgeBackgroundNode.displaysAsynchronously = false
            badgeBackgroundNode.displayWithoutProcessing = true
            badgeBackgroundNode.image = badgeBackground
            badgeBackgroundNodes.append(badgeBackgroundNode)
            
            let badgeTextNode = ImmediateTextNode()
            badgeTextNode.displaysAsynchronously = false
            badgeTextNode.attributedText = NSAttributedString(string: "\(i + 1)", font: badgeFont, textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
            badgeTextNode.maximumNumberOfLines = 0
            badgeTextNode.lineSpacing = 0.1
            badgeTextNodes.append(badgeTextNode)
            
            let string: String
            switch i {
            case 0:
                string = self.presentationData.strings.AuthSessions_AddDeviceIntro_Text1
            case 1:
                string = self.presentationData.strings.AuthSessions_AddDeviceIntro_Text2
            default:
                string = self.presentationData.strings.AuthSessions_AddDeviceIntro_Text3
            }
            
            let body = MarkdownAttributeSet(font: textFont, textColor: textColor)
            let link = MarkdownAttributeSet(font: textFont, textColor: self.presentationData.theme.list.itemAccentColor, additionalAttributes: ["URL": true as NSNumber])
            
            let text = parseMarkdownIntoAttributedString(string, attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in
                return nil
            }))
            
            let textNode = ImmediateTextNode()
            textNode.displaysAsynchronously = false
            textNode.attributedText = text
            textNode.maximumNumberOfLines = 0
            textNode.lineSpacing = 0.1
            textNodes.append(textNode)
        }
        
        self.badgeBackgroundNodes = badgeBackgroundNodes
        self.badgeTextNodes = badgeTextNodes
        self.textNodes = textNodes
            
        buttonText = self.presentationData.strings.AuthSessions_AddDeviceIntro_Action
        
        self.titleNode = ImmediateTextNode()
        self.titleNode.displaysAsynchronously = false
        self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_AddDeviceIntro_Title, font: Font.bold(24.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
        self.titleNode.maximumNumberOfLines = 0
        self.titleNode.textAlignment = .center
        
        self.buttonNode = SolidRoundedButtonNode(title: buttonText, theme: SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor), height: 50.0, cornerRadius: 10.0, gloss: false)
        self.buttonNode.isHidden = buttonText.isEmpty
        
        var updateInHierarchy: ((Bool) -> Void)?
        self.hierarchyTrackingNode = HierarchyTrackingNode({ value in
            updateInHierarchy?(value)
        })
        
        super.init()
        
        self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
        
        self.addSubnode(self.hierarchyTrackingNode)
        
        if let animationNode = self.animationNode {
            self.addSubnode(animationNode)
        }
        self.addSubnode(self.titleNode)
        
        self.badgeBackgroundNodes.forEach(self.addSubnode)
        self.badgeTextNodes.forEach(self.addSubnode)
        self.textNodes.forEach(self.addSubnode)
        
        self.addSubnode(self.buttonNode)
        
        self.buttonNode.pressed = {
            action()
        }
        
        for textNode in self.textNodes {
            textNode.linkHighlightColor = self.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.5)
            textNode.highlightAttributeAction = { attributes in
                if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
                    return NSAttributedString.Key(rawValue: "URL")
                } else {
                    return nil
                }
            }
            textNode.tapAttributeAction = { attributes in
                if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
                    context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: "https://desktop.telegram.org", forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
                }
            }
        }
        
        updateInHierarchy = { [weak self] value in
            if value {
                self?.animationNode?.play()
            } else {
                self?.animationNode?.reset()
            }
        }
    }
    
    override func didLoad() {
        super.didLoad()
    }
    
    func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
        let firstTime = self.validLayout == nil
        self.validLayout = layout
        
        let sideInset: CGFloat = 22.0
        let textSideInset: CGFloat = 54.0
        let buttonSideInset: CGFloat = 16.0
        let titleSpacing: CGFloat = 25.0
        let buttonHeight: CGFloat = 50.0
        let buttonSpacing: CGFloat = 16.0
        let textSpacing: CGFloat = 25.0
        let badgeSize: CGFloat = 20.0
        
        let animationFitSize = CGSize(width: min(500.0, layout.size.width - sideInset + 20.0), height: 500.0)
        let animationSize = self.animationNode?.preferredSize()?.fitted(animationFitSize) ?? animationFitSize
        let iconSize: CGSize = animationSize
        var iconOffset = CGPoint()
        
        let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
        
        var badgeTextSizes: [CGSize] = []
        var textSizes: [CGSize] = []
        var textContentHeight: CGFloat = 0.0
        for i in 0 ..< self.badgeTextNodes.count {
            let badgeTextSize = self.badgeTextNodes[i].updateLayout(CGSize(width: 100.0, height: .greatestFiniteMagnitude))
            badgeTextSizes.append(badgeTextSize)
            let textSize = self.textNodes[i].updateLayout(CGSize(width: layout.size.width - sideInset * 2.0 - 40.0, height: .greatestFiniteMagnitude))
            textSizes.append(textSize)
            
            if i != 0 {
                textContentHeight += textSpacing
            }
            textContentHeight += textSize.height
        }
        
        var contentHeight = iconSize.height + titleSize.height + titleSpacing + textContentHeight
        
        let bottomInset = layout.intrinsicInsets.bottom + 20.0
        let contentTopInset = navigationHeight
        let contentBottomInset = bottomInset + buttonHeight + buttonSpacing
        
        let iconSpacing: CGFloat = max(20.0, min(61.0, layout.size.height - contentTopInset - contentBottomInset - contentHeight - 40.0))
        
        contentHeight += iconSpacing
        
        var contentVerticalOrigin = contentTopInset + floor((layout.size.height - contentTopInset - contentBottomInset - contentHeight) / 2.0)
        
        let buttonWidth = layout.size.width - buttonSideInset * 2.0
        
        let buttonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonWidth) / 2.0), y: layout.size.height - bottomInset - buttonHeight), size: CGSize(width: buttonWidth, height: buttonHeight))
        transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
        self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition)
        
        var maxContentVerticalOrigin = buttonFrame.minY - 12.0 - contentHeight
        
        contentVerticalOrigin = min(contentVerticalOrigin, maxContentVerticalOrigin)
        
        var contentY = contentVerticalOrigin
        let iconFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0) + self.animationOffset.x, y: contentY), size: iconSize).offsetBy(dx: iconOffset.x, dy: iconOffset.y)
        contentY += iconSize.height + iconSpacing
        if let animationNode = self.animationNode {
            transition.updateFrameAdditive(node: animationNode, frame: iconFrame)
            if iconFrame.minY < 0.0 {
                transition.updateAlpha(node: animationNode, alpha: 0.0)
            } else {
                transition.updateAlpha(node: animationNode, alpha: 1.0)
            }
        }
        
        let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: contentY), size: titleSize)
        transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
        contentY += titleSize.height + titleSpacing
        
        for i in 0 ..< self.badgeTextNodes.count {
            if i != 0 {
                contentY += textSpacing
            }
            
            let badgeTextSize = badgeTextSizes[i]
            let textSize = textSizes[i]
            
            let textFrame = CGRect(origin: CGPoint(x: textSideInset, y: contentY), size: textSize)
            transition.updateFrameAdditive(node: self.textNodes[i], frame: textFrame)
            
            let badgeFrame = CGRect(origin: CGPoint(x: sideInset, y: textFrame.minY), size: CGSize(width: badgeSize, height: badgeSize))
            transition.updateFrameAdditive(node: self.badgeBackgroundNodes[i], frame: badgeFrame)
            
            let badgeTextOffsetX: CGFloat
            if i == 0 {
                badgeTextOffsetX = 0.5
            } else {
                badgeTextOffsetX = 1.0
            }
            
            transition.updateFrameAdditive(node: self.badgeTextNodes[i], frame: CGRect(origin: CGPoint(x: badgeFrame.minX + floor((badgeFrame.width - badgeTextSize.width) / 2.0) + badgeTextOffsetX, y: badgeFrame.minY + floor((badgeFrame.height - badgeTextSize.height) / 2.0) + 0.5), size: badgeTextSize))
            
            contentY += textSize.height
        }
        
        if firstTime {
            self.animationNode?.play()
        }
    }
}