// 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
//

// @_exported import of Dispatch here makes it available to all
// classes in Foundation and all sources that import Foundation.
// This brings it into line with Darwin usage for compatibility.
@_exported import Dispatch

import CoreFoundation

/// The `NSObjectProtocol` groups methods that are fundamental to all Foundation objects.
///
/// If an object conforms to this protocol, it can be considered a first-class object.
/// 
/// The Cocoa root class, NSObject, adopts this protocol, so all objects inheriting
/// from NSObject have the features described by this protocol.
public protocol NSObjectProtocol : class {
    
    /// Returns a Boolean value that indicates whether the instance
    /// and a given `object` are equal.
    ///
    /// This method defines what it means for instances to be equal. For example, a container
    /// object might define two containers as equal if their corresponding objects all respond
    /// true to an `isEqual(_:)` request. See the `NSData`, `NSDictionary`, `NSArray`,
    /// and `NSString` class specifications for examples of the use of this method.
    ///
    /// If two objects are equal, they must have the same hash value.
    /// This last point is particularly important if you define `isEqual(_:)` in a subclass
    /// and intend to put instances of that subclass into a collection.
    /// Make sure you also define hash in your subclass.
    ///
    /// - Parameter object: The object to be compared to the instance.
    ///                     May be `nil`, in which case this method returns `false`.
    /// - Returns:          `true` if the instance and `object` are equal, otherwise `false`.
    func isEqual(_ object: Any?) -> Bool
    
    /// Returns an integer that can be used as a table address in a hash table structure.
    /// 
    /// If two objects are equal (as determined by the `isEqual(_:)` method),
    /// they must have the same hash value. This last point is particularly important
    /// if you define `hash` in a subclass and intend to put instances of that subclass
    /// into a collection.
    ///
    /// If a mutable object is added to a collection that uses hash values to determine
    /// the object’s position in the collection, the value returned by the `hash` property
    /// of the object must not change while the object is in the collection. Therefore, either
    /// the `hash` property must not rely on any of the object’s internal state information
    /// or you must make sure the object’s internal state information does not change while
    /// the object is in the collection. Thus, for example, a mutable dictionary can be put
    /// in a hash table but you must not change it while it is in there.
    /// (Note that it can be difficult to know whether or not a given object is in a collection.)
    var hash: Int { get }
    
    /// Returns the instance itself.
    ///
    /// - Returns: The instance itself.
    func `self`() -> Self
    
    /// Returns a Boolean value that indicates whether the instance does not descend from NSObject.
    ///
    /// - Returns: `false` if the instance really descends from `NSObject`, otherwise `true`.
    func isProxy() -> Bool

    /// Returns a string that describes the contents of the instance.
    var description: String { get }
    
    /// Returns a string that describes the contents of the instance for presentation
    /// in the debugger.
    var debugDescription: String { get }
}

extension NSObjectProtocol {
    
    public var debugDescription: String {
        return description
    }
}

public struct NSZone : ExpressibleByNilLiteral {
    
    public init() {
        
    }
    
    public init(nilLiteral: ()) {
        
    }
}

/// The `NSCopying` protocol declares a method for providing functional copies of an object.
/// The exact meaning of “copy” can vary from class to class, but a copy must be a functionally
/// independent object with values identical to the original at the time the copy was made.
///
/// NSCopying declares one method, `copy(with:)`, but copying is commonly invoked with the
/// convenience method `copy`. The copy method is defined for all objects inheriting from NSObject
/// and simply invokes `copy(with:)` with the `nil` zone.
///
/// If a subclass inherits `NSCopying` from its superclass and declares additional instance variables,
/// the subclass has to override `copy(with:)` to properly handle its own instance variables,
/// invoking the superclass’s implementation first.
public protocol NSCopying {
    
    /// Returns a new instance that’s a copy of the current one.
    ///
    /// - Parameter zone:   This parameter is ignored. Memory zones are no longer used.
    /// - Returns:          A new instance that’s a copy of the current one.
    func copy(with zone: NSZone?) -> Any
}

extension NSCopying {
    
    /// Returns a new instance that’s a copy of the current one.
    ///
    /// - Returns: A new instance that’s a copy of the current one.
    public func copy() -> Any {
        return copy(with: nil)
    }
}

/// The `NSMutableCopying` protocol declares a method for providing mutable
/// copies of an object. Only classes that define an “immutable vs. mutable” distinction
/// should adopt this protocol. Classes that don’t define such a distinction should
/// adopt `NSCopying` instead.
public protocol NSMutableCopying {
    
    /// Returns a new instance that’s a mutable copy of the current one.
    ///
    /// - Parameter zone:   This parameter is ignored. Memory zones are no longer used.
    /// - Returns:          A new instance that’s a mutable copy of the current one.
    func mutableCopy(with zone: NSZone?) -> Any
}

extension NSMutableCopying {
    
    /// Returns a new instance that’s a mutable copy of the current one.
    ///
    /// - Returns: A new instance that’s a mutable copy of the current one.
    public func mutableCopy() -> Any {
        return mutableCopy(with: nil)
    }
}

/// The root class of most Foundation class hierarchies.
open class NSObject : NSObjectProtocol, Equatable, Hashable {
    // Important: add no ivars here. It will subvert the careful layout of subclasses that bridge into CF.    
    
    /// Implemented by subclasses to initialize a new object immediately after memory
    /// for it has been allocated.
    public init() {}
    
    /// Returns the object returned by `copy(with:)`.
    ///
    /// This is a convenience method for classes that adopt the `NSCopying` protocol.
    /// `NSObject` does not itself support the `NSCopying` protocol.
    /// Subclasses must support the protocol and implement the `copy(with:)` method.
    /// A subclass version of the `copy(with:)` method should invoke `super`'s method first,
    /// to incorporate its implementation, unless the subclass descends directly from `NSObject`.
    ///
    /// - Returns: The object returned by the `NSCopying` protocol method `copy(with:)`.
    open func copy() -> Any {
        if let copyable = self as? NSCopying {
            return copyable.copy(with: nil)
        }
        return self
    }
    
    /// Returns the object returned by `mutableCopy(with:)` where the zone is `nil.`
    ///
    /// This is a convenience method for classes that adopt the `NSMutableCopying` protocol.
    ///
    /// - Returns: The object returned by the `NSMutableCopying` protocol method
    ///            `mutableCopy(with:)`, where the zone is `nil`.
    open func mutableCopy() -> Any {
        if let copyable = self as? NSMutableCopying {
            return copyable.mutableCopy(with: nil)
        }
        return self
    }
    
    /// Returns a Boolean value that indicates whether the instance is equal to another given object.
    ///
    /// The default implementation for this method provided by `NSObject` returns `true` if
    /// the objects being compared refer to the same instance.
    ///
    /// - Parameter object: The object with which to compare the instance.
    /// - Returns:          `true` if the instance is equal to `object`, otherwise `false`.
    open func isEqual(_ object: Any?) -> Bool {
        guard let obj = object as? NSObject else { return false }
        return obj === self
    }
    
    /// Returns an integer that can be used as a table address in a hash table structure.
    ///
    /// If two objects are equal (as determined by the `isEqual(_:)` method),
    /// they must have the same hash value. This last point is particularly important
    /// if you define `hash` in a subclass and intend to put instances of that subclass
    /// into a collection.
    ///
    /// If a mutable object is added to a collection that uses hash values to determine
    /// the object’s position in the collection, the value returned by the `hash` property
    /// of the object must not change while the object is in the collection. Therefore, either
    /// the `hash` property must not rely on any of the object’s internal state information
    /// or you must make sure the object’s internal state information does not change while
    /// the object is in the collection. Thus, for example, a mutable dictionary can be put
    /// in a hash table but you must not change it while it is in there.
    /// (Note that it can be difficult to know whether or not a given object is in a collection.)
    open var hash: Int {
        return ObjectIdentifier(self).hashValue
    }
    
    /// Returns the instance itself.
    ///
    /// - Returns: The instance itself.
    open func `self`() -> Self {
        return self
    }
    
    /// Returns a Boolean value that indicates whether the instance does not descend from NSObject.
    ///
    /// - Returns: `false` if the instance really descends from `NSObject`, otherwise `true`.
    open func isProxy() -> Bool {
        return false
    }
    
    /// Returns a string that describes the contents of the instance.
    open var description: String {
        return "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())>"
    }
    
    /// Returns a string that describes the contents of the instance for presentation
    /// in the debugger.
    open var debugDescription: String {
        return description
    }
    
    open var _cfTypeID: CFTypeID {
        return 0
    }
    
    // TODO: move these back into extensions once extension methods can be overriden
    
    /// Overridden by subclasses to substitute a class other than its own during coding.
    ///
    /// This property is needed for `NSCoder`.
    /// `NSObject`’s implementation returns the instance's class.
    /// The private subclasses of a class cluster substitute the name of their public
    /// superclass when being archived.
    open var classForCoder: AnyClass {
        return type(of: self)
    }
 
    /// Overridden by subclasses to substitute another object for itself during encoding.
    ///
    /// An object might encode itself into an archive, but encode a proxy for itself if
    /// it’s being encoded for distribution. This method is invoked by `NSCoder`.
    /// `NSObject`’s implementation returns `self`.
    ///
    /// - Parameter aCoder: The coder encoding the instance.
    /// - Returns:          The object encode instead of the instance (if different).
    open func replacementObject(for aCoder: NSCoder) -> Any? {
        return self
    }

    // TODO: Could perhaps be an extension of NSCoding instead.
    // The reason it is an extension of NSObject is the lack of default
    // implementations on protocols in Objective-C.
    
    /// Subclasses to substitute a new class for instances during keyed archiving.
    ///
    /// The object will be encoded as if it were a member of the class. This property is
    /// overridden by the encoder class and instance name to class encoding tables.
    /// If this property is `nil`, the result of this property is ignored.
    open var classForKeyedArchiver: AnyClass? {
        return self.classForCoder
    }
    
    /// Overridden by subclasses to substitute another object for itself during keyed archiving.
    ///
    /// This method is called only if no replacement mapping for the object has been set up
    /// in the encoder (for example, due to a previous call of `replacementObject(for:)` to that object).
    ///
    /// - Parameter archiver:   A keyed archiver creating an archive.
    /// - Returns:              The object encode instead of the instance (if different).
    open func replacementObject(for archiver: NSKeyedArchiver) -> Any? {
        return self.replacementObject(for: archiver as NSCoder)
    }
    
    /// Overridden to return the names of classes that can be used to decode
    /// objects if their class is unavailable.
    ///
    /// `NSKeyedArchiver` calls this method and stores the result inside the archive.
    /// If the actual class of an object doesn’t exist at the time of unarchiving,
    /// `NSKeyedUnarchiver` goes through the stored list of classes and uses the first one
    /// that does exists as a substitute class for decoding the object.
    /// The default implementation of this method returns empty array.
    ///
    /// You can use this method if you introduce a new class into your application to provide
    /// some backwards compatibility in case the archive will be read on a system that does not
    /// have that class. Sometimes there may be another class which may work nearly as well as
    /// a substitute for the new class, and the archive keys and archived state for the new class
    /// can be carefully chosen (or compatibility written out) so that the object can be unarchived
    /// as the substitute class if necessary.
    ///
    /// - Returns: An array of strings that specify the names of classes in preferred order for unarchiving.
    open class func classFallbacksForKeyedArchiver() -> [String] {
        return []
    }

    /// Overridden by subclasses to substitute a new class during keyed unarchiving.
    ///
    /// During keyed unarchiving, instances of the class will be decoded as members
    /// of the returned class. This method overrides the results of the decoder’s class
    /// and instance name to class encoding tables.
    ///
    /// - Returns: The class to substitute for the current class during keyed unarchiving.
    open class func classForKeyedUnarchiver() -> AnyClass {
        return self
    }

    /// The hash value.
    ///
    /// `NSObject` implements this by returning `self.hash`.
    ///
    /// `NSObject.hashValue` is not overridable; subclasses can customize hashing
    /// by overriding the `hash` property.
    ///
    /// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
    ///
    /// - Note: the hash value is not guaranteed to be stable across
    ///   different invocations of the same program.  Do not persist the
    ///   hash value across program runs.
    public final var hashValue: Int {
        return hash
    }

    /// Hashes the essential components of this value by feeding them into the
    /// given hasher.
    ///
    /// NSObject implements this by feeding `self.hash` to the hasher.
    ///
    /// `NSObject.hash(into:)` is not overridable; subclasses can customize
    /// hashing by overriding the `hash` property.
    public final func hash(into hasher: inout Hasher) {
        hasher.combine(self.hash)
    }

    /// Returns a Boolean value indicating whether two values are equal.
    ///
    /// Equality is the inverse of inequality. For any values `a` and `b`,
    /// `a == b` implies that `a != b` is `false`.
    ///
    /// - Parameters:
    ///   - lhs: A value to compare.
    ///   - rhs: Another value to compare.
    public static func ==(lhs: NSObject, rhs: NSObject) -> Bool {
        return lhs.isEqual(rhs)
    }
    
    // Please note:
    // the following methods are not overridable in swift-corelibs-foundation.
    
    private class var nsObjectSuperclass: NSObject.Type? {
        // Pretend that {Swift,}Foundation.NSObject is the top of the class hierarchy.
        // On Darwin, avoids dipping into the class hierarchy that exists above this class,
        // which is ultimately rooted in the ObjC NSObject class.
        guard !(self == NSObject.self) else { return nil }
        return _getSuperclass(self) as! NSObject.Type
    }
    
    public class var superclass: AnyClass? {
        return nsObjectSuperclass
    }
    
    /// Returns a Boolean value that indicates whether the receiving
    /// class is a subclass of, or identical to, a given class.
    public class func isSubclass(of aClass: AnyClass) -> Bool {
        var checkedClass: NSObject.Type? = self
        while let thisClass = checkedClass {
            if thisClass === aClass {
                return true
            }
            checkedClass = thisClass.nsObjectSuperclass
        }
        
        return false
    }
    
    public func isMember(of aClass: AnyClass) -> Bool {
        return type(of: self) === aClass
    }
    
    public func isKind(of aClass: AnyClass) -> Bool {
        return type(of: self).isSubclass(of: aClass)
    }
}

extension NSObject : CustomDebugStringConvertible {
}

extension NSObject : CustomStringConvertible {
}