import Foundation
import UIKit
import AsyncDisplayKit

public class StatusBarSurface {
    var statusBars: [StatusBar] = []
    
    func addStatusBar(_ statusBar: StatusBar) {
        self.removeStatusBar(statusBar)
        self.statusBars.append(statusBar)
    }
    
    func insertStatusBar(_ statusBar: StatusBar, atIndex index: Int) {
        self.removeStatusBar(statusBar)
        self.statusBars.insert(statusBar, at: index)
    }
    
    func removeStatusBar(_ statusBar: StatusBar) {
        for i in 0 ..< self.statusBars.count {
            if self.statusBars[i] === statusBar {
                self.statusBars.remove(at: i)
                break
            }
        }
    }
}

open class CallStatusBarNode: ASDisplayNode {
    open func update(size: CGSize) {
        
    }
}

private final class StatusBarView: UITracingLayerView {
    weak var node: StatusBar?
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if let node = self.node {
            return node.hitTest(point, with: event)
        }
        return nil
    }
}

public final class StatusBar: ASDisplayNode {
    private var _statusBarStyle: StatusBarStyle = .Black
    
    public var statusBarStyle: StatusBarStyle {
        get {
            return self._statusBarStyle
        } set(value) {
            if self._statusBarStyle != value {
                self._statusBarStyle = value
                self.alphaUpdated?(.immediate)
            }
        }
    }
    
    public func updateStatusBarStyle(_ statusBarStyle: StatusBarStyle, animated: Bool) {
        if self._statusBarStyle != statusBarStyle {
            self._statusBarStyle = statusBarStyle
            self.alphaUpdated?(animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
        }
    }
    
    public var ignoreInCall: Bool = false
    
    var inCallNavigate: (() -> Void)?
    
    private var proxyNode: StatusBarProxyNode?
    private var removeProxyNodeScheduled = false
    
    let offsetNode = ASDisplayNode()
    var callStatusBarNode: CallStatusBarNode? = nil
    
    public var verticalOffset: CGFloat = 0.0 {
        didSet {
            if !self.verticalOffset.isEqual(to: oldValue) {
                self.offsetNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -self.verticalOffset), size: CGSize())
            }
        }
    }
    
    var alphaUpdated: ((ContainedViewLayoutTransition) -> Void)?
    
    public func updateAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
        self.alpha = alpha
        self.alphaUpdated?(transition)
    }
    
    public override init() {
        self.offsetNode.isUserInteractionEnabled = false
        
        super.init()
        
        self.setViewBlock({
            return StatusBarView()
        })
        
        (self.view as! StatusBarView).node = self
        
        self.addSubnode(self.offsetNode)
        
        self.clipsToBounds = true
        
        self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
    }
    
    func updateState(statusBar: UIView?, withSafeInsets: Bool, inCallNode: CallStatusBarNode?, animated: Bool) {
        if let statusBar = statusBar {
            self.removeProxyNodeScheduled = false
            let resolvedStyle: StatusBarStyle
            if inCallNode != nil && !self.ignoreInCall {
                resolvedStyle = .White
            } else {
                resolvedStyle = self.statusBarStyle
            }
            if let proxyNode = self.proxyNode {
                proxyNode.statusBarStyle = resolvedStyle
            } else {
                self.proxyNode = StatusBarProxyNode(statusBarStyle: resolvedStyle, statusBar: statusBar)
                self.proxyNode!.isHidden = false
                self.addSubnode(self.proxyNode!)
            }
        } else {
            self.removeProxyNodeScheduled = true
            
            DispatchQueue.main.async(execute: { [weak self] in
                if let strongSelf = self {
                    if strongSelf.removeProxyNodeScheduled {
                        strongSelf.removeProxyNodeScheduled = false
                        strongSelf.proxyNode?.isHidden = true
                        strongSelf.proxyNode?.removeFromSupernode()
                        strongSelf.proxyNode = nil
                    }
                }
            })
        }
        
        var ignoreInCall = self.ignoreInCall
        switch self.statusBarStyle {
            case .Black, .White:
                break
            default:
                ignoreInCall = true
        }
        
        var resolvedCallStatusBarNode: CallStatusBarNode? = inCallNode
        if ignoreInCall {
            resolvedCallStatusBarNode = nil
        }
        
        if (resolvedCallStatusBarNode != nil) != (self.callStatusBarNode != nil) {
            if let resolvedCallStatusBarNode = resolvedCallStatusBarNode {
                self.addSubnode(resolvedCallStatusBarNode)
            } else if let callStatusBarNode = self.callStatusBarNode {
                self.callStatusBarNode = nil
                callStatusBarNode.removeFromSupernode()
            }
        }
        
        self.callStatusBarNode = resolvedCallStatusBarNode
    }
    
    override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if self.bounds.contains(point) && self.callStatusBarNode != nil {
            return self.view
        } else {
            return nil
        }
    }
    
    @objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
        if case .ended = recognizer.state, self.callStatusBarNode != nil {
            self.inCallNavigate?()
        }
    }
}