import Foundation
import UIKit
import AsyncDisplayKit
import Display
import TelegramCore
import SyncCore
import TelegramPresentationData
import PasscodeInputFieldNode

enum PasscodeSetupInitialState {
    case createPasscode
    case changePassword(current: String, hasRecoveryEmail: Bool, hasSecureValues: Bool)
}

enum PasscodeSetupStateKind: Int32 {
    case enterPasscode
    case confirmPasscode
}

private func generateFieldBackground(backgroundColor: UIColor, borderColor: UIColor) -> UIImage? {
    return generateImage(CGSize(width: 1.0, height: 48.0), contextGenerator: { size, context in
        let bounds = CGRect(origin: CGPoint(), size: size)
        
        context.setFillColor(backgroundColor.cgColor)
        context.fill(bounds)
        
        context.setFillColor(borderColor.cgColor)
        context.fill(CGRect(origin: CGPoint(), size: CGSize(width: 1.0, height: UIScreenPixel)))
        context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height - UIScreenPixel), size: CGSize(width: 1.0, height: UIScreenPixel)))
    })
}

final class PasscodeSetupControllerNode: ASDisplayNode {
    private var presentationData: PresentationData
    private var mode: PasscodeSetupControllerMode
    
    private let wrapperNode: ASDisplayNode
    
    private let titleNode: ASTextNode
    private let subtitleNode: ASTextNode
    private let inputFieldNode: PasscodeInputFieldNode
    private let inputFieldBackgroundNode: ASImageNode
    private let modeButtonNode: HighlightableButtonNode
    
    var previousPasscode: String?
    var currentPasscode: String {
        return self.inputFieldNode.text
    }
    
    var selectPasscodeMode: (() -> Void)?
    var checkPasscode: ((String) -> Bool)?
    var complete: ((String, Bool) -> Void)?
    var updateNextAction: ((Bool) -> Void)?
    
    private let hapticFeedback = HapticFeedback()
    
    private var validLayout: (ContainerViewLayout, CGFloat)?
    private var maxBottomInset: CGFloat?
    
    init(presentationData: PresentationData, mode: PasscodeSetupControllerMode) {
        self.presentationData = presentationData
        self.mode = mode
        
        self.wrapperNode = ASDisplayNode()
        
        self.titleNode = ASTextNode()
        self.titleNode.isUserInteractionEnabled = false
        self.titleNode.displaysAsynchronously = false
        
        self.subtitleNode = ASTextNode()
        self.subtitleNode.isUserInteractionEnabled = false
        self.subtitleNode.displaysAsynchronously = false
        
        let passcodeType: PasscodeEntryFieldType
        switch self.mode {
            case let .entry(challenge):
                switch challenge {
                    case let .numericalPassword(value):
                        passcodeType = value.count == 6 ? .digits6 : .digits4
                    default:
                        passcodeType = .alphanumeric
                }
            case .setup:
                passcodeType = .digits6
        }
        
        self.inputFieldNode = PasscodeInputFieldNode(color: self.presentationData.theme.list.itemPrimaryTextColor, accentColor: self.presentationData.theme.list.itemAccentColor, fieldType: passcodeType, keyboardAppearance: self.presentationData.theme.rootController.keyboardColor.keyboardAppearance)
        self.inputFieldBackgroundNode = ASImageNode()
        self.inputFieldBackgroundNode.alpha = passcodeType == .alphanumeric ? 1.0 : 0.0
        self.inputFieldBackgroundNode.contentMode = .scaleToFill
        self.inputFieldBackgroundNode.image = generateFieldBackground(backgroundColor: self.presentationData.theme.list.itemBlocksBackgroundColor, borderColor: self.presentationData.theme.list.itemBlocksSeparatorColor)
        
        self.modeButtonNode = HighlightableButtonNode()
        self.modeButtonNode.setTitle(self.presentationData.strings.PasscodeSettings_PasscodeOptions, with: Font.regular(17.0), with: self.presentationData.theme.list.itemAccentColor, for: .normal)
      
        super.init()
        
        self.setViewBlock({
            return UITracingLayerView()
        })
        
        self.backgroundColor = self.presentationData.theme.list.blocksBackgroundColor
        
        self.addSubnode(self.wrapperNode)
        
        self.wrapperNode.addSubnode(self.titleNode)
        self.wrapperNode.addSubnode(self.subtitleNode)
        self.wrapperNode.addSubnode(self.inputFieldBackgroundNode)
        self.wrapperNode.addSubnode(self.inputFieldNode)
        self.wrapperNode.addSubnode(self.modeButtonNode)
        
        let text: String
        switch self.mode {
            case .entry:
                self.modeButtonNode.isHidden = true
                self.modeButtonNode.isAccessibilityElement = false
                text = self.presentationData.strings.EnterPasscode_EnterPasscode
            case let .setup(change, _):
                if change {
                    text = self.presentationData.strings.EnterPasscode_EnterNewPasscodeChange
                } else {
                    text = self.presentationData.strings.EnterPasscode_EnterNewPasscodeNew
                }
        }
        self.titleNode.attributedText = NSAttributedString(string: text, font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
        
        self.inputFieldNode.complete = { [weak self] passcode in
            self?.activateNext()
        }
        
        self.modeButtonNode.addTarget(self, action: #selector(self.modePressed), forControlEvents: .touchUpInside)
    }
    
    func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
        self.validLayout = (layout, navigationBarHeight)
        
        var insets = layout.insets(options: [.statusBar, .input])
        if let maxBottomInset = self.maxBottomInset {
            if maxBottomInset > insets.bottom {
                insets.bottom = maxBottomInset
            } else {
                self.maxBottomInset = insets.bottom
            }
        } else {
            self.maxBottomInset = insets.bottom
        }
        
        self.wrapperNode.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
        
        let inputFieldFrame = self.inputFieldNode.updateLayout(size: layout.size, topOffset: floor(insets.top + navigationBarHeight + (layout.size.height - navigationBarHeight - insets.top - insets.bottom - 24.0) / 2.0), transition: transition)
        transition.updateFrame(node: self.inputFieldNode, frame: CGRect(origin: CGPoint(), size: layout.size))
        
        transition.updateFrame(node: self.inputFieldBackgroundNode, frame: CGRect(x: 0.0, y: inputFieldFrame.minY - 6.0, width: layout.size.width, height: 48.0))
        
        let titleSize = self.titleNode.measure(CGSize(width: layout.size.width - 28.0, height: CGFloat.greatestFiniteMagnitude))
        transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: inputFieldFrame.minY - titleSize.height - 20.0), size: titleSize))
        
        let subtitleSize = self.subtitleNode.measure(CGSize(width: layout.size.width - 28.0, height: CGFloat.greatestFiniteMagnitude))
        transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - subtitleSize.width) / 2.0), y: inputFieldFrame.maxY + 20.0), size: subtitleSize))
        
        transition.updateFrame(node: self.modeButtonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - 53.0), size: CGSize(width: layout.size.width, height: 44.0)))
    }
    
    func updateMode(_ mode: PasscodeSetupControllerMode) {
        self.mode = mode
        self.inputFieldNode.reset()
        
        if case let .setup(_, type) = mode {
            self.inputFieldNode.updateFieldType(type, animated: true)
            
            let fieldBackgroundAlpha: CGFloat
            if case .alphanumeric = type {
                fieldBackgroundAlpha = 1.0
                self.updateNextAction?(true)
            } else {
                fieldBackgroundAlpha = 0.0
                self.updateNextAction?(false)
            }
            let previousAlpha = self.inputFieldBackgroundNode.alpha
            self.inputFieldBackgroundNode.alpha = fieldBackgroundAlpha
            self.inputFieldBackgroundNode.layer.animateAlpha(from: previousAlpha, to: fieldBackgroundAlpha, duration: 0.25)
            self.subtitleNode.isHidden = true
        }
    }
    
    func activateNext() {
        guard !self.currentPasscode.isEmpty else {
            self.animateError()
            return
        }
        
        switch self.mode {
            case .entry:
                if !(self.checkPasscode?(self.currentPasscode) ?? false) {
                    self.animateError()
                }
            case .setup:
                if let previousPasscode = self.previousPasscode {
                    if self.currentPasscode == previousPasscode {
                        var numerical = false
                        if case let .setup(_, type) = mode {
                            if case .alphanumeric = type {
                            } else {
                                numerical = true
                            }
                        }
                        self.complete?(self.currentPasscode, numerical)
                    } else {
                        self.previousPasscode = nil
                        
                        if let snapshotView = self.wrapperNode.view.snapshotContentTree() {
                            snapshotView.frame = self.wrapperNode.frame
                            self.wrapperNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.wrapperNode.view)
                            snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: self.wrapperNode.bounds.width, y: 0.0), duration: 0.25, removeOnCompletion: false, additive: true, completion : { [weak snapshotView] _ in
                                snapshotView?.removeFromSuperview()
                            })
                            self.wrapperNode.layer.animatePosition(from: CGPoint(x: -self.wrapperNode.bounds.width, y: 0.0), to: CGPoint(), duration: 0.25, additive: true)
                            
                            self.inputFieldNode.reset(animated: false)
                            self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.EnterPasscode_EnterNewPasscodeChange, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
                            self.subtitleNode.isHidden = false
                            self.subtitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.PasscodeSettings_DoNotMatch, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
                            self.modeButtonNode.isHidden = false
                            self.modeButtonNode.isAccessibilityElement = true
                            
                            UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: self.presentationData.strings.PasscodeSettings_DoNotMatch)
                            
                            if let validLayout = self.validLayout {
                                self.containerLayoutUpdated(validLayout.0, navigationBarHeight: validLayout.1, transition: .immediate)
                            }
                        }
                    }
                } else {
                    self.previousPasscode = self.currentPasscode
                    
                    if let snapshotView = self.wrapperNode.view.snapshotContentTree() {
                        snapshotView.frame = self.wrapperNode.frame
                        self.wrapperNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.wrapperNode.view)
                        snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -self.wrapperNode.bounds.width, y: 0.0), duration: 0.25, removeOnCompletion: false, additive: true, completion : { [weak snapshotView] _ in
                            snapshotView?.removeFromSuperview()
                        })
                        self.wrapperNode.layer.animatePosition(from: CGPoint(x: self.wrapperNode.bounds.width, y: 0.0), to: CGPoint(), duration: 0.25, additive: true)
                        
                        self.inputFieldNode.reset(animated: false)
                        self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.EnterPasscode_RepeatNewPasscode, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
                        self.subtitleNode.isHidden = true
                        self.modeButtonNode.isHidden = true
                        self.modeButtonNode.isAccessibilityElement = false
                        
                        UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: self.presentationData.strings.EnterPasscode_RepeatNewPasscode)
                        
                        if let validLayout = self.validLayout {
                            self.containerLayoutUpdated(validLayout.0, navigationBarHeight: validLayout.1, transition: .immediate)
                        }
                    }
                }
        }
    }
    
    func activateInput() {
        self.inputFieldNode.activateInput()
        
        UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: self.titleNode.attributedText?.string)
    }
    
    func animateError() {
        self.inputFieldNode.reset()
        self.inputFieldNode.layer.addShakeAnimation(amplitude: -30.0, duration: 0.5, count: 6, decay: true)
        
        self.hapticFeedback.error()
    }
    
    @objc func modePressed() {
        self.selectPasscodeMode?()
    }
}