//
//  SwiftDate
//  Parse, validate, manipulate, and display dates, time and timezones in Swift
//
//  Created by Daniele Margutti
//   - Web: https://www.danielemargutti.com
//   - Twitter: https://twitter.com/danielemargutti
//   - Mail: hello@danielemargutti.com
//
//  Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//

#if os(Linux)

#else
import Foundation
import ObjectiveC.runtime

internal func getAssociatedValue<T>(key: String, object: AnyObject) -> T? {
	return (objc_getAssociatedObject(object, key.address) as? AssociatedValue)?.value as? T
}

internal func getAssociatedValue<T>(key: String, object: AnyObject, initialValue: @autoclosure () -> T) -> T {
	return getAssociatedValue(key: key, object: object) ?? setAndReturn(initialValue: initialValue(), key: key, object: object)
}

internal func getAssociatedValue<T>(key: String, object: AnyObject, initialValue: () -> T) -> T {
	return getAssociatedValue(key: key, object: object) ?? setAndReturn(initialValue: initialValue(), key: key, object: object)
}

private func setAndReturn<T>(initialValue: T, key: String, object: AnyObject) -> T {
	set(associatedValue: initialValue, key: key, object: object)
	return initialValue
}

internal func set<T>(associatedValue: T?, key: String, object: AnyObject) {
	set(associatedValue: AssociatedValue(associatedValue), key: key, object: object)
}

internal func set<T: AnyObject>(weakAssociatedValue: T?, key: String, object: AnyObject) {
	set(associatedValue: AssociatedValue(weak: weakAssociatedValue), key: key, object: object)
}

extension String {

	fileprivate var address: UnsafeRawPointer {
		return UnsafeRawPointer(bitPattern: abs(hashValue))!
	}

}

private func set(associatedValue: AssociatedValue, key: String, object: AnyObject) {
	objc_setAssociatedObject(object, key.address, associatedValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}

private class AssociatedValue {

	weak var _weakValue: AnyObject?
	var _value: Any?

	var value: Any? {
		return _weakValue ?? _value
	}

	init(_ value: Any?) {
		_value = value
	}

	init(weak: AnyObject?) {
		_weakValue = weak
	}

}
#endif
