import Foundation
import UIKit
import Display
import AsyncDisplayKit
import TelegramPresentationData

private let shadowInset: CGFloat = 8.0

private func generateShadowImage(theme: PresentationTheme) -> UIImage? {
    return generateImage(CGSize(width: 32.0 + shadowInset * 2.0, height: 32.0 + shadowInset * 2.0), rotatedContext: { size, context in
        context.clear(CGRect(origin: CGPoint(), size: size))
        context.setShadow(offset: CGSize(width: 0.0, height: 0.0), blur: 20.0, color: UIColor(white: 0.0, alpha: 0.2).cgColor)
        context.setFillColor(theme.actionSheet.opaqueItemBackgroundColor.cgColor)
        context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset, y: shadowInset), size: CGSize(width: size.width - shadowInset * 2.0, height: size.height - shadowInset * 2.0)))
    })?.stretchableImage(withLeftCapWidth: 16 + Int(shadowInset) / 2, topCapHeight: 16 + Int(shadowInset) / 2)
}

private final class MessageActionButtonNode: HighlightableButtonNode {
    let theme: PresentationTheme
    let separatorNode: ASDisplayNode
    let backgroundNode: ASDisplayNode
    
    init(theme: PresentationTheme) {
        self.theme = theme
        self.separatorNode = ASDisplayNode()
        self.separatorNode.isLayerBacked = true
        self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
        
        self.backgroundNode = ASDisplayNode()
        self.backgroundNode.isLayerBacked = true
        self.backgroundNode.alpha = 0.0
        self.backgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor
        
        super.init()
        
        self.setAttributedTitle(NSAttributedString(string: " "), for: [])
        
        self.insertSubnode(self.separatorNode, at: 0)
        self.insertSubnode(self.backgroundNode, at: 1)
        
        self.highligthedChanged = { [weak self] highlighted in
            if let strongSelf = self {
                if highlighted {
                    if let supernode = strongSelf.titleNode.supernode {
                        strongSelf.titleNode.removeFromSupernode()
                        supernode.addSubnode(strongSelf.titleNode)
                    }
                    strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity")
                    strongSelf.backgroundNode.alpha = 1.0
                } else {
                    strongSelf.backgroundNode.alpha = 0.0
                    strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
                }
            }
        }
    }
    
    override func layout() {
        super.layout()
        self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.bounds.size.height - UIScreenPixel), size: CGSize(width: self.bounds.size.width, height: UIScreenPixel))
        self.backgroundNode.frame = self.bounds
    }
}

final class ChatMessageActionSheetControllerNode: ViewControllerTracingNode {
    private let theme: PresentationTheme
    
    private let sideDimNode: ASDisplayNode
    private let sideInputDimNode: ASDisplayNode
    private let inputDimNode: ASDisplayNode
    private let itemsShadowNode: ASImageNode
    private let itemsContainerNode: ASDisplayNode
    
    private let actions: [ChatMessageContextMenuSheetAction]
    private let dismissed: () -> Void
    private weak var associatedController: ViewController?
    private let actionNodes: [MessageActionButtonNode]
    
    private let feedback = HapticFeedback()
    private var validLayout: ContainerViewLayout?
    
    init(theme: PresentationTheme, actions: [ChatMessageContextMenuSheetAction], dismissed: @escaping () -> Void, associatedController: ViewController?) {
        self.theme = theme
        self.actions = actions
        self.dismissed = dismissed
        self.associatedController = associatedController
        
        self.sideDimNode = ASDisplayNode()
        self.sideDimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
        
        self.sideInputDimNode = ASDisplayNode()
        self.sideInputDimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
        
        self.inputDimNode = ASDisplayNode()
        self.inputDimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
        
        self.itemsShadowNode = ASImageNode()
        self.itemsShadowNode.isLayerBacked = true
        self.itemsShadowNode.displayWithoutProcessing = true
        self.itemsShadowNode.displaysAsynchronously = false
        self.itemsShadowNode.image = generateShadowImage(theme: theme)
        
        self.itemsContainerNode = ASDisplayNode()
        self.itemsContainerNode.backgroundColor = theme.actionSheet.opaqueItemBackgroundColor
        self.itemsContainerNode.cornerRadius = 16.0
        self.itemsContainerNode.clipsToBounds = true
        
        self.actionNodes = actions.map { action in
            let node = MessageActionButtonNode(theme: theme)
            node.setAttributedTitle(NSAttributedString(string: action.title, font: Font.regular(20.0), textColor: action.color == .destructive ? theme.actionSheet.destructiveActionTextColor : theme.actionSheet.controlAccentColor), for: [])
            return node
        }
        
        super.init()
        
        self.addSubnode(self.sideDimNode)
        self.addSubnode(self.sideInputDimNode)
        self.addSubnode(self.inputDimNode)
        self.addSubnode(self.itemsShadowNode)
        self.addSubnode(self.itemsContainerNode)

        for actionNode in actionNodes {
            self.itemsContainerNode.addSubnode(actionNode)
            actionNode.addTarget(self, action: #selector(actionPressed(_:)), forControlEvents: .touchUpInside)
        }
        
        self.feedback.prepareImpact(.light)
    }
    
    override func didLoad() {
        super.didLoad()
        
        self.sideDimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTap(_:))))
        self.sideInputDimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTap(_:))))
        self.inputDimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTap(_:))))
    }
    
    func animateIn(transition: ContainedViewLayoutTransition) {
        self.inputDimNode.alpha = 0.0
        self.sideInputDimNode.alpha = 0.0
        self.sideDimNode.alpha = 0.0
        transition.updateAlpha(node: self.inputDimNode, alpha: 1.0)
        transition.updateAlpha(node: self.sideInputDimNode, alpha: 1.0)
        transition.updateAlpha(node: self.sideDimNode, alpha: 1.0)
        transition.animatePositionAdditive(node: self.itemsContainerNode, offset: CGPoint(x: 0.0, y: self.bounds.size.height))
        transition.animatePositionAdditive(node: self.itemsShadowNode, offset: CGPoint(x: 0.0, y: self.bounds.size.height))
        
        self.feedback.impact(.light)
    }
    
    func animateOut(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
        transition.updateAlpha(node: self.sideInputDimNode, alpha: 0.0)
        transition.updateAlpha(node: self.sideDimNode, alpha: 0.0)
        transition.updateAlpha(node: self.inputDimNode, alpha: 0.0)
        let position = CGPoint(x: self.itemsContainerNode.position.x, y: self.bounds.size.height + self.itemsContainerNode.bounds.height)
        transition.updatePosition(node: self.itemsContainerNode, position: position, completion: { _ in
            completion()
        })
        transition.updatePosition(node: self.itemsShadowNode, position: position)
    }
    
    func updateLayout(layout: ContainerViewLayout, horizontalOrigin: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
        self.validLayout = layout
        
        var height: CGFloat = max(14.0, layout.intrinsicInsets.bottom)
        let inputHeight = layout.inputHeight ?? 0.0
        
        var horizontalOffset: CGFloat = horizontalOrigin
        if !horizontalOffset.isZero {
            horizontalOffset += UIScreenPixel
        }
        
        var isSlideOver = false
        if case .compact = layout.metrics.widthClass, case .regular = layout.metrics.heightClass {
            isSlideOver = true
        }
        
        transition.updateFrame(node: self.sideDimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: max(0.0, horizontalOffset), height: max(0.0, layout.size.height - inputHeight))))
        transition.updateFrame(node: self.sideInputDimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - inputHeight), size: CGSize(width: max(0.0, horizontalOrigin), height: max(0.0, inputHeight))))
        transition.updateFrame(node: self.inputDimNode, frame: CGRect(origin: CGPoint(x: horizontalOrigin, y: layout.size.height - inputHeight), size: CGSize(width: layout.size.width, height: inputHeight)))
        
        height += layout.safeInsets.bottom
        
        let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 7.0 * 2.0)
        
        var itemsHeight: CGFloat = 0.0
        for actionNode in self.actionNodes {
            actionNode.frame = CGRect(origin: CGPoint(x: 0.0, y: itemsHeight), size: CGSize(width: containerWidth, height: 57.0))
            actionNode.layout()
            itemsHeight += actionNode.bounds.height
        }
        
        var containerFrame = CGRect(origin: CGPoint(x: horizontalOrigin + floor((layout.size.width - containerWidth) / 2.0), y: layout.size.height - height - itemsHeight), size: CGSize(width: containerWidth, height: itemsHeight))
        if isSlideOver {
            containerFrame = containerFrame.offsetBy(dx: 0.0, dy: -inputHeight)
        }
        transition.updateFrame(node: self.itemsContainerNode, frame: containerFrame)
        transition.updateFrame(node: self.itemsShadowNode, frame: containerFrame.insetBy(dx: -shadowInset, dy: -shadowInset))
        
        height += itemsHeight
        
        if isSlideOver {
            height += inputHeight
        }
        
        height += 6.0
        
        return height
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if self.itemsContainerNode.frame.contains(point) {
            let subpoint = self.view.convert(point, to: self.itemsContainerNode.view)
            return itemsContainerNode.hitTest(subpoint, with: event)
        }
        if let validLayout = self.validLayout, let inputHeight = validLayout.inputHeight {
            if point.y >= validLayout.size.height - inputHeight {
                return self.inputDimNode.view
            }
        }
        if let associatedController = self.associatedController {
            let subpoint = self.view.convert(point, to: nil)
            if let result = associatedController.view.hitTest(subpoint, with: event) {
                return result
            }
        }
        return self.inputDimNode.view
    }
    
    @objc func actionPressed(_ node: ASDisplayNode) {
        for i in 0 ..< self.actionNodes.count {
            if node == self.actionNodes[i] {
                self.actions[i].action()
                self.dismissed()
                break
            }
        }
    }
    
    @objc func dimTap(_ recognizer: UITapGestureRecognizer) {
        if case .ended = recognizer.state {
            self.dismissed()
        }
    }
}