// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// RUN: %target-run-simple-swift
// REQUIRES: executable_test
// REQUIRES: objc_interop

import Foundation

#if FOUNDATION_XCTEST
import XCTest
class TestCharacterSetSuper : XCTestCase { }
#else
import StdlibUnittest
class TestCharacterSetSuper { }
#endif

class TestCharacterSet : TestCharacterSetSuper {
    let capitalA = UnicodeScalar(0x0041)! // LATIN CAPITAL LETTER A
    let capitalB = UnicodeScalar(0x0042)! // LATIN CAPITAL LETTER B
    let capitalC = UnicodeScalar(0x0043)! // LATIN CAPITAL LETTER C
    
    func testBasicConstruction() {
        // Create a character set
        let cs = CharacterSet.letters
        
        // Use some method from it
        let invertedCs = cs.inverted
        expectTrue(!invertedCs.contains(capitalA), "Character set must not contain our letter")
        
        // Use another method from it
        let originalCs = invertedCs.inverted
        
        expectTrue(originalCs.contains(capitalA), "Character set must contain our letter")
    }
    
    func testMutability_copyOnWrite() {
        var firstCharacterSet = CharacterSet(charactersIn: "ABC")
        expectTrue(firstCharacterSet.contains(capitalA), "Character set must contain our letter")
        expectTrue(firstCharacterSet.contains(capitalB), "Character set must contain our letter")
        expectTrue(firstCharacterSet.contains(capitalC), "Character set must contain our letter")
        
        // Make a 'copy' (just the struct)
        var secondCharacterSet = firstCharacterSet
        // first: ABC, second: ABC
        
        // Mutate first and verify that it has correct content
        firstCharacterSet.remove(charactersIn: "A")
        // first: BC, second: ABC
        
        expectTrue(!firstCharacterSet.contains(capitalA), "Character set must not contain our letter")
        expectTrue(secondCharacterSet.contains(capitalA), "Copy should not have been mutated")
        
        // Make a 'copy' (just the struct) of the second set, mutate it
        let thirdCharacterSet = secondCharacterSet
        // first: BC, second: ABC, third: ABC
        
        secondCharacterSet.remove(charactersIn: "B")
        // first: BC, second: AC, third: ABC
        
        expectTrue(firstCharacterSet.contains(capitalB), "Character set must contain our letter")
        expectTrue(!secondCharacterSet.contains(capitalB), "Character set must not contain our letter")
        expectTrue(thirdCharacterSet.contains(capitalB), "Character set must contain our letter")
        
        firstCharacterSet.remove(charactersIn: "C")
        // first: B, second: AC, third: ABC
        
        expectTrue(!firstCharacterSet.contains(capitalC), "Character set must not contain our letter")
        expectTrue(secondCharacterSet.contains(capitalC), "Character set must not contain our letter")
        expectTrue(thirdCharacterSet.contains(capitalC), "Character set must contain our letter")
    }

    func testMutability_mutableCopyCrash() {
        let cs = CharacterSet(charactersIn: "ABC")
        (cs as NSCharacterSet).mutableCopy() // this should not crash
    }
    
    func testMutability_SR_1782() {
        var nonAlphanumeric = CharacterSet.alphanumerics.inverted
        nonAlphanumeric.remove(charactersIn: " ") // this should not crash
    }

    func testRanges() {
        // Simple range check
        let asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)!...UnicodeScalar(0x5A)!)
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)!))
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))
        expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5B)!))
        
        // Some string filtering tests
        let asciiLowercase = CharacterSet(charactersIn: UnicodeScalar(0x61)!...UnicodeScalar(0x7B)!)
        let testString = "helloHELLOhello"
        let expected = "HELLO"
        
        let result = testString.trimmingCharacters(in: asciiLowercase)
        expectEqual(result, expected)
    }

    func testClosedRanges_SR_2988() {
      // "CharacterSet.insert(charactersIn: ClosedRange) crashes on a closed ClosedRange<UnicodeScalar> containing U+D7FF"
      let problematicChar = UnicodeScalar(0xD7FF)!
      let range = capitalA...problematicChar
      var characters = CharacterSet(charactersIn: range) // this should not crash
      expectTrue(characters.contains(problematicChar))
      characters.remove(charactersIn: range) // this should not crash
      expectTrue(!characters.contains(problematicChar))
      characters.insert(charactersIn: range) // this should not crash
      expectTrue(characters.contains(problematicChar))
    }

    func testInsertAndRemove() {
        var asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)!...UnicodeScalar(0x5A)!)
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)!))
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))
        
        asciiUppercase.remove(UnicodeScalar(0x49)!)
        expectTrue(!asciiUppercase.contains(UnicodeScalar(0x49)!))
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))
       

        // Zero-length range
        asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)!..<UnicodeScalar(0x41)!)
        expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))

        asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)!..<UnicodeScalar(0x42)!)
        expectTrue(!asciiUppercase.contains(UnicodeScalar(0x41)!))
        
        asciiUppercase.remove(charactersIn: "Z")
        expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5A)))
    }
    
    func testBasics() {
        
        var result : [String] = []
        
        let string = "The quick, brown, fox jumps over the lazy dog - because, why not?"
        var set = CharacterSet(charactersIn: ",-")
        result = string.components(separatedBy: set)
        expectEqual(5, result.count)
        
        set.remove(charactersIn: ",")
        set.insert(charactersIn: " ")
        result = string.components(separatedBy: set)
        expectEqual(14, result.count)
        
        set.remove(" ".unicodeScalars.first!)
        result = string.components(separatedBy: set)
        expectEqual(2, result.count)
    }

    // MARK: -
    func test_classForCoder() {
        // confirm internal bridged impl types are not exposed to archival machinery
        let cs = CharacterSet() as NSCharacterSet
        
        // Either of the following two are OK
        let expectedImmutable: AnyClass = NSCharacterSet.self as AnyClass
        let expectedMutable: AnyClass = NSMutableCharacterSet.self as AnyClass
        
        let actualClass: AnyClass = cs.classForCoder
        let actualClassForCoder: AnyClass = cs.classForKeyedArchiver!
        
        expectTrue(actualClass == expectedImmutable || actualClass == expectedMutable)
        expectTrue(actualClassForCoder == expectedImmutable || actualClassForCoder == expectedMutable)
    }

    func test_AnyHashableContainingCharacterSet() {
        let values: [CharacterSet] = [
            CharacterSet(charactersIn: "ABC"),
            CharacterSet(charactersIn: "XYZ"),
            CharacterSet(charactersIn: "XYZ")
        ]
        let anyHashables = values.map(AnyHashable.init)
        expectEqual(CharacterSet.self, type(of: anyHashables[0].base))
        expectEqual(CharacterSet.self, type(of: anyHashables[1].base))
        expectEqual(CharacterSet.self, type(of: anyHashables[2].base))
        expectNotEqual(anyHashables[0], anyHashables[1])
        expectEqual(anyHashables[1], anyHashables[2])
    }

    func test_AnyHashableCreatedFromNSCharacterSet() {
        let values: [NSCharacterSet] = [
            NSCharacterSet(charactersIn: "ABC"),
            NSCharacterSet(charactersIn: "XYZ"),
            NSCharacterSet(charactersIn: "XYZ"),
        ]
        let anyHashables = values.map(AnyHashable.init)
        expectEqual(CharacterSet.self, type(of: anyHashables[0].base))
        expectEqual(CharacterSet.self, type(of: anyHashables[1].base))
        expectEqual(CharacterSet.self, type(of: anyHashables[2].base))
        expectNotEqual(anyHashables[0], anyHashables[1])
        expectEqual(anyHashables[1], anyHashables[2])
    }

    func test_superSet() {
        let a = CharacterSet.letters.isSuperset(of: CharacterSet(charactersIn: "ab"))
        expectTrue(a)
    }

    func test_union() {
        let union = CharacterSet(charactersIn: "ab").union(CharacterSet(charactersIn: "cd"))
        let expected = CharacterSet(charactersIn: "abcd")
        expectEqual(expected, union)
    }

    func test_subtracting() {
        let difference = CharacterSet(charactersIn: "abc").subtracting(CharacterSet(charactersIn: "b"))
        let expected = CharacterSet(charactersIn: "ac")
        expectEqual(expected, difference)
    }

    func test_subtractEmptySet() {
        var mutableSet = CharacterSet(charactersIn: "abc")
        let emptySet = CharacterSet()
        mutableSet.subtract(emptySet)
        let expected = CharacterSet(charactersIn: "abc")
        expectEqual(expected, mutableSet)
    }

    func test_subtractNonEmptySet() {
        var mutableSet = CharacterSet()
        let nonEmptySet = CharacterSet(charactersIn: "abc")
        mutableSet.subtract(nonEmptySet)
        expectTrue(mutableSet.isEmpty)
    }

    func test_symmetricDifference() {
        let symmetricDifference = CharacterSet(charactersIn: "ac").symmetricDifference(CharacterSet(charactersIn: "b"))
        let expected = CharacterSet(charactersIn: "abc")
        expectEqual(expected, symmetricDifference)
    }

    func test_hasMember() {
        let contains = CharacterSet.letters.hasMember(inPlane: 1)
        expectTrue(contains)
    }

    func test_bitmap() {
        let bitmap = CharacterSet(charactersIn: "ab").bitmapRepresentation
        expectEqual(0x6, bitmap[12])
        expectEqual(8192, bitmap.count)
    }
    
    func test_setOperationsOfEmptySet() {
        // The following tests pass on these versions of the OS
        if #available(OSX 10.12.3, iOS 10.3, watchOS 3.2, tvOS 10.2, *) {
            let emptySet = CharacterSet()
            let abcSet = CharacterSet(charactersIn: "abc")
            
            expectTrue(abcSet.isSuperset(of: emptySet))
            expectTrue(emptySet.isSuperset(of: emptySet))
            expectFalse(emptySet.isSuperset(of: abcSet))
            
            expectTrue(abcSet.isStrictSuperset(of: emptySet))
            expectFalse(emptySet.isStrictSuperset(of: emptySet))
            expectFalse(emptySet.isStrictSuperset(of: abcSet))
            
            expectTrue(emptySet.isSubset(of: abcSet))
            expectTrue(emptySet.isSubset(of: emptySet))
            expectFalse(abcSet.isSubset(of: emptySet))
            
            expectTrue(emptySet.isStrictSubset(of: abcSet))
            expectFalse(emptySet.isStrictSubset(of: emptySet))
            expectFalse(abcSet.isStrictSubset(of: emptySet))
            expectFalse(abcSet.isStrictSubset(of: abcSet))
            
            expectEqual(emptySet, emptySet)
            expectNotEqual(abcSet, emptySet)
        }
    }
    
    func test_moreSetOperations() {
        // previous to these releases the subset methods improperly calculated strict subsets
        // as of macOS 10.12.4, iOS 10.3, watchOS 3.2 and tvOS 10.2 CoreFoundation had a bug
        // fix that corrected this behavior.
        // TODO: figure out why the simulator is claiming this as a failure.
        // https://bugs.swift.org/browse/SR-4457

        /* Disabled now: rdar://problem/31746923
        #if os(macOS)
            if #available(OSX 10.12.4, iOS 10.3, watchOS 3.2, tvOS 10.2, *) {
                let abcSet = CharacterSet(charactersIn: "abc")
                let abcdSet = CharacterSet(charactersIn: "abcd")
                
                expectEqual(abcSet, abcSet)
                expectNotEqual(abcSet, abcdSet)
                
                expectTrue(abcSet.isStrictSubset(of:abcdSet))
                expectFalse(abcdSet.isStrictSubset(of:abcSet))
                expectTrue(abcdSet.isStrictSuperset(of:abcSet))
                expectFalse(abcSet.isStrictSuperset(of:abcdSet))
            }
        #endif
        */
    }

    func test_unconditionallyBridgeFromObjectiveC() {
        expectEqual(CharacterSet(), CharacterSet._unconditionallyBridgeFromObjectiveC(nil))
    }
}

#if !FOUNDATION_XCTEST
var CharacterSetTests = TestSuite("TestCharacterSet")
CharacterSetTests.test("testBasicConstruction") { TestCharacterSet().testBasicConstruction() }
CharacterSetTests.test("testMutability_copyOnWrite") { TestCharacterSet().testMutability_copyOnWrite() }
CharacterSetTests.test("testMutability_mutableCopyCrash") { TestCharacterSet().testMutability_mutableCopyCrash() }
CharacterSetTests.test("testMutability_SR_1782") { TestCharacterSet().testMutability_SR_1782() }
CharacterSetTests.test("testRanges") { TestCharacterSet().testRanges() }
CharacterSetTests.test("testInsertAndRemove") { TestCharacterSet().testInsertAndRemove() }
CharacterSetTests.test("testBasics") { TestCharacterSet().testBasics() }
CharacterSetTests.test("test_classForCoder") { TestCharacterSet().test_classForCoder() }
CharacterSetTests.test("test_AnyHashableContainingCharacterSet") { TestCharacterSet().test_AnyHashableContainingCharacterSet() }
CharacterSetTests.test("test_AnyHashableCreatedFromNSCharacterSet") { TestCharacterSet().test_AnyHashableCreatedFromNSCharacterSet() }
CharacterSetTests.test("test_superSet") { TestCharacterSet().test_superSet() }
CharacterSetTests.test("test_union") { TestCharacterSet().test_union() }
CharacterSetTests.test("test_subtracting") { TestCharacterSet().test_subtracting() }
CharacterSetTests.test("test_subtractEmptySet") { TestCharacterSet().test_subtractEmptySet() }
CharacterSetTests.test("test_subtractNonEmptySet") { TestCharacterSet().test_subtractNonEmptySet() }
CharacterSetTests.test("test_symmetricDifference") { TestCharacterSet().test_symmetricDifference() }
CharacterSetTests.test("test_hasMember") { TestCharacterSet().test_hasMember() }
CharacterSetTests.test("test_bitmap") { TestCharacterSet().test_bitmap() }
CharacterSetTests.test("test_setOperationsOfEmptySet") { TestCharacterSet().test_setOperationsOfEmptySet() }
CharacterSetTests.test("test_moreSetOperations") { TestCharacterSet().test_moreSetOperations() }
CharacterSetTests.test("test_unconditionallyBridgeFromObjectiveC") { TestCharacterSet().test_unconditionallyBridgeFromObjectiveC() }
runAllTests()
#endif

