import Foundation

public enum EditorActiveLineTracker {
    public static func lines(from source: String, activeLineIndex: Int) -> [EditorLine] {
        var lines: [EditorLine] = []
        var lineStart = source.startIndex
        var utf16Location = 0
        var index = 0

        while lineStart < source.endIndex {
            let lineEnd = source[lineStart...].firstIndex(of: "\n") ?? source.endIndex
            let line = String(source[lineStart..<lineEnd])
            let length = line.utf16.count
            lines.append(EditorLine(
                index: index,
                source: line,
                range: NSRange(location: utf16Location, length: length),
                mode: index == activeLineIndex ? .source : .rendered
            ))

            if lineEnd == source.endIndex {
                lineStart = lineEnd
                utf16Location += length
            } else {
                lineStart = source.index(after: lineEnd)
                utf16Location += length + 1
            }
            index += 1
        }

        if source.isEmpty || source.hasSuffix("\n") {
            lines.append(EditorLine(
                index: index,
                source: "",
                range: NSRange(location: utf16Location, length: 0),
                mode: index == activeLineIndex ? .source : .rendered
            ))
        }

        return lines
    }

    public static func lineIndex(containing location: Int, in source: String) -> Int {
        let clampedLocation = max(0, min(location, source.utf16.count))
        var currentLocation = 0

        for (index, line) in source.split(separator: "\n", omittingEmptySubsequences: false).enumerated() {
            let length = line.utf16.count
            if clampedLocation <= currentLocation + length {
                return index
            }
            currentLocation += length + 1
        }

        return 0
    }

    public static func clampedSelection(_ selection: EditorSelection, in source: String) -> EditorSelection {
        let sourceLength = source.utf16.count
        let location = max(0, min(selection.location, sourceLength))
        let length = max(0, min(selection.length, sourceLength - location))
        return EditorSelection(location: location, length: length)
    }
}
