// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//

class TestNSTextCheckingResult: XCTestCase {
    func test_textCheckingResult() {
       let patternString = "(a|b)x|123|(?<aname>c|d)y"
       do {
           let patternOptions: NSRegularExpression.Options = []
           let regex = try NSRegularExpression(pattern: patternString, options: patternOptions)
           let searchString = "1x030cy"
           let searchOptions: NSRegularExpression.MatchingOptions = []
           let searchRange = NSRange(location: 0, length: 7)
           let match: NSTextCheckingResult =  regex.firstMatch(in: searchString, options: searchOptions, range: searchRange)!
           //Positive offset
           var result = match.adjustingRanges(offset: 1)
           XCTAssertEqual(result.range(at: 0).location, 6)
           XCTAssertEqual(result.range(at: 1).location, NSNotFound)
           XCTAssertEqual(result.range(at: 2).location, 6)
           if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
               XCTAssertEqual(result.range(withName: "aname").location, 6)
           }
           //Negative offset
           result = match.adjustingRanges(offset: -2)
           XCTAssertEqual(result.range(at: 0).location, 3)
           XCTAssertEqual(result.range(at: 1).location, NSNotFound)
           XCTAssertEqual(result.range(at: 2).location, 3)
           if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
               XCTAssertEqual(result.range(withName: "aname").location, 3)
           }
           //ZeroOffset
           result = match.adjustingRanges(offset: 0)
           XCTAssertEqual(result.range(at: 0).location, 5)
           XCTAssertEqual(result.range(at: 1).location, NSNotFound)
           XCTAssertEqual(result.range(at: 2).location, 5)
           if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
               XCTAssertEqual(result.range(withName: "aname").location, 5)
           }
       } catch {
            XCTFail("Unable to build regular expression for pattern \(patternString)")
       }
    }

    func test_multipleMatches() {
        let patternString = "(?<name>hello)[0-9]"

        do {
            let regex = try NSRegularExpression(pattern: patternString, options: [])
            let searchString = "hello1 hello2"
            let searchRange = NSRange(location: 0, length: searchString.count)
            let matches = regex.matches(in: searchString, options: [], range: searchRange)
            XCTAssertEqual(matches.count, 2)
            XCTAssertEqual(matches[0].numberOfRanges, 2)
            XCTAssertEqual(matches[0].range, NSRange(location: 0, length: 6))
            XCTAssertEqual(matches[0].range(at: 0), NSRange(location: 0, length: 6))
            XCTAssertEqual(matches[0].range(at: 1), NSRange(location: 0, length: 5))
            if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
                XCTAssertEqual(matches[0].range(withName: "name"), NSRange(location: 0, length: 5))
            }
            XCTAssertEqual(matches[1].numberOfRanges, 2)
            XCTAssertEqual(matches[1].range, NSRange(location: 7, length: 6))
            XCTAssertEqual(matches[1].range(at: 0), NSRange(location: 7, length: 6))
            XCTAssertEqual(matches[1].range(at: 1), NSRange(location: 7, length: 5))
            if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
                XCTAssertEqual(matches[1].range(withName: "name"), NSRange(location: 7, length: 5))
            }
        } catch {
            XCTFail("Unable to build regular expression for pattern \(patternString)")
        }
    }


    func test_rangeWithName() {
        guard #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) else {
            return
        }

        let patternString = "(?<name1>hel)lo, (?<name2>worl)d"

        do {
            let regex = try NSRegularExpression(pattern: patternString, options: [])
            let searchString = "hello, world"
            let searchRange = NSRange(location: 0, length: searchString.count)
            let matches = regex.matches(in: searchString, options: [], range: searchRange)
            XCTAssertEqual(matches.count, 1)
            XCTAssertEqual(matches[0].numberOfRanges, 3)
            XCTAssertEqual(matches[0].range(withName: "incorrect").location, NSNotFound)
            XCTAssertEqual(matches[0].range(withName: "name1"), NSRange(location: 0, length: 3))
            XCTAssertEqual(matches[0].range(withName: "name2"), NSRange(location: 7, length: 4))
        } catch {
            XCTFail("Unable to build regular expression for pattern \(patternString)")
        }
    }
    
    let fixtures = [
        Fixtures.textCheckingResultSimpleRegex,
        Fixtures.textCheckingResultExtendedRegex,
        Fixtures.textCheckingResultComplexRegex,
    ]
    
    private func areEqual(_ lhs: NSTextCheckingResult, _ rhs: NSTextCheckingResult) -> Bool {
        guard lhs.resultType == rhs.resultType else { return false }
        guard lhs.numberOfRanges == rhs.numberOfRanges else { return false }
        
        for i in 0 ..< lhs.numberOfRanges {
            guard lhs.range(at: i) == rhs.range(at: i) else {
                return false
            }
        }
        
        guard lhs.regularExpression == rhs.regularExpression else {
            return false
        }
        
        return true
    }
    
    func test_codingRoundtrip() throws {
        for fixture in fixtures {
            try fixture.assertValueRoundtripsInCoder(secureCoding: true, matchingWith: areEqual(_:_:))
        }
    }
    
    func test_loadedVauesMatch() throws {
        for fixture in fixtures {
            try fixture.assertLoadedValuesMatch(areEqual(_:_:))
        }
    }
    
    static var allTests: [(String, (TestNSTextCheckingResult) -> () throws -> Void)] {
        return [
            ("test_textCheckingResult", test_textCheckingResult),
            ("test_multipleMatches", test_multipleMatches),
            ("test_rangeWithName", test_rangeWithName),
            ("test_codingRoundtrip", test_codingRoundtrip),
            ("test_loadedVauesMatch", test_loadedVauesMatch),
        ]
    }
}
