import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import ItemListUI
import PresentationDataUtils
import TelegramStringFormatting

class BotCheckoutTipItem: ListViewItem, ItemListItem {
    let theme: PresentationTheme
    let strings: PresentationStrings
    let title: String
    let currency: String
    let value: String
    let numericValue: Int64
    let maxValue: Int64
    let availableVariants: [(String, Int64)]
    let updateValue: (Int64) -> Void
    let updatedFocus: (Bool) -> Void

    let sectionId: ItemListSectionId
    
    let requestsNoInset: Bool = true
    
    init(theme: PresentationTheme, strings: PresentationStrings, title: String, currency: String, value: String, numericValue: Int64, maxValue: Int64, availableVariants: [(String, Int64)], sectionId: ItemListSectionId, updateValue: @escaping (Int64) -> Void, updatedFocus: @escaping (Bool) -> Void) {
        self.theme = theme
        self.strings = strings
        self.title = title
        self.currency = currency
        self.value = value
        self.numericValue = numericValue
        self.maxValue = maxValue
        self.availableVariants = availableVariants
        self.updateValue = updateValue
        self.updatedFocus = updatedFocus
        self.sectionId = sectionId
    }
    
    func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
        async {
            let node = BotCheckoutTipItemNode()
            let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
            
            node.contentSize = layout.contentSize
            node.insets = layout.insets
            
            Queue.mainQueue().async {
                completion(node, {
                    return (nil, { _ in apply() })
                })
            }
        }
    }
    
    func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
        Queue.mainQueue().async {
            if let nodeValue = node() as? BotCheckoutTipItemNode {
                let makeLayout = nodeValue.asyncLayout()
                
                async {
                    let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
                    Queue.mainQueue().async {
                        completion(layout, { _ in
                            apply()
                        })
                    }
                }
            }
        }
    }
    
    let selectable: Bool = false
}

private let titleFont = Font.regular(17.0)
private let finalFont = Font.semibold(17.0)

private func priceItemInsets(_ neighbors: ItemListNeighbors) -> UIEdgeInsets {
    var insets = UIEdgeInsets()
    switch neighbors.top {
        case .otherSection:
            insets.top += 8.0
        case .none, .sameSection:
            break
    }
    switch neighbors.bottom {
        case .none, .otherSection:
            insets.bottom += 8.0
        case .sameSection:
            break
    }
    return insets
}

private final class TipValueNode: ASDisplayNode {
    private let backgroundNode: ASImageNode
    private let titleNode: ImmediateTextNode

    private let button: HighlightTrackingButtonNode

    private var currentBackgroundColor: UIColor?

    var action: (() -> Void)?

    override init() {
        self.backgroundNode = ASImageNode()
        self.titleNode = ImmediateTextNode()

        self.button = HighlightTrackingButtonNode()

        super.init()

        self.addSubnode(self.backgroundNode)
        self.addSubnode(self.titleNode)
        self.addSubnode(self.button)
        self.button.addTarget(self, action:  #selector(self.buttonPressed), forControlEvents: .touchUpInside)
    }

    @objc private func buttonPressed() {
        self.action?()
    }

    func update(theme: PresentationTheme, text: String, isHighlighted: Bool, height: CGFloat) -> (CGFloat, (CGFloat) -> Void) {
        var updateBackground = false
        let backgroundColor = isHighlighted ? theme.list.paymentOption.activeFillColor : theme.list.paymentOption.inactiveFillColor
        if let currentBackgroundColor = self.currentBackgroundColor {
            if !currentBackgroundColor.isEqual(backgroundColor) {
                updateBackground = true
            }
        } else {
            updateBackground = true
        }
        if updateBackground {
            self.currentBackgroundColor = backgroundColor
            self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 20.0, color: backgroundColor)
        }

        self.titleNode.attributedText = NSAttributedString(string: text, font: Font.semibold(15.0), textColor: isHighlighted ? theme.list.paymentOption.activeForegroundColor : theme.list.paymentOption.inactiveForegroundColor)
        let titleSize = self.titleNode.updateLayout(CGSize(width: 200.0, height: height))

        let minWidth: CGFloat = 80.0

        let calculatedWidth = max(titleSize.width + 16.0 * 2.0, minWidth)

        return (calculatedWidth, { calculatedWidth in
            self.titleNode.frame = CGRect(origin: CGPoint(x: floor((calculatedWidth - titleSize.width) / 2.0), y: floor((height - titleSize.height) / 2.0)), size: titleSize)

            let size = CGSize(width: calculatedWidth, height: height)
            self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size)

            self.button.frame = CGRect(origin: CGPoint(), size: size)
        })
    }
}

private final class FormatterImpl: NSObject, UITextFieldDelegate {
    private struct Representation {
        private let format: CurrencyFormat
        private var caretIndex: Int = 0
        private var wholePart: [Int] = []
        private var decimalPart: [Int] = []

        init(string: String, format: CurrencyFormat) {
            self.format = format

            var isDecimalPart = false
            for c in string {
                if c.isNumber {
                    if let value = Int(String(c)) {
                        if isDecimalPart {
                            self.decimalPart.append(value)
                        } else {
                            self.wholePart.append(value)
                        }
                    }
                } else if String(c) == format.decimalSeparator {
                    isDecimalPart = true
                }
            }

            while self.wholePart.count > 1 {
                if self.wholePart[0] != 0 {
                    break
                } else {
                    self.wholePart.removeFirst()
                }
            }
            if self.wholePart.isEmpty {
                self.wholePart = [0]
            }

            while self.decimalPart.count > 1 {
                if self.decimalPart[self.decimalPart.count - 1] != 0 {
                    break
                } else {
                    self.decimalPart.removeLast()
                }
            }
            while self.decimalPart.count < format.decimalDigits {
                self.decimalPart.append(0)
            }

            self.caretIndex = self.wholePart.count
        }

        var minCaretIndex: Int {
            for i in 0 ..< self.wholePart.count {
                if self.wholePart[i] != 0 {
                    return i
                }
            }
            return self.wholePart.count
        }

        mutating func moveCaret(offset: Int) {
            self.caretIndex = max(self.minCaretIndex, min(self.caretIndex + offset, self.wholePart.count + self.decimalPart.count))
        }

        mutating func normalize() {
            while self.wholePart.count > 1 {
                if self.wholePart[0] != 0 {
                    break
                } else {
                    self.wholePart.removeFirst()
                    self.moveCaret(offset: -1)
                }
            }
            if self.wholePart.isEmpty {
                self.wholePart = [0]
            }

            while self.decimalPart.count < format.decimalDigits {
                self.decimalPart.append(0)
            }
            while self.decimalPart.count > format.decimalDigits {
                self.decimalPart.removeLast()
            }

            self.caretIndex = max(self.minCaretIndex, min(self.caretIndex, self.wholePart.count + self.decimalPart.count))
        }

        mutating func backspace() {
            if self.caretIndex > self.wholePart.count {
                let decimalIndex = self.caretIndex - self.wholePart.count
                if decimalIndex > 0 {
                    self.decimalPart.remove(at: decimalIndex - 1)

                    self.moveCaret(offset: -1)
                    self.normalize()
                }
            } else {
                if self.caretIndex > 0 {
                    self.wholePart.remove(at: self.caretIndex - 1)

                    self.moveCaret(offset: -1)
                    self.normalize()
                }
            }
        }

        mutating func insert(letter: String) {
            if letter == "." || letter == "," {
                if self.caretIndex == self.wholePart.count {
                    return
                } else if self.caretIndex < self.wholePart.count {
                    for i in (self.caretIndex ..< self.wholePart.count).reversed() {
                        self.decimalPart.insert(self.wholePart[i], at: 0)
                        self.wholePart.remove(at: i)
                    }
                }

                self.normalize()
            } else if letter.count == 1 && letter[letter.startIndex].isNumber {
                if let value = Int(letter) {
                    if self.caretIndex <= self.wholePart.count {
                        self.wholePart.insert(value, at: self.caretIndex)
                    } else {
                        let decimalIndex = self.caretIndex - self.wholePart.count
                        self.decimalPart.insert(value, at: decimalIndex)
                    }
                    self.moveCaret(offset: 1)
                    self.normalize()
                }
            }
        }

        var string: String {
            var result = ""

            for digit in self.wholePart {
                result.append("\(digit)")
            }
            result.append(self.format.decimalSeparator)
            for digit in self.decimalPart {
                result.append("\(digit)")
            }

            return result
        }

        var stringCaretIndex: Int {
            var logicalIndex = 0
            var resolvedIndex = 0

            if logicalIndex == self.caretIndex {
                return resolvedIndex
            }

            for _ in self.wholePart {
                logicalIndex += 1
                resolvedIndex += 1

                if logicalIndex == self.caretIndex {
                    return resolvedIndex
                }
            }

            resolvedIndex += 1

            for _ in self.decimalPart {
                logicalIndex += 1
                resolvedIndex += 1

                if logicalIndex == self.caretIndex {
                    return resolvedIndex
                }
            }

            return resolvedIndex
        }

        var numericalValue: Int64 {
            var result: Int64 = 0

            for digit in self.wholePart {
                result *= 10
                result += Int64(digit)
            }
            for digit in self.decimalPart {
                result *= 10
                result += Int64(digit)
            }

            return result
        }
    }

    private let format: CurrencyFormat
    private let currency: String
    private let maxNumericalValue: Int64
    private let updated: (Int64) -> Void
    private let focusUpdated: (Bool) -> Void

    private var representation: Representation

    private var previousResolvedCaretIndex: Int = 0
    private var ignoreTextSelection: Bool = false
    private var enableTextSelectionProcessing: Bool = false

    init?(textField: UITextField, currency: String, maxNumericalValue: Int64, initialValue: String, updated: @escaping (Int64) -> Void, focusUpdated: @escaping (Bool) -> Void) {
        guard let format = CurrencyFormat(currency: currency) else {
            return nil
        }
        self.format = format
        self.currency = currency
        self.maxNumericalValue = maxNumericalValue
        self.updated = updated
        self.focusUpdated = focusUpdated

        self.representation = Representation(string: initialValue, format: format)

        super.init()

        textField.text = self.representation.string
        self.previousResolvedCaretIndex = self.representation.stringCaretIndex
    }

    func reset(textField: UITextField, initialValue: String) {
        self.representation = Representation(string: initialValue, format: self.format)
        self.resetFromRepresentation(textField: textField, notifyUpdated: false)
    }

    private func resetFromRepresentation(textField: UITextField, notifyUpdated: Bool) {
        self.ignoreTextSelection = true

        if self.representation.numericalValue > self.maxNumericalValue {
            self.representation = Representation(string: formatCurrencyAmountCustom(self.maxNumericalValue, currency: self.currency).0, format: self.format)
        }

        textField.text = self.representation.string
        self.previousResolvedCaretIndex = self.representation.stringCaretIndex

        if self.enableTextSelectionProcessing {
            let stringCaretIndex = self.representation.stringCaretIndex
            if let caretPosition = textField.position(from: textField.beginningOfDocument, offset: stringCaretIndex) {
                textField.selectedTextRange = textField.textRange(from: caretPosition, to: caretPosition)
            }
        }
        self.ignoreTextSelection = false

        if notifyUpdated {
            self.updated(self.representation.numericalValue)
        }
    }

    @objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if string.count == 1 {
            self.representation.insert(letter: string)
            self.resetFromRepresentation(textField: textField, notifyUpdated: true)
        } else if string.count == 0 {
            self.representation.backspace()
            self.resetFromRepresentation(textField: textField, notifyUpdated: true)
        }

        return false
    }

    @objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        return false
    }

    @objc public func textFieldDidBeginEditing(_ textField: UITextField) {
        self.enableTextSelectionProcessing = true
        self.focusUpdated(true)

        let stringCaretIndex = self.representation.stringCaretIndex
        self.previousResolvedCaretIndex = stringCaretIndex
        if let caretPosition = textField.position(from: textField.beginningOfDocument, offset: stringCaretIndex) {
            self.ignoreTextSelection = true
            textField.selectedTextRange = textField.textRange(from: caretPosition, to: caretPosition)
            DispatchQueue.main.async {
                textField.selectedTextRange = textField.textRange(from: caretPosition, to: caretPosition)
                self.ignoreTextSelection = false
            }
        }
    }

    @objc public func textFieldDidChangeSelection(_ textField: UITextField) {
        if self.ignoreTextSelection {
            return
        }
        if !self.enableTextSelectionProcessing {
            return
        }

        if let selectedTextRange = textField.selectedTextRange {
            let index = textField.offset(from: textField.beginningOfDocument, to: selectedTextRange.end)
            if self.previousResolvedCaretIndex != index {
                self.representation.moveCaret(offset: self.previousResolvedCaretIndex < index ? 1 : -1)

                let stringCaretIndex = self.representation.stringCaretIndex
                self.previousResolvedCaretIndex = stringCaretIndex
                if let caretPosition = textField.position(from: textField.beginningOfDocument, offset: stringCaretIndex) {
                        textField.selectedTextRange = textField.textRange(from: caretPosition, to: caretPosition)
                }
            }
        }
    }

    @objc public func textFieldDidEndEditing(_ textField: UITextField) {
        self.enableTextSelectionProcessing = false
        self.focusUpdated(false)
    }
}

class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate {
    private let backgroundNode: ASDisplayNode
    private let maskNode: ASImageNode
    let titleNode: TextNode
    let labelNode: TextNode
    let tipMeasurementNode: ImmediateTextNode
    let tipCurrencyNode: ImmediateTextNode
    private let textNode: TextFieldNode

    private let scrollNode: ASScrollNode
    private var valueNodes: [TipValueNode] = []
    
    private var item: BotCheckoutTipItem?
    private var formatter: FormatterImpl?
    
    init() {
        self.backgroundNode = ASDisplayNode()

        self.titleNode = TextNode()
        self.titleNode.isUserInteractionEnabled = false

        self.labelNode = TextNode()
        self.labelNode.isUserInteractionEnabled = false
        self.labelNode.isHidden = true

        self.tipMeasurementNode = ImmediateTextNode()
        self.tipCurrencyNode = ImmediateTextNode()

        self.textNode = TextFieldNode()

        self.scrollNode = ASScrollNode()
        self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true
        self.scrollNode.view.showsVerticalScrollIndicator = false
        self.scrollNode.view.showsHorizontalScrollIndicator = false
        self.scrollNode.view.scrollsToTop = false
        self.scrollNode.view.delaysContentTouches = false
        self.scrollNode.view.canCancelContentTouches = true
        if #available(iOS 11.0, *) {
            self.scrollNode.view.contentInsetAdjustmentBehavior = .never
        }
        
        self.maskNode = ASImageNode()
        self.maskNode.isUserInteractionEnabled = false
        
        super.init(layerBacked: false, dynamicBounce: false)

        self.addSubnode(self.backgroundNode)
        
        self.addSubnode(self.titleNode)
        self.addSubnode(self.labelNode)
        self.addSubnode(self.textNode)
        self.addSubnode(self.tipCurrencyNode)
        self.addSubnode(self.scrollNode)

        self.textNode.clipsToBounds = true
        self.textNode.textField.addTarget(self, action: #selector(self.textFieldTextChanged(_:)), for: .editingChanged)
        self.textNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
    }
    
    func asyncLayout() -> (_ item: BotCheckoutTipItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
        let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
        let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
        
        return { item, params, neighbors in
            //let rightInset: CGFloat = 16.0 + params.rightInset

            let labelsContentHeight: CGFloat = 34.0
            
            var contentSize = CGSize(width: params.width, height: labelsContentHeight)
            if !item.availableVariants.isEmpty {
                contentSize.height += 75.0
            }

            let insets = priceItemInsets(neighbors)
            
            let textFont: UIFont
            let textColor: UIColor

            textFont = titleFont
            textColor = item.theme.list.itemSecondaryTextColor
            
            let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: textFont, textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))

            let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.Checkout_OptionalTipItemPlaceholder, font: textFont, textColor: textColor.withMultipliedAlpha(0.8)), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
            
            return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
                if let strongSelf = self {
                    strongSelf.item = item
                    
                    let _ = titleApply()
                    let _ = labelApply()
                    
                    let leftInset: CGFloat = 16.0 + params.leftInset
                    
                    strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((labelsContentHeight - titleLayout.size.height) / 2.0)), size: titleLayout.size)
                    strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: params.width - leftInset - labelLayout.size.width, y: floor((labelsContentHeight - labelLayout.size.height) / 2.0)), size: labelLayout.size)

                    if strongSelf.formatter == nil {
                        strongSelf.formatter = FormatterImpl(textField: strongSelf.textNode.textField, currency: item.currency, maxNumericalValue: item.maxValue, initialValue: item.value, updated: { value in
                            guard let strongSelf = self, let item = strongSelf.item else {
                                return
                            }
                            if item.numericValue != value {
                                item.updateValue(value)
                            }
                        }, focusUpdated: { value in
                            guard let strongSelf = self else {
                                return
                            }
                            if value {
                                strongSelf.item?.updatedFocus(true)
                            }
                        })
                        strongSelf.textNode.textField.delegate = strongSelf.formatter

                        /*strongSelf.formatterDelegate = CurrencyUITextFieldDelegate(formatter: CurrencyFormatter(currency: item.currency, { formatter in
                            formatter.maxValue = currencyToFractionalAmount(value: item.maxValue, currency: item.currency) ?? 10000.0
                            formatter.minValue = 0.0
                            formatter.hasDecimals = true
                        }))
                        strongSelf.formatterDelegate?.passthroughDelegate = strongSelf

                        strongSelf.formatterDelegate?.textUpdated = {
                            guard let strongSelf = self else {
                                return
                            }
                            strongSelf.textFieldTextChanged(strongSelf.textNode.textField)
                        }

                        strongSelf.textNode.textField.delegate = strongSelf.formatterDelegate*/

                        strongSelf.textNode.clipsToBounds = true
                        //strongSelf.textNode.textField.delegate = strongSelf
                    }

                    strongSelf.textNode.textField.typingAttributes = [NSAttributedString.Key.font: titleFont]
                    strongSelf.textNode.textField.font = titleFont

                    strongSelf.textNode.textField.textColor = textColor
                    strongSelf.textNode.textField.textAlignment = .right
                    strongSelf.textNode.textField.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance
                    strongSelf.textNode.textField.keyboardType = .decimalPad
                    strongSelf.textNode.textField.returnKeyType = .next
                    strongSelf.textNode.textField.tintColor = item.theme.list.itemAccentColor

                    var textInputFrame = CGRect(origin: CGPoint(x: params.width - leftInset - 150.0, y: -2.0), size: CGSize(width: 150.0, height: labelsContentHeight))

                    let currencyText: (String, String, Bool) = formatCurrencyAmountCustom(item.numericValue, currency: item.currency)

                    let currencySymbolOnTheLeft = currencyText.2
                    //let currencySymbolOnTheLeft = true

                    if strongSelf.textNode.textField.text ?? "" != currencyText.0 {
                        strongSelf.formatter?.reset(textField: strongSelf.textNode.textField, initialValue: currencyText.0)
                    }

                    strongSelf.tipMeasurementNode.attributedText = NSAttributedString(string: currencyText.0, font: titleFont, textColor: textColor)
                    let inputTextSize = strongSelf.tipMeasurementNode.updateLayout(textInputFrame.size)

                    let spaceRect = NSAttributedString(string: " ", font: titleFont, textColor: textColor).boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)

                    strongSelf.tipCurrencyNode.attributedText = NSAttributedString(string: "\(currencyText.1)", font: titleFont, textColor: textColor)
                    let currencySize = strongSelf.tipCurrencyNode.updateLayout(CGSize(width: 100.0, height: .greatestFiniteMagnitude))
                    if currencySymbolOnTheLeft {
                        strongSelf.tipCurrencyNode.frame = CGRect(origin: CGPoint(x: textInputFrame.maxX - currencySize.width - inputTextSize.width - spaceRect.width, y: floor((labelsContentHeight - currencySize.height) / 2.0) - 1.0), size: currencySize)
                    } else {
                        strongSelf.tipCurrencyNode.frame = CGRect(origin: CGPoint(x: textInputFrame.maxX - currencySize.width, y: floor((labelsContentHeight - currencySize.height) / 2.0) - 1.0), size: currencySize)
                        textInputFrame.origin.x -= currencySize.width + spaceRect.width
                    }

                    strongSelf.textNode.frame = textInputFrame

                    let valueHeight: CGFloat = 52.0
                    let valueY: CGFloat = labelsContentHeight + 9.0

                    var index = 0
                    var variantLayouts: [(CGFloat, (CGFloat) -> Void)] = []
                    var totalMinWidth: CGFloat = 0.0
                    for (variantText, variantValue) in item.availableVariants {
                        let valueNode: TipValueNode
                        if strongSelf.valueNodes.count > index {
                            valueNode = strongSelf.valueNodes[index]
                        } else {
                            valueNode = TipValueNode()
                            strongSelf.valueNodes.append(valueNode)
                            strongSelf.scrollNode.addSubnode(valueNode)
                        }
                        let (nodeMinWidth, nodeApply) = valueNode.update(theme: item.theme, text: variantText, isHighlighted: item.value == variantText, height: valueHeight)
                        valueNode.action = {
                            guard let strongSelf = self, let item = strongSelf.item else {
                                return
                            }
                            if item.numericValue == variantValue {
                                item.updateValue(0)
                            } else {
                                item.updateValue(variantValue)
                            }
                        }
                        totalMinWidth += nodeMinWidth
                        variantLayouts.append((nodeMinWidth, nodeApply))
                        index += 1
                    }

                    let sideInset: CGFloat = params.leftInset + 16.0
                    var scaleFactor: CGFloat = 1.0
                    let availableWidth = params.width - sideInset * 2.0 - CGFloat(max(0, item.availableVariants.count - 1)) * 12.0
                    if totalMinWidth < availableWidth {
                        scaleFactor = availableWidth / totalMinWidth
                    }

                    var variantsOffset: CGFloat = sideInset
                    for index in 0 ..< item.availableVariants.count {
                        if index != 0 {
                            variantsOffset += 12.0
                        }

                        let valueNode: TipValueNode = strongSelf.valueNodes[index]
                        let (minWidth, nodeApply) = variantLayouts[index]

                        let nodeWidth = floor(scaleFactor * minWidth)

                        var valueFrame = CGRect(origin: CGPoint(x: variantsOffset, y: 0.0), size: CGSize(width: nodeWidth, height: valueHeight))
                        if scaleFactor > 1.0 && index == item.availableVariants.count - 1 {
                            valueFrame.size.width = params.width - sideInset - valueFrame.minX
                        }

                        valueNode.frame = valueFrame
                        nodeApply(nodeWidth)
                        variantsOffset += nodeWidth
                    }

                    variantsOffset += 16.0

                    strongSelf.scrollNode.frame = CGRect(origin: CGPoint(x: 0.0, y: valueY), size: CGSize(width: params.width, height: max(0.0, contentSize.height - valueY)))
                    strongSelf.scrollNode.view.contentSize = CGSize(width: variantsOffset, height: strongSelf.scrollNode.frame.height)

                    strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
                    strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width, height: contentSize.height))
                }
            })
        }
    }

    @objc private func dismissKeyboard() {
        self.textNode.textField.resignFirstResponder()
    }

    @objc private func textFieldTextChanged(_ textField: UITextField) {
        let text = textField.text ?? ""
        //self.labelNode.isHidden = !text.isEmpty

        guard let item = self.item else {
            return
        }

        if text.isEmpty {
            item.updateValue(0)
            return
        }

        /*var cleanText = ""
        for c in text {
            if c.isNumber {
                cleanText.append(c)
            } else if c == "," {
                cleanText.append(".")
            }
        }

        guard let doubleValue = Double(cleanText) else {
            return
        }

        if var value = fractionalToCurrencyAmount(value: doubleValue, currency: item.currency) {
            if value > item.maxValue {
                value = item.maxValue

                let currencyText = formatCurrencyAmountCustom(value, currency: item.currency)
                if self.textNode.textField.text ?? "" != currencyText.0 {
                    self.textNode.textField.text = currencyText.0
                }
            }
            item.updateValue(value)
        }*/
    }

    @objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        return true
    }

    @objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        return false
    }

    @objc public func textFieldDidBeginEditing(_ textField: UITextField) {
        textField.selectedTextRange = textField.textRange(from: textField.endOfDocument, to: textField.endOfDocument)

        self.item?.updatedFocus(true)
    }

    @objc public func textFieldDidChangeSelection(_ textField: UITextField) {
        textField.selectedTextRange = textField.textRange(from: textField.endOfDocument, to: textField.endOfDocument)
    }

    @objc public func textFieldDidEndEditing(_ textField: UITextField) {
    }
    
    override func animateInsertion(_ currentTimestamp: Double, duration: Double, options: ListViewItemAnimationOptions) {
        self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
    }
    
    override func animateAdded(_ currentTimestamp: Double, duration: Double) {
        self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
    }
    
    override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
        self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
    }
}