import SwiftSyntax

public extension SwiftLintFile {
    /// This function determines if given a scope with a left/right brace, such as a function, closure, type, etc, how
    /// many lines the "body" spans when you ignore lines only containing comments and/or whitespace.
    ///
    /// - parameter leftBraceLine:  The line where the scope's left opening brace is located.
    /// - parameter rightBraceLine: The line where the scope's right closing brace is located.
    ///
    /// - returns: The number of effective lines of the body ignoring lines only containing comments and/or whitespace.
    func bodyLineCountIgnoringCommentsAndWhitespace(
        leftBraceLine: Int, rightBraceLine: Int
    ) -> Int {
        // Ignore left/right brace lines
        let startLine = min(leftBraceLine + 1, rightBraceLine - 1)
        let endLine = max(rightBraceLine - 1, leftBraceLine + 1)
        // Add one because if `endLine == startLine` it's still a one-line "body". Here are some examples:
        //
        //   # 1 line
        //   {}
        //
        //   # 1 line
        //   {
        //   }
        //
        //   # 1 line
        //   {
        //     print("foo")
        //   }
        //
        //   # 2 lines
        //   {
        //     let sum = 1 + 2
        //     print(sum)
        //   }
        let totalNumberOfLines = 1 + endLine - startLine
        let numberOfCommentAndWhitespaceOnlyLines = Set(startLine...endLine).subtracting(linesWithTokens).count
        return totalNumberOfLines - numberOfCommentAndWhitespaceOnlyLines
    }

    func computeLinesWithTokens() -> Set<Int> {
        let locationConverter = locationConverter
        return syntaxTree
            .tokens(viewMode: .sourceAccurate)
            .reduce(into: []) { linesWithTokens, token in
                if case .stringSegment = token.tokenKind {
                    let sourceRange = token.sourceRange(
                        converter: locationConverter,
                        afterLeadingTrivia: true,
                        afterTrailingTrivia: true
                    )
                    linesWithTokens.formUnion(sourceRange.start.line...sourceRange.end.line)
                } else {
                    let line = locationConverter.location(for: token.positionAfterSkippingLeadingTrivia).line
                    linesWithTokens.insert(line)
                }
            }
    }
}
