import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import LegacyComponents
import TelegramPresentationData
import TooltipUI

private final class ChecksNodeParameters: NSObject {
    let color: UIColor
    let progress: CGFloat
    
    init(color: UIColor, progress: CGFloat) {
        self.color = color
        self.progress = progress
        
        super.init()
    }
}

private class ChecksNode: ASDisplayNode {
    var state: Bool? = nil
    
    var color: UIColor {
        didSet {
            self.setNeedsDisplay()
        }
    }
    
    private var effectiveProgress: CGFloat = 1.0 {
        didSet {
            self.setNeedsDisplay()
        }
    }
    
    init(color: UIColor) {
        self.color = color
        
        super.init()
        
        self.backgroundColor = .clear
        self.isOpaque = false
    }
    
    func animateProgress(from: CGFloat, to: CGFloat) {
        self.pop_removeAllAnimations()
        
        let animation = POPBasicAnimation()
        animation.property = (POPAnimatableProperty.property(withName: "progress", initializer: { property in
            property?.readBlock = { node, values in
                values?.pointee = (node as! ChecksNode).effectiveProgress
            }
            property?.writeBlock = { node, values in
                (node as! ChecksNode).effectiveProgress = values!.pointee
            }
            property?.threshold = 0.01
        }) as! POPAnimatableProperty)
        animation.fromValue = from as NSNumber
        animation.toValue = to as NSNumber
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        animation.duration = 0.2
        self.pop_add(animation, forKey: "progress")
    }
    
    override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
        return ChecksNodeParameters(color: self.color, progress: self.effectiveProgress)
    }
    
    override func didEnterHierarchy() {
        super.didEnterHierarchy()
    }
    
    @objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
        let context = UIGraphicsGetCurrentContext()!
        
        if !isRasterizing {
            context.setBlendMode(.copy)
            context.setFillColor(UIColor.clear.cgColor)
            context.fill(bounds)
        }
        
        guard let parameters = parameters as? ChecksNodeParameters else {
            return
        }
        
        let scaleFactor: CGFloat = 1.0
        context.translateBy(x: bounds.width / 2.0, y: bounds.height / 2.0)
        context.scaleBy(x: scaleFactor, y: scaleFactor)
        context.translateBy(x: -bounds.width / 2.0, y: -bounds.height / 2.0)
        
        let progress = parameters.progress
        
        context.setStrokeColor(parameters.color.cgColor)
        context.setLineWidth(1.0 + UIScreenPixel)
        context.setLineCap(.round)
        context.setLineJoin(.round)
        context.setMiterLimit(10.0)
        
        context.saveGState()
        var s1 = CGPoint(x: 9.0, y: 13.0)
        var s2 = CGPoint(x: 5.0, y: 13.0)
        let p1 = CGPoint(x: 3.5, y: 3.5)
        let p2 = CGPoint(x: 7.5 - UIScreenPixel, y: -8.0)
        
        let check1FirstSegment: CGFloat = max(0.0, min(1.0, progress * 3.0))
        let check2FirstSegment: CGFloat = max(0.0, min(1.0, (progress - 1.0) * 3.0))
        
        let firstProgress = max(0.0, min(1.0, progress))
        let secondProgress = max(0.0, min(1.0, progress - 1.0))
        
        let scale: CGFloat = 1.2
        context.translateBy(x: 16.0, y: 13.0)
        context.scaleBy(x: scale - abs((scale - 1.0) * (firstProgress - 0.5) / 0.5), y: scale - abs((scale - 1.0) * (firstProgress - 0.5) / 0.5))
        s1 = s1.offsetBy(dx: -16.0, dy: -13.0)
        
        if !check1FirstSegment.isZero {
            if check1FirstSegment < 1.0 {
                context.move(to: CGPoint(x: s1.x + p1.x * check1FirstSegment, y: s1.y + p1.y * check1FirstSegment))
                context.addLine(to: s1)
            } else {
                let secondSegment = (min(1.0, progress) - 0.33) * 1.5
                context.move(to: CGPoint(x: s1.x + p1.x + p2.x * secondSegment, y: s1.y + p1.y + p2.y * secondSegment))
                context.addLine(to: CGPoint(x: s1.x + p1.x, y: s1.y + p1.y))
                context.addLine(to: CGPoint(x: s1.x + p1.x * min(1.0, check2FirstSegment), y: s1.y + p1.y * min(1.0, check2FirstSegment)))
            }
        }
        context.strokePath()
        
        context.restoreGState()
        
        context.translateBy(x: 12.0, y: 13.0)
        context.scaleBy(x: scale - abs((scale - 1.0) * (secondProgress - 0.5) / 0.5), y: scale - abs((scale - 1.0) * (secondProgress - 0.5) / 0.5))
        s2 = s2.offsetBy(dx: -12.0, dy: -13.0)
        
        if !check2FirstSegment.isZero {
            if check2FirstSegment < 1.0 {
                context.move(to: CGPoint(x: s2.x + p1.x * check2FirstSegment, y: s2.y + p1.y * check2FirstSegment))
                context.addLine(to: s2)
            } else {
                let secondSegment = (max(0.0, (progress - 1.0)) - 0.33) * 1.5
                context.move(to: CGPoint(x: s2.x + p1.x + p2.x * secondSegment, y: s2.y + p1.y + p2.y * secondSegment))
                context.addLine(to: CGPoint(x: s2.x + p1.x, y: s2.y + p1.y))
                context.addLine(to: s2)
            }
        }
        context.strokePath()
    }
    
    func updateState(_ state: Bool, animated: Bool) {
        guard state != self.state else {
            return
        }
        let previousState = self.state
        self.state = state
        if animated {
            if previousState == nil && self.state == false {
                self.animateProgress(from: 0.0, to: 1.0)
            } else if previousState == false && self.state == true {
                self.animateProgress(from: 1.0, to: 2.0)
            }
        } else {
            if let state = self.state {
                self.effectiveProgress = state ? 2.0 : 1.0
            } else {
                self.effectiveProgress = 0.0
            }
        }
    }
}

class ChatStatusChecksTooltipContentNode: ASDisplayNode, TooltipControllerCustomContentNode {
    private let deliveredChecksNode: ChecksNode
    private let deliveredTextNode: ImmediateTextNode
    private let readChecksNode: ChecksNode
    private let readTextNode: ImmediateTextNode
    
    init(presentationData: PresentationData) {
        self.deliveredChecksNode = ChecksNode(color: .white)
        self.deliveredTextNode = ImmediateTextNode()
        self.readChecksNode = ChecksNode(color: .white)
        self.readTextNode = ImmediateTextNode()
        
        self.deliveredTextNode.attributedText = NSAttributedString(string: presentationData.strings.Conversation_ChecksTooltip_Delivered, font: Font.regular(14.0), textColor: UIColor.white)
        self.readTextNode.attributedText = NSAttributedString(string: presentationData.strings.Conversation_ChecksTooltip_Read, font: Font.regular(14.0), textColor: UIColor.white)
        
        super.init()
        
        self.addSubnode(self.deliveredChecksNode)
        self.addSubnode(self.deliveredTextNode)
        self.addSubnode(self.readChecksNode)
        self.addSubnode(self.readTextNode)
    }
    
    func animateIn() {
        self.deliveredChecksNode.updateState(false, animated: true)
        self.readChecksNode.updateState(false, animated: true)
        
        self.deliveredChecksNode.layer.animateScale(from: 1.0, to: 1.12, duration: 0.25, delay: 0.1, removeOnCompletion: false, completion: { [weak self] _ in
            if let strongSelf = self {
                strongSelf.deliveredChecksNode.layer.animateScale(from: 1.12, to: 1.0, duration: 0.25)
            }
        })
        
        self.deliveredTextNode.layer.animateScale(from: 1.0, to: 1.12, duration: 0.25, delay: 0.1, removeOnCompletion: false, completion: { [weak self] _ in
            if let strongSelf = self {
                strongSelf.deliveredTextNode.layer.animateScale(from: 1.12, to: 1.0, duration: 0.25)
            }
        })
        
        Queue.mainQueue().after(0.5) {
            self.readChecksNode.updateState(true, animated: true)
            
            self.readChecksNode.layer.animateScale(from: 1.0, to: 1.12, duration: 0.25, removeOnCompletion: false, completion: { [weak self] _ in
                if let strongSelf = self {
                    strongSelf.readChecksNode.layer.animateScale(from: 1.12, to: 1.0, duration: 0.25)
                }
            })
            
            self.readTextNode.layer.animateScale(from: 1.0, to: 1.12, duration: 0.25, removeOnCompletion: false, completion: { [weak self] _ in
                if let strongSelf = self {
                    strongSelf.readTextNode.layer.animateScale(from: 1.12, to: 1.0, duration: 0.25)
                }
            })
        }
    }
    
    func updateLayout(size: CGSize) -> CGSize {
        let deliveredSize = self.deliveredTextNode.updateLayout(size)
        let readSize = self.readTextNode.updateLayout(size)
        
        let checksInset: CGFloat = 8.0
        let checksSize = CGSize(width: 24.0, height: 24.0)
        
        self.deliveredChecksNode.frame = CGRect(origin: CGPoint(x: checksInset, y: 15.0), size: checksSize)
        self.deliveredTextNode.frame = CGRect(origin: CGPoint(x: checksInset + checksSize.width + 5.0, y: 19.0), size: deliveredSize)
        self.readChecksNode.frame = CGRect(origin: CGPoint(x: checksInset, y: 38.0), size: checksSize)
        self.readTextNode.frame = CGRect(origin: CGPoint(x: checksInset + checksSize.width + 5.0, y: 43.0), size: readSize)
        
        let contentWidth = max(deliveredSize.width, readSize.width) + checksInset + checksSize.width + 18.0
        let contentHeight: CGFloat = 77.0
        
        return CGSize(width: contentWidth, height: contentHeight)
    }
}