import Foundation
import UIKit
import TelegramCore
import SyncCore
import Postbox
import Display
import AccountContext
import Emoji
import ChatInterfaceState

struct PossibleContextQueryTypes: OptionSet {
    var rawValue: Int32
    
    init() {
        self.rawValue = 0
    }
    
    init(rawValue: Int32) {
        self.rawValue = rawValue
    }
    
    static let emoji = PossibleContextQueryTypes(rawValue: (1 << 0))
    static let hashtag = PossibleContextQueryTypes(rawValue: (1 << 1))
    static let mention = PossibleContextQueryTypes(rawValue: (1 << 2))
    static let command = PossibleContextQueryTypes(rawValue: (1 << 3))
    static let contextRequest = PossibleContextQueryTypes(rawValue: (1 << 4))
    static let emojiSearch = PossibleContextQueryTypes(rawValue: (1 << 5))
}

private func makeScalar(_ c: Character) -> Character {
    return c
}

private let spaceScalar = " " as UnicodeScalar
private let newlineScalar = "\n" as UnicodeScalar
private let hashScalar = "#" as UnicodeScalar
private let atScalar = "@" as UnicodeScalar
private let slashScalar = "/" as UnicodeScalar
private let colonScalar = ":" as UnicodeScalar
private let alphanumerics = CharacterSet.alphanumerics

private func scalarCanPrependQueryControl(_ c: UnicodeScalar?) -> Bool {
    if let c = c {
        if c == " " || c == "\n" || c == "." || c == "," {
            return true
        }
        return false
    } else {
        return true
    }
}

func textInputStateContextQueryRangeAndType(_ inputState: ChatTextInputState) -> [(NSRange, PossibleContextQueryTypes, NSRange?)] {
    if inputState.selectionRange.count != 0 {
        return []
    }
    
    let inputText = inputState.inputText
    let inputString: NSString = inputText.string as NSString
    var results: [(NSRange, PossibleContextQueryTypes, NSRange?)] = []
    let inputLength = inputString.length
    
    if inputLength != 0 {
        if inputString.hasPrefix("@") && inputLength != 1 {
            let startIndex = 1
            var index = startIndex
            var contextAddressRange: NSRange?
            
            while true {
                if index == inputLength {
                    break
                }
                if let c = UnicodeScalar(inputString.character(at: index)) {
                    if c == " " {
                        if index != startIndex {
                            contextAddressRange = NSRange(location: startIndex, length: index - startIndex)
                            index += 1
                        }
                        break
                    } else {
                        if !((c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || (c >= "0" && c <= "9") || c == "_") {
                            break
                        }
                    }
                    
                    if index == inputLength {
                        break
                    } else {
                        index += 1
                    }
                } else {
                    index += 1
                }
            }
            
            if let contextAddressRange = contextAddressRange {
                results.append((contextAddressRange, [.contextRequest], NSRange(location: index, length: inputLength - index)))
            }
        }
        
        let maxIndex = min(inputState.selectionRange.lowerBound, inputLength)
        if maxIndex == 0 {
            return results
        }
        var index = maxIndex - 1
        
        var possibleQueryRange: NSRange?
        
        let string = (inputString as String)
        let trimmedString = string.trimmingTrailingSpaces()
        if string.count < 3, trimmedString.isSingleEmoji {
            return [(NSRange(location: 0, length: inputString.length - (string.count - trimmedString.count)), [.emoji], nil)]
        }
        
        var possibleTypes = PossibleContextQueryTypes([.command, .mention, .hashtag, .emojiSearch])
        var definedType = false
        
        while true {
            var previousC: UnicodeScalar?
            if index != 0 {
                previousC = UnicodeScalar(inputString.character(at: index - 1))
            }
            if let c = UnicodeScalar(inputString.character(at: index)) {
                if c == spaceScalar || c == newlineScalar {
                    possibleTypes = []
                } else if c == hashScalar {
                    if scalarCanPrependQueryControl(previousC) {
                        possibleTypes = possibleTypes.intersection([.hashtag])
                        definedType = true
                        index += 1
                        possibleQueryRange = NSRange(location: index, length: maxIndex - index)
                    }
                    break
                } else if c == atScalar {
                    if scalarCanPrependQueryControl(previousC) {
                        possibleTypes = possibleTypes.intersection([.mention])
                        definedType = true
                        index += 1
                        possibleQueryRange = NSRange(location: index, length: maxIndex - index)
                    }
                    break
                    } else if c == slashScalar {
                        if scalarCanPrependQueryControl(previousC) {
                        possibleTypes = possibleTypes.intersection([.command])
                        definedType = true
                        index += 1
                        possibleQueryRange = NSRange(location: index, length: maxIndex - index)
                    }
                    break
                } else if c == colonScalar {
                    if scalarCanPrependQueryControl(previousC) {
                        possibleTypes = possibleTypes.intersection([.emojiSearch])
                        definedType = true
                        index += 1
                        possibleQueryRange = NSRange(location: index, length: maxIndex - index)
                    }
                    break
                }
            }
            
            if index == 0 {
                break
            } else {
                index -= 1
                possibleQueryRange = NSRange(location: index, length: maxIndex - index)
            }
        }
        
        if let possibleQueryRange = possibleQueryRange, definedType && !possibleTypes.isEmpty {
            results.append((possibleQueryRange, possibleTypes, nil))
        }
    }
    return results
}

func inputContextQueriesForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState) -> [ChatPresentationInputQuery] {
    let inputState = chatPresentationInterfaceState.interfaceState.effectiveInputState
    let inputString: NSString = inputState.inputText.string as NSString
    var result: [ChatPresentationInputQuery] = []
    for (possibleQueryRange, possibleTypes, additionalStringRange) in textInputStateContextQueryRangeAndType(inputState) {
        let query = inputString.substring(with: possibleQueryRange)
        if possibleTypes == [.emoji] {
            result.append(.emoji(query.basicEmoji.0))
        } else if possibleTypes == [.hashtag] {
            result.append(.hashtag(query))
        } else if possibleTypes == [.mention] {
            var types: ChatInputQueryMentionTypes = [.members]
            if possibleQueryRange.lowerBound == 1 {
                types.insert(.contextBots)
            }
            result.append(.mention(query: query, types: types))
        } else if possibleTypes == [.command] {
            result.append(.command(query))
        } else if possibleTypes == [.contextRequest], let additionalStringRange = additionalStringRange {
            let additionalString = inputString.substring(with: additionalStringRange)
            result.append(.contextRequest(addressName: query, query: additionalString))
        } else if possibleTypes == [.emojiSearch], !query.isEmpty, let inputLanguage = chatPresentationInterfaceState.interfaceState.inputLanguage {
            result.append(.emojiSearch(query: query, languageCode: inputLanguage, range: possibleQueryRange))
        }
    }
    return result
}

func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext) -> ChatTextInputPanelState {
    var contextPlaceholder: NSAttributedString?
    loop: for (_, result) in chatPresentationInterfaceState.inputQueryResults {
        if case let .contextRequestResult(peer, _) = result, let botUser = peer as? TelegramUser, let botInfo = botUser.botInfo, let inlinePlaceholder = botInfo.inlinePlaceholder {
            let inputQueries = inputContextQueriesForChatPresentationIntefaceState(chatPresentationInterfaceState)
            for inputQuery in inputQueries {
                if case let .contextRequest(addressName, query) = inputQuery, query.isEmpty {
                    let baseFontSize: CGFloat = max(17.0, chatPresentationInterfaceState.fontSize.baseDisplaySize)
                    
                    let string = NSMutableAttributedString()
                    string.append(NSAttributedString(string: "@" + addressName, font: Font.regular(baseFontSize), textColor: UIColor.clear))
                    string.append(NSAttributedString(string: " " + inlinePlaceholder, font: Font.regular(baseFontSize), textColor: chatPresentationInterfaceState.theme.chat.inputPanel.inputPlaceholderColor))
                    contextPlaceholder = string
                }
            }
            
            break loop
        }
    }
    
    var accessoryItems: [ChatTextInputAccessoryItem] = []
    if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
        var extendedSearchLayout = false
        loop: for (_, result) in chatPresentationInterfaceState.inputQueryResults {
            if case let .contextRequestResult(peer, _) = result, peer != nil {
                extendedSearchLayout = true
                break loop
            }
        }
        
        if !extendedSearchLayout {
            accessoryItems.append(.messageAutoremoveTimeout(peer.messageAutoremoveTimeout))
        }
    }
    
    switch chatPresentationInterfaceState.inputMode {
        case .media:
            accessoryItems.append(.keyboard)
            return ChatTextInputPanelState(accessoryItems: accessoryItems, contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
        case .inputButtons:
            return ChatTextInputPanelState(accessoryItems: [.keyboard], contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
        case .none, .text:
            if let _ = chatPresentationInterfaceState.interfaceState.editMessage {
                return ChatTextInputPanelState(accessoryItems: [], contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
            } else {
                var accessoryItems: [ChatTextInputAccessoryItem] = []
                if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
                    var extendedSearchLayout = false
                    loop: for (_, result) in chatPresentationInterfaceState.inputQueryResults {
                        if case let .contextRequestResult(peer, _) = result, peer != nil {
                            extendedSearchLayout = true
                            break loop
                        }
                    }
                    
                    if !extendedSearchLayout {
                        accessoryItems.append(.messageAutoremoveTimeout(peer.messageAutoremoveTimeout))
                    }
                }
                
                if chatPresentationInterfaceState.interfaceState.composeInputState.inputText.length == 0 && chatPresentationInterfaceState.interfaceState.forwardMessageIds == nil {
                    if chatPresentationInterfaceState.hasScheduledMessages {
                        accessoryItems.append(.scheduledMessages)
                    }
                    
                    var stickersEnabled = true
                    if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
                        if case .broadcast = peer.info, canSendMessagesToPeer(peer) {
                            accessoryItems.append(.silentPost(chatPresentationInterfaceState.interfaceState.silentPosting))
                        }
                        if peer.hasBannedPermission(.banSendStickers) != nil {
                            stickersEnabled = false
                        }
                    } else if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
                        if peer.hasBannedPermission(.banSendStickers) {
                            stickersEnabled = false
                        }
                    }
                    if let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, let _ = peer.botInfo {
                        accessoryItems.append(.commands)
                    } else if chatPresentationInterfaceState.hasBots {
                        accessoryItems.append(.commands)
                    }
                    accessoryItems.append(.stickers(stickersEnabled))
                    if let message = chatPresentationInterfaceState.keyboardButtonsMessage, let _ = message.visibleButtonKeyboardMarkup {
                        accessoryItems.append(.inputButtons)
                    }
                }
                return ChatTextInputPanelState(accessoryItems: accessoryItems, contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
            }
    }
}