import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import ComponentFlow
import TelegramPresentationData
import AccountContext
import ComponentDisplayAdapters
import MultilineTextComponent
import EmojiStatusComponent
import TelegramStringFormatting
import SolidRoundedButtonComponent
import PresentationDataUtils

protocol ContextMenuItemWithAction: AnyObject {
    func performAction() -> ContextMenuPerformActionResult
}

enum ContextMenuPerformActionResult {
    case none
    case clearHighlight
}

private final class ContextMenuActionItem: Component, ContextMenuItemWithAction {
    typealias EnvironmentType = ContextMenuActionItemEnvironment
    
    let title: String
    let action: () -> ContextMenuPerformActionResult
    
    init(title: String, action: @escaping () -> ContextMenuPerformActionResult) {
        self.title = title
        self.action = action
    }
    
    static func ==(lhs: ContextMenuActionItem, rhs: ContextMenuActionItem) -> Bool {
        if lhs.title != rhs.title {
            return false
        }
        return true
    }
    
    func performAction() -> ContextMenuPerformActionResult {
        return self.action()
    }
    
    final class View: UIView {
        private let titleView: ComponentView<Empty>
        
        override init(frame: CGRect) {
            self.titleView = ComponentView<Empty>()
            
            super.init(frame: frame)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        func update(component: ContextMenuActionItem, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
            let contextEnvironment = environment[EnvironmentType.self].value
            
            let sideInset: CGFloat = 16.0
            let height: CGFloat = 44.0
            
            let titleSize = self.titleView.update(
                transition: .immediate,
                component: AnyComponent(MultilineTextComponent(
                    text: .plain(NSAttributedString(string: component.title, font: Font.regular(17.0), textColor: contextEnvironment.theme.contextMenu.primaryColor))
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
            )
            let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
            if let view = self.titleView.view {
                if view.superview == nil {
                    self.addSubview(view)
                }
                transition.setFrame(view: view, frame: titleFrame)
            }
            
            return CGSize(width: sideInset * 2.0 + titleSize.width, height: height)
        }
    }
    
    func makeView() -> View {
        return View(frame: CGRect())
    }
    
    func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
        return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
    }
}

private final class ContextMenuActionItemEnvironment: Equatable {
    let theme: PresentationTheme
    
    init(
        theme: PresentationTheme
    ) {
        self.theme = theme
    }
    
    static func ==(lhs: ContextMenuActionItemEnvironment, rhs: ContextMenuActionItemEnvironment) -> Bool {
        if lhs.theme !== rhs.theme {
            return false
        }
        return true
    }
}

private final class ContextMenuActionsComponent: Component {
    let theme: PresentationTheme
    let items: [AnyComponentWithIdentity<ContextMenuActionItemEnvironment>]
    
    init(
        theme: PresentationTheme,
        items: [AnyComponentWithIdentity<ContextMenuActionItemEnvironment>]
    ) {
        self.theme = theme
        self.items = items
    }
    
    static func ==(lhs: ContextMenuActionsComponent, rhs: ContextMenuActionsComponent) -> Bool {
        if lhs.theme !== rhs.theme {
            return false
        }
        if lhs.items != rhs.items {
            return false
        }
        return true
    }
    
    final class View: UIButton {
        private final class ItemView {
            let view = ComponentView<ContextMenuActionItemEnvironment>()
            let separatorView = UIView()
        }
        
        private let backgroundView: BlurredBackgroundView
        private var itemViews: [AnyHashable: ItemView] = [:]
        private var highligntedBackgroundView: UIView?
        
        private var component: ContextMenuActionsComponent?
        
        override init(frame: CGRect) {
            self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
            
            super.init(frame: frame)
            
            self.clipsToBounds = true
            self.layer.cornerRadius = 14.0
            
            self.addSubview(self.backgroundView)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
            self.setHighlightedItem(id: self.itemAtPoint(point: touch.location(in: self)))
            
            return super.beginTracking(touch, with: event)
        }
        
        override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
            self.setHighlightedItem(id: self.itemAtPoint(point: touch.location(in: self)))
            
            return super.continueTracking(touch, with: event)
        }
        
        override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
            if let component = self.component, let touch = touch, let id = self.itemAtPoint(point: touch.location(in: self)) {
                self.setHighlightedItem(id: id)
                for item in component.items {
                    if item.id == id {
                        if let itemComponent = item.component.wrapped as? ContextMenuItemWithAction {
                            switch itemComponent.performAction() {
                            case .none:
                                break
                            case .clearHighlight:
                                self.setHighlightedItem(id: nil)
                            }
                        }
                        break
                    }
                }
            } else {
                self.setHighlightedItem(id: nil)
            }
            
            super.endTracking(touch, with: event)
        }
        
        override func cancelTracking(with event: UIEvent?) {
            self.setHighlightedItem(id: nil)
            
            super.cancelTracking(with: event)
        }
        
        override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
            self.setHighlightedItem(id: nil)
            
            super.touchesCancelled(touches, with: event)
        }
        
        private func itemAtPoint(point: CGPoint) -> AnyHashable? {
            for (id, itemView) in self.itemViews {
                guard let itemComponentView = itemView.view.view else {
                    continue
                }
                let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: itemComponentView.frame.minY), size: CGSize(width: self.bounds.width, height: itemComponentView.bounds.height))
                if itemFrame.contains(point) {
                    return id
                }
            }
            return nil
        }
        
        private func setHighlightedItem(id: AnyHashable?) {
            if let component = self.component, let id = id, let itemView = self.itemViews[id], let itemComponentView = itemView.view.view {
                let highligntedBackgroundView: UIView
                if let current = self.highligntedBackgroundView {
                    highligntedBackgroundView = current
                } else {
                    highligntedBackgroundView = UIView()
                    self.highligntedBackgroundView = highligntedBackgroundView
                    var found = false
                    outer: for subview in self.subviews {
                        for (_, listItemView) in self.itemViews {
                            if subview === listItemView.view.view {
                                self.insertSubview(highligntedBackgroundView, belowSubview: subview)
                                found = true
                                break outer
                            }
                        }
                    }
                    if !found {
                        self.insertSubview(highligntedBackgroundView, aboveSubview: self.backgroundView)
                    }
                    
                    highligntedBackgroundView.backgroundColor = component.theme.contextMenu.itemHighlightedBackgroundColor
                }
                var highlightFrame = CGRect(origin: CGPoint(x: 0.0, y: itemComponentView.frame.minY), size: CGSize(width: self.bounds.width, height: itemComponentView.bounds.height))
                if id != component.items.last?.id {
                    highlightFrame.size.height += UIScreenPixel
                }
                
                highligntedBackgroundView.frame = highlightFrame
            } else {
                if let highligntedBackgroundView = self.highligntedBackgroundView {
                    self.highligntedBackgroundView = nil
                    highligntedBackgroundView.removeFromSuperview()
                }
            }
        }
        
        override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
            if !self.bounds.contains(point) {
                return nil
            }
            return self
        }
        
        func update(component: ContextMenuActionsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
            self.component = component
            
            let availableItemSize = availableSize
            
            var itemsSize = CGSize()
            var validIds = Set<AnyHashable>()
            var currentItems: [(id: AnyHashable, itemFrame: CGRect, itemTransition: ComponentTransition)] = []
            for i in 0 ..< component.items.count {
                let item = component.items[i]
                validIds.insert(item.id)
                
                let itemView: ItemView
                var itemTransition = transition
                if let current = self.itemViews[item.id] {
                    itemView = current
                } else {
                    itemTransition = .immediate
                    itemView = ItemView()
                    self.itemViews[item.id] = itemView
                    self.insertSubview(itemView.separatorView, aboveSubview: self.backgroundView)
                }
                
                let itemSize = itemView.view.update(
                    transition: itemTransition,
                    component: item.component,
                    environment: {
                        ContextMenuActionItemEnvironment(theme: component.theme)
                    },
                    containerSize: availableItemSize
                )
                let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: itemsSize.height), size: itemSize)
                if let view = itemView.view.view {
                    if view.superview == nil {
                        self.addSubview(view)
                    }
                    itemTransition.setFrame(view: view, frame: itemFrame)
                }
                currentItems.append((item.id, itemFrame, itemTransition))
                itemsSize.width = max(itemsSize.width, itemSize.width)
                itemsSize.height += itemSize.height
            }
            
            itemsSize.width = max(itemsSize.width, 180.0)
            
            for i in 0 ..< currentItems.count {
                let item = currentItems[i]
                guard let itemView = self.itemViews[item.id] else {
                    continue
                }
                itemView.separatorView.backgroundColor = component.theme.contextMenu.itemSeparatorColor
                itemView.separatorView.isHidden = i == currentItems.count - 1
                item.itemTransition.setFrame(view: itemView.separatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: item.itemFrame.maxY), size: CGSize(width: itemsSize.width, height: UIScreenPixel)))
            }
            
            var removeIds: [AnyHashable] = []
            for (id, itemView) in self.itemViews {
                if !validIds.contains(id) {
                    removeIds.append(id)
                    itemView.view.view?.removeFromSuperview()
                    itemView.separatorView.removeFromSuperview()
                }
            }
            
            self.backgroundView.updateColor(color: component.theme.contextMenu.backgroundColor, transition: .immediate)
            transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: itemsSize))
            self.backgroundView.update(size: itemsSize, transition: transition.containedViewLayoutTransition)
            
            return itemsSize
        }
    }
    
    func makeView() -> View {
        return View(frame: CGRect())
    }
    
    func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
        return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
    }
}

private final class TimeSelectionControlComponent: Component {
    let theme: PresentationTheme
    let strings: PresentationStrings
    let bottomInset: CGFloat
    let apply: (Int32) -> Void
    let cancel: () -> Void
    
    init(
        theme: PresentationTheme,
        strings: PresentationStrings,
        bottomInset: CGFloat,
        apply: @escaping (Int32) -> Void,
        cancel: @escaping () -> Void
    ) {
        self.theme = theme
        self.strings = strings
        self.bottomInset = bottomInset
        self.apply = apply
        self.cancel = cancel
    }
    
    static func ==(lhs: TimeSelectionControlComponent, rhs: TimeSelectionControlComponent) -> Bool {
        if lhs.theme !== rhs.theme {
            return false
        }
        if lhs.strings !== rhs.strings {
            return false
        }
        if lhs.bottomInset != rhs.bottomInset {
            return false
        }
        return true
    }
    
    final class View: UIView {
        private let backgroundView: BlurredBackgroundView
        private let pickerView: UIDatePicker
        private let titleView: ComponentView<Empty>
        private let leftButtonView: ComponentView<Empty>
        private let actionButtonView: ComponentView<Empty>
        
        private var component: TimeSelectionControlComponent?
        
        override init(frame: CGRect) {
            self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
            self.pickerView = UIDatePicker()
            
            self.titleView = ComponentView<Empty>()
            self.leftButtonView = ComponentView<Empty>()
            self.actionButtonView = ComponentView<Empty>()
            
            super.init(frame: frame)
            
            self.addSubview(self.backgroundView)
            
            self.pickerView.timeZone = TimeZone(secondsFromGMT: 0)
            self.pickerView.datePickerMode = .countDownTimer
            self.pickerView.datePickerMode = .dateAndTime
            if #available(iOS 13.4, *) {
                self.pickerView.preferredDatePickerStyle = .wheels
            }
            self.pickerView.minimumDate = Date(timeIntervalSince1970: Date().timeIntervalSince1970 + Double(TimeZone.current.secondsFromGMT()))
            self.pickerView.maximumDate = Date(timeIntervalSince1970: Double(Int32.max - 1))
            
            self.addSubview(self.pickerView)
            self.pickerView.addTarget(self, action: #selector(self.datePickerUpdated), for: .valueChanged)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        @objc private func datePickerUpdated() {
        }
        
        func update(component: TimeSelectionControlComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
            if self.component?.theme !== component.theme {
                UILabel.setDateLabel(component.theme.list.itemPrimaryTextColor)
                
                self.pickerView.setValue(component.theme.list.itemPrimaryTextColor, forKey: "textColor")
                
                self.backgroundView.updateColor(color: component.theme.contextMenu.backgroundColor, transition: .immediate)
            }
            
            self.component = component
            
            let topPanelHeight: CGFloat = 54.0
            let pickerSpacing: CGFloat = 10.0
            
            let pickerSize = CGSize(width: availableSize.width, height: 216.0)
            let pickerFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight + pickerSpacing), size: pickerSize)
            
            let titleSize = self.titleView.update(
                transition: transition,
                component: AnyComponent(Text(text: component.strings.EmojiStatusSetup_SetUntil, font: Font.semibold(17.0), color: component.theme.list.itemPrimaryTextColor)),
                environment: {},
                containerSize: CGSize(width: availableSize.width, height: 100.0)
            )
            if let titleComponentView = self.titleView.view {
                if titleComponentView.superview == nil {
                    self.addSubview(titleComponentView)
                }
                transition.setFrame(view: titleComponentView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: floor((topPanelHeight - titleSize.height) / 2.0)), size: titleSize))
            }
            
            let leftButtonSize = self.leftButtonView.update(
                transition: transition,
                component: AnyComponent(Button(
                    content: AnyComponent(Text(
                        text: component.strings.Common_Cancel,
                        font: Font.regular(17.0),
                        color: component.theme.list.itemAccentColor
                    )),
                    action: { [weak self] in
                        self?.component?.cancel()
                    }
                ).minSize(CGSize(width: 16.0, height: topPanelHeight))),
                environment: {},
                containerSize: CGSize(width: availableSize.width, height: 100.0)
            )
            if let leftButtonComponentView = self.leftButtonView.view {
                if leftButtonComponentView.superview == nil {
                    self.addSubview(leftButtonComponentView)
                }
                transition.setFrame(view: leftButtonComponentView, frame: CGRect(origin: CGPoint(x: 16.0, y: floor((topPanelHeight - leftButtonSize.height) / 2.0)), size: leftButtonSize))
            }
            
            let actionButtonSize = self.actionButtonView.update(
                transition: transition,
                component: AnyComponent(SolidRoundedButtonComponent(
                    title: component.strings.EmojiStatusSetup_SetUntil,
                    icon: nil,
                    theme: SolidRoundedButtonComponent.Theme(theme: component.theme),
                    font: .bold,
                    fontSize: 17.0,
                    height: 50.0,
                    cornerRadius: 10.0,
                    gloss: false,
                    action: { [weak self] in
                        guard let strongSelf = self, let component = strongSelf.component else {
                            return
                        }
                        
                        let timestamp = Int32(strongSelf.pickerView.date.timeIntervalSince1970 - Double(TimeZone.current.secondsFromGMT()))
                        component.apply(timestamp)
                    }
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: 50.0)
            )
            let actionButtonFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - actionButtonSize.width) / 2.0), y: pickerFrame.maxY + pickerSpacing), size: actionButtonSize)
            if let actionButtonComponentView = self.actionButtonView.view {
                if actionButtonComponentView.superview == nil {
                    self.addSubview(actionButtonComponentView)
                }
                transition.setFrame(view: actionButtonComponentView, frame: actionButtonFrame)
            }
            
            self.pickerView.frame = pickerFrame
            
            var size = CGSize(width: availableSize.width, height: actionButtonFrame.maxY)
            if component.bottomInset.isZero {
                size.height += 10.0
            } else {
                size.height += max(10.0, component.bottomInset)
            }
            
            self.backgroundView.update(size: size, cornerRadius: 10.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition.containedViewLayoutTransition)
            
            return size
        }
    }
    
    func makeView() -> View {
        return View(frame: CGRect())
    }
    
    func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
        return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
    }
}

final class EmojiStatusPreviewScreenComponent: Component {
    struct StatusResult {
        let timestamp: Int32
        let sourceView: UIView
    }
    
    final class TransitionAnimation {
        enum TransitionType {
            case animateIn(sourceLayer: CALayer)
        }
        
        let transitionType: TransitionType
        
        init(transitionType: TransitionType) {
            self.transitionType = transitionType
        }
    }
    
    private enum CurrentState {
        case menu
        case timeSelection
    }
    
    typealias EnvironmentType = Empty
    
    let theme: PresentationTheme
    let strings: PresentationStrings
    let bottomInset: CGFloat
    let item: EmojiStatusComponent
    let dismiss: (StatusResult?) -> Void
    
    init(
        theme: PresentationTheme,
        strings: PresentationStrings,
        bottomInset: CGFloat,
        item: EmojiStatusComponent,
        dismiss: @escaping (StatusResult?) -> Void
    ) {
        self.theme = theme
        self.strings = strings
        self.bottomInset = bottomInset
        self.item = item
        self.dismiss = dismiss
    }
    
    static func ==(lhs: EmojiStatusPreviewScreenComponent, rhs: EmojiStatusPreviewScreenComponent) -> Bool {
        if lhs.theme !== rhs.theme {
            return false
        }
        if lhs.strings !== rhs.strings {
            return false
        }
        if lhs.bottomInset != rhs.bottomInset {
            return false
        }
        if lhs.item != rhs.item {
            return false
        }
        return true
    }
    
    final class View: UIView {
        private let backgroundView: BlurredBackgroundView
        private let itemView: ComponentView<Empty>
        private let actionsView: ComponentView<Empty>
        private let timeSelectionView: ComponentView<Empty>
        
        private var currentState: CurrentState = .menu
        
        private var component: EmojiStatusPreviewScreenComponent?
        private weak var state: EmptyComponentState?
        
        override init(frame: CGRect) {
            self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
            self.itemView = ComponentView<Empty>()
            self.actionsView = ComponentView<Empty>()
            self.timeSelectionView = ComponentView<Empty>()
            
            super.init(frame: frame)
            
            self.addSubview(self.backgroundView)
            self.backgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.backgroundTapGesture(_:))))
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        @objc private func backgroundTapGesture(_ recognizer: UITapGestureRecognizer) {
            if case .ended = recognizer.state {
                switch self.currentState {
                case .menu:
                    self.component?.dismiss(nil)
                case .timeSelection:
                    self.toggleState()
                }
            }
        }
        
        private func toggleState() {
            switch self.currentState {
            case .menu:
                self.currentState = .timeSelection
                self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.5, curve: .spring)))
            case .timeSelection:
                self.currentState = .menu
                self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.3, curve: .spring)))
            }
        }
        
        func update(component: EmojiStatusPreviewScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
            self.component = component
            self.state = state
            
            let itemSpacing: CGFloat = 12.0
            
            let itemSize = self.itemView.update(
                transition: transition,
                component: AnyComponent(component.item),
                environment: {},
                containerSize: CGSize(width: 128.0, height: 128.0)
            )
            
            var menuItems: [AnyComponentWithIdentity<ContextMenuActionItemEnvironment>] = []
            let delayDurations: [Int] = [
                1 * 60 * 60,
                2 * 60 * 60,
                8 * 60 * 60,
                2 * 24 * 60 * 60
            ]
            for duration in delayDurations {
                menuItems.append(AnyComponentWithIdentity(id: duration, component: AnyComponent(ContextMenuActionItem(
                    title: setTimeoutForIntervalString(strings: component.strings, value: Int32(duration)),
                    action: { [weak self] in
                        guard let strongSelf = self, let component = strongSelf.component else {
                            return .none
                        }
                        guard let itemComponentView = strongSelf.itemView.view else {
                            return .none
                        }
                        component.dismiss(StatusResult(timestamp: Int32(Date().timeIntervalSince1970) + Int32(duration), sourceView: itemComponentView))
                        return .none
                    }
                ))))
            }
            menuItems.append(AnyComponentWithIdentity(id: "Other", component: AnyComponent(ContextMenuActionItem(
                title: component.strings.EmojiStatusSetup_TimerOther,
                action: { [weak self] in
                    self?.toggleState()
                    return .clearHighlight
                }
            ))))
            
            let actionsSize = self.actionsView.update(
                transition: transition,
                component: AnyComponent(ContextMenuActionsComponent(
                    theme: component.theme,
                    items: menuItems
                )),
                environment: {},
                containerSize: availableSize
            )
            
            let timeSelectionSize = self.timeSelectionView.update(
                transition: transition,
                component: AnyComponent(TimeSelectionControlComponent(
                    theme: component.theme,
                    strings: component.strings,
                    bottomInset: component.bottomInset,
                    apply: { [weak self] timestamp in
                        guard let strongSelf = self, let component = strongSelf.component else {
                            return
                        }
                        guard let itemComponentView = strongSelf.itemView.view else {
                            return
                        }
                        component.dismiss(StatusResult(timestamp: timestamp, sourceView: itemComponentView))
                    },
                    cancel: { [weak self] in
                        self?.toggleState()
                    }
                )),
                environment: {},
                containerSize: availableSize
            )
            
            let totalContentHeight = itemSize.height + itemSpacing + max(actionsSize.height, timeSelectionSize.height)
            
            let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((availableSize.height - totalContentHeight) / 2.0)), size: CGSize(width: availableSize.width, height: totalContentHeight))
            
            let itemFrame = CGRect(origin: CGPoint(x: contentFrame.minX + floor((contentFrame.width - itemSize.width) / 2.0), y: contentFrame.minY), size: itemSize)
            let actionsFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - actionsSize.width) / 2.0), y: itemFrame.maxY + itemSpacing), size: actionsSize)
            
            var timeSelectionFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - timeSelectionSize.width) / 2.0), y: availableSize.height - timeSelectionSize.height), size: timeSelectionSize)
            if case .menu = self.currentState {
                timeSelectionFrame.origin.y = availableSize.height
            }
            
            if let itemComponentView = self.itemView.view {
                if itemComponentView.superview == nil {
                    self.addSubview(itemComponentView)
                }
                transition.setFrame(view: itemComponentView, frame: itemFrame)
            }
            
            if let actionsComponentView = self.actionsView.view {
                if actionsComponentView.superview == nil {
                    self.addSubview(actionsComponentView)
                }
                transition.setPosition(view: actionsComponentView, position: actionsFrame.center)
                transition.setBounds(view: actionsComponentView, bounds: CGRect(origin: CGPoint(), size: actionsFrame.size))
                
                if case .menu = self.currentState {
                    transition.setTransform(view: actionsComponentView, transform: CATransform3DIdentity)
                    transition.setAlpha(view: actionsComponentView, alpha: 1.0)
                    actionsComponentView.isUserInteractionEnabled = true
                } else {
                    transition.setTransform(view: actionsComponentView, transform: CATransform3DMakeScale(0.001, 0.001, 1.0))
                    transition.setAlpha(view: actionsComponentView, alpha: 0.0)
                    actionsComponentView.isUserInteractionEnabled = false
                }
            }
            
            if let timeSelectionComponentView = self.timeSelectionView.view {
                if timeSelectionComponentView.superview == nil {
                    self.addSubview(timeSelectionComponentView)
                }
                transition.setFrame(view: timeSelectionComponentView, frame: timeSelectionFrame)
            }
            
            self.backgroundView.updateColor(color: component.theme.contextMenu.dimColor, transition: .immediate)
            transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: availableSize))
            self.backgroundView.update(size: availableSize, transition: transition.containedViewLayoutTransition)
            
            if let transitionAnimation = transition.userData(TransitionAnimation.self) {
                switch transitionAnimation.transitionType {
                case let .animateIn(sourceLayer):
                    var additionalPositionDifference = CGPoint()
                    if let copyLayer = sourceLayer.snapshotContentTree(), let itemComponentView = self.itemView.view {
                        sourceLayer.isHidden = true
                        copyLayer.frame = sourceLayer.convert(sourceLayer.bounds, to: self.layer)
                        self.layer.addSublayer(copyLayer)
                        
                        copyLayer.animatePosition(from: copyLayer.frame.center, to: itemComponentView.frame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
                        copyLayer.animateScale(from: 1.0, to: itemComponentView.bounds.width / copyLayer.bounds.width, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
                        copyLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak copyLayer] _ in
                            copyLayer?.removeFromSuperlayer()
                        })
                        
                        additionalPositionDifference = CGPoint(x: itemComponentView.frame.center.x - copyLayer.frame.center.x, y: itemComponentView.frame.center.y - copyLayer.frame.center.y)
                        itemComponentView.layer.animatePosition(from: copyLayer.frame.center, to: itemComponentView.frame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
                        itemComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.16)
                        itemComponentView.layer.animateScale(from: copyLayer.bounds.width / itemComponentView.bounds.width, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
                    }
                    
                    self.backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
                    
                    if let actionsComponentView = self.actionsView.view {
                        actionsComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
                        actionsComponentView.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.6)
                        actionsComponentView.layer.animateSpring(from: (-actionsComponentView.bounds.height / 2.0) as NSNumber, to: 0.0 as NSNumber, keyPath: "transform.translation.y", duration: 0.6)
                        
                        let _ = additionalPositionDifference
                    }
                }
            }
            
            return availableSize
        }
        
        func animateOut(targetLayer: CALayer?, completion: @escaping () -> Void) {
            if let targetLayer = targetLayer, let itemComponentView = self.itemView.view {
                targetLayer.isHidden = false
                let targetLayerPosition = targetLayer.position
                let targetLayerSuperlayer = targetLayer.superlayer
                var targetLayerIndexPosition: UInt32?
                if let targetLayerSuperlayer = targetLayerSuperlayer {
                    if let index = targetLayerSuperlayer.sublayers?.firstIndex(of: targetLayer) {
                        targetLayerIndexPosition = UInt32(index)
                    }
                }
                
                let localTargetPosition = targetLayer.convert(targetLayer.bounds.center, to: self.layer)
                self.layer.addSublayer(targetLayer)
                targetLayer.position = localTargetPosition
                
                targetLayer.animatePosition(from: itemComponentView.frame.center, to: localTargetPosition, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
                targetLayer.animateScale(from: itemComponentView.bounds.width / targetLayer.bounds.width, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak targetLayer, weak targetLayerSuperlayer] _ in
                    if let targetLayer = targetLayer, let targetLayerSuperlayer = targetLayerSuperlayer {
                        if let targetLayerIndexPosition = targetLayerIndexPosition {
                            targetLayerSuperlayer.insertSublayer(targetLayer, at: targetLayerIndexPosition)
                            targetLayer.position = targetLayerPosition
                        }
                    }
                    completion()
                })
                targetLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.16)
                
                itemComponentView.layer.animatePosition(from: itemComponentView.frame.center, to: localTargetPosition, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
                itemComponentView.layer.animateScale(from: 1.0, to: targetLayer.bounds.width / itemComponentView.bounds.width, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
                itemComponentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
                
                self.backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
                if let actionsComponentView = self.actionsView.view {
                    actionsComponentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
                    })
                }
                
                if let timeSelectionComponentView = self.timeSelectionView.view {
                    timeSelectionComponentView.layer.animatePosition(from: timeSelectionComponentView.layer.position, to: CGPoint(x: timeSelectionComponentView.layer.position.x, y: self.bounds.height + timeSelectionComponentView.bounds.height / 2.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
                }
            } else {
                self.backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
                if let actionsComponentView = self.actionsView.view {
                    actionsComponentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
                        completion()
                    })
                    
                    if let timeSelectionComponentView = self.timeSelectionView.view {
                        timeSelectionComponentView.layer.animatePosition(from: timeSelectionComponentView.layer.position, to: CGPoint(x: timeSelectionComponentView.layer.position.x, y: self.bounds.height + timeSelectionComponentView.bounds.height / 2.0), duration: 0.2, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
                    }
                } else {
                    completion()
                }
            }
        }
    }
    
    func makeView() -> View {
        return View(frame: CGRect())
    }
    
    func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
        return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
    }
}