//
//  RuntimeExt.swift
//  ExtensionKit
//
//  Created by FFIB on 2017/9/26.
//  Copyright © 2017年 FFIB. All rights reserved.
//

import UIKit

extension NSObject {
    
    public func associatedObject<ValueType: Any>(
        key: UnsafeRawPointer,
        initialiser: () -> ValueType)
        -> ValueType {
            if let associated = objc_getAssociatedObject(self, key)
                as? ValueType { return associated }
            let associated = initialiser()
            objc_setAssociatedObject(self, key, associated,
                                     .OBJC_ASSOCIATION_RETAIN)
            return associated
    }
    public func associateObject<ValueType: Any>(
        key: UnsafeRawPointer,
        value: ValueType) {
        objc_setAssociatedObject(self, key, value,
                                 .OBJC_ASSOCIATION_RETAIN)
    }
    
    public func swizzleInstanceMethod(oringin: Selector,
                                      swizzled: Selector) {
       guard let originMethod = class_getInstanceMethod(self as? AnyClass, oringin),
        let swizzledMethod = class_getInstanceMethod(self as? AnyClass, swizzled) else {
            assert(true, "\(#function) + \(#line) + \(oringin) or + \(swizzled) is nil")
            return
        }
        swizzleMethod(origin: oringin,
                      swizzled: swizzled,
                      originMethod: originMethod,
                      swizzledMethod: swizzledMethod)
    }
    
    public func swizzleClassMethod(origin: Selector,
                                   swizzled: Selector) {
        guard let originMethod = class_getClassMethod(self as? AnyClass, origin),
            let swizzledMethod = class_getClassMethod(self as? AnyClass, swizzled) else {
                assert(true, "\(#function) + \(#line) + \(origin) or + \(swizzled) is nil")
                return
        }
        swizzleMethod(origin: origin,
                      swizzled: swizzled,
                      originMethod: originMethod,
                      swizzledMethod: swizzledMethod)
    }
    
    public func swizzleMethod(origin: Selector,
                              swizzled: Selector,
                              originMethod: Method,
                              swizzledMethod: Method) {
        let isExist = class_addMethod(self as? AnyClass,
                                      origin,
                                      method_getImplementation(swizzledMethod),
                                      method_getTypeEncoding(swizzledMethod))
        if isExist {
            class_replaceMethod(self as? AnyClass,
                                swizzled,
                                method_getImplementation(originMethod),
                                method_getTypeEncoding(originMethod))
        }else {
            method_exchangeImplementations(originMethod, swizzledMethod)
        }
    }
    
}