//
//  FuzzyMatcher.swift
//  ComposeUI
//
//  Created by Shadowfacts on 10/10/20.
//  Copyright © 2020 Shadowfacts. All rights reserved.
//

import Foundation

struct FuzzyMatcher {
    
    private init() {}
    
    /// Rudimentary string fuzzy matching algorithm.
    ///
    /// Operates on UTF-8 code points, so attempting to match strings which include characters composed of
    /// multiple code points may produce unexpected results.
    ///
    /// Scoring is as follows:
    /// +2 points for every char in `pattern` that occurs in `str` sequentially
    /// -2 points for every char in `pattern` that does not occur in `str` sequentially
    /// -1 point for every char in `str` skipped between matching chars from the `pattern`
    static func match(pattern: String, str: String) -> (matched: Bool, score: Int) {
        let pattern = pattern.lowercased()
        let str = str.lowercased()
        
        var patternIndex = pattern.utf8.startIndex
        var lastStrMatchIndex: String.UTF8View.Index?
        var strIndex = str.utf8.startIndex
        
        var score = 0
        
        while patternIndex < pattern.utf8.endIndex && strIndex < str.utf8.endIndex {
            let patternChar = pattern.utf8[patternIndex]
            let strChar = str.utf8[strIndex]
            if patternChar == strChar {
                let distance = str.utf8.distance(from: lastStrMatchIndex ?? str.utf8.startIndex, to: strIndex)
                if distance > 1 {
                    score -= distance - 1
                }
                
                patternIndex = pattern.utf8.index(after: patternIndex)
                lastStrMatchIndex = strIndex
                strIndex = str.utf8.index(after: strIndex)
                
                score += 2
            } else {
                strIndex = str.utf8.index(after: strIndex)
                
                if strIndex >= str.utf8.endIndex {
                    patternIndex = pattern.utf8.index(after: patternIndex)
                    strIndex = str.utf8.index(after: lastStrMatchIndex ?? str.utf8.startIndex)
                    score -= 2
                }
            }
        }
        
        return (score > 0, score)
    }
    
}
