import Foundation
import SwiftSignalKit
import TextFormat
import TelegramCore
import AccountContext

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

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

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

public func textInputStateContextQueryRangeAndType(_ inputState: ChatTextInputState) -> [(NSRange, PossibleContextQueryTypes, NSRange?)] {
    return textInputStateContextQueryRangeAndType(inputText: inputState.inputText, selectionRange: inputState.selectionRange)
}

public func textInputStateContextQueryRangeAndType(inputText: NSAttributedString, selectionRange: Range<Int>) -> [(NSRange, PossibleContextQueryTypes, NSRange?)] {
    if selectionRange.count != 0 {
        return []
    }
    
    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(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 {
            if inputText.attribute(ChatTextInputAttributes.customEmoji, at: 0, effectiveRange: nil) == nil {
                return [(NSRange(location: 0, length: inputString.length - (string.count - trimmedString.count)), [.emoji], nil)]
            }
        } else {
            /*let activeString = inputText.attributedSubstring(from: NSRange(location: 0, length: inputState.selectionRange.upperBound))
            if let lastCharacter = activeString.string.last, String(lastCharacter).isSingleEmoji {
                let matchLength = (String(lastCharacter) as NSString).length
                
                if activeString.attribute(ChatTextInputAttributes.customEmoji, at: activeString.length - matchLength, effectiveRange: nil) == nil {
                    return [(NSRange(location: inputState.selectionRange.upperBound - matchLength, length: matchLength), [.emojiSearch], 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
}

public enum ChatPresentationInputQueryKind: Int32 {
    case emoji
    case hashtag
    case mention
    case command
    case contextRequest
    case emojiSearch
}

public struct ChatInputQueryMentionTypes: OptionSet, Hashable {
    public var rawValue: Int32
    
    public init(rawValue: Int32) {
        self.rawValue = rawValue
    }
    
    public static let contextBots = ChatInputQueryMentionTypes(rawValue: 1 << 0)
    public static let members = ChatInputQueryMentionTypes(rawValue: 1 << 1)
    public static let accountPeer = ChatInputQueryMentionTypes(rawValue: 1 << 2)
}

public enum ChatPresentationInputQuery: Hashable, Equatable {
    case emoji(String)
    case hashtag(String)
    case mention(query: String, types: ChatInputQueryMentionTypes)
    case command(String)
    case emojiSearch(query: String, languageCode: String, range: NSRange)
    case contextRequest(addressName: String, query: String)
    
    public var kind: ChatPresentationInputQueryKind {
        switch self {
            case .emoji:
                return .emoji
            case .hashtag:
                return .hashtag
            case .mention:
                return .mention
            case .command:
                return .command
            case .contextRequest:
                return .contextRequest
            case .emojiSearch:
                return .emojiSearch
        }
    }
}

public enum ChatContextQueryError {
    case generic
    case inlineBotLocationRequest(EnginePeer.Id)
}

public enum ChatContextQueryUpdate {
    case remove
    case update(ChatPresentationInputQuery, Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError>)
}