//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// 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
//
//===----------------------------------------------------------------------===//

@_exported import Dispatch
import _SwiftDispatchOverlayShims

/// dispatch_assert

@available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
public enum DispatchPredicate {
	case onQueue(DispatchQueue)
	case onQueueAsBarrier(DispatchQueue)
	case notOnQueue(DispatchQueue)
}

@available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
public func _dispatchPreconditionTest(_ condition: DispatchPredicate) -> Bool {
	switch condition {
	case .onQueue(let q):
		__dispatch_assert_queue(q)
	case .onQueueAsBarrier(let q):
		__dispatch_assert_queue_barrier(q)
	case .notOnQueue(let q):
		__dispatch_assert_queue_not(q)
	}
	return true
}

@_transparent
@available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
public func dispatchPrecondition(condition: @autoclosure () -> DispatchPredicate) {
	// precondition is able to determine release-vs-debug asserts where the overlay
	// cannot, so formulating this into a call that we can call with precondition()
	precondition(_dispatchPreconditionTest(condition()), "dispatchPrecondition failure")
}

/// qos_class_t

public struct DispatchQoS : Equatable {
	public let qosClass: QoSClass
	public let relativePriority: Int

	@available(OSX 10.10, iOS 8.0, *)
	public static let background = DispatchQoS(qosClass: .background, relativePriority: 0)

	@available(OSX 10.10, iOS 8.0, *)
	public static let utility = DispatchQoS(qosClass: .utility, relativePriority: 0)

	@available(OSX 10.10, iOS 8.0, *)
	public static let `default` = DispatchQoS(qosClass: .default, relativePriority: 0)

	@available(OSX 10.10, iOS 8.0, *)
	public static let userInitiated = DispatchQoS(qosClass: .userInitiated, relativePriority: 0)

	@available(OSX 10.10, iOS 8.0, *)
	public static let userInteractive = DispatchQoS(qosClass: .userInteractive, relativePriority: 0)

	public static let unspecified = DispatchQoS(qosClass: .unspecified, relativePriority: 0)

	public enum QoSClass {
		@available(OSX 10.10, iOS 8.0, *)
		case background

		@available(OSX 10.10, iOS 8.0, *)
		case utility

		@available(OSX 10.10, iOS 8.0, *)
		case `default`

		@available(OSX 10.10, iOS 8.0, *)
		case userInitiated

		@available(OSX 10.10, iOS 8.0, *)
		case userInteractive

		case unspecified

		@available(OSX 10.10, iOS 8.0, *)
		public init?(rawValue: qos_class_t) {
			switch rawValue {
			case QOS_CLASS_BACKGROUND: self = .background
			case QOS_CLASS_UTILITY: self = .utility
			case QOS_CLASS_DEFAULT: self = .default
			case QOS_CLASS_USER_INITIATED: self = .userInitiated
			case QOS_CLASS_USER_INTERACTIVE: self = .userInteractive
			case QOS_CLASS_UNSPECIFIED: self = .unspecified
			default: return nil
			}
		}

		@available(OSX 10.10, iOS 8.0, *)
		public var rawValue: qos_class_t {
			switch self {
			case .background: return QOS_CLASS_BACKGROUND
			case .utility: return QOS_CLASS_UTILITY
			case .default: return QOS_CLASS_DEFAULT
			case .userInitiated: return QOS_CLASS_USER_INITIATED
			case .userInteractive: return QOS_CLASS_USER_INTERACTIVE
			case .unspecified: return QOS_CLASS_UNSPECIFIED
			}
		}
	}

	public init(qosClass: QoSClass, relativePriority: Int) {
		self.qosClass = qosClass
		self.relativePriority = relativePriority
	}
}

public func ==(a: DispatchQoS, b: DispatchQoS) -> Bool {
	return a.qosClass == b.qosClass && a.relativePriority == b.relativePriority
}

/// 

public enum DispatchTimeoutResult {
	case success
	case timedOut
}

/// dispatch_group

public extension DispatchGroup {
	public func notify(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], queue: DispatchQueue, execute work: @escaping @convention(block) () -> Void) {
		if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty {
			let item = DispatchWorkItem(qos: qos, flags: flags, block: work)
			_swift_dispatch_group_notify(self, queue, item._block)
		} else {
			_swift_dispatch_group_notify(self, queue, work)
		}
	}

	@available(OSX 10.10, iOS 8.0, *)
	public func notify(queue: DispatchQueue, work: DispatchWorkItem) {
		_swift_dispatch_group_notify(self, queue, work._block)
	}

	public func wait() {
		_ = __dispatch_group_wait(self, DispatchTime.distantFuture.rawValue)
	}

	public func wait(timeout: DispatchTime) -> DispatchTimeoutResult {
		return __dispatch_group_wait(self, timeout.rawValue) == 0 ? .success : .timedOut
	}

	public func wait(wallTimeout timeout: DispatchWallTime) -> DispatchTimeoutResult {
		return __dispatch_group_wait(self, timeout.rawValue) == 0 ? .success : .timedOut
	}
}

/// dispatch_semaphore

public extension DispatchSemaphore {
	@discardableResult
	public func signal() -> Int {
		return __dispatch_semaphore_signal(self)
	}

	public func wait() {
		_ = __dispatch_semaphore_wait(self, DispatchTime.distantFuture.rawValue)
	}

	public func wait(timeout: DispatchTime) -> DispatchTimeoutResult {
		return __dispatch_semaphore_wait(self, timeout.rawValue) == 0 ? .success : .timedOut
	}

	public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult {
		return __dispatch_semaphore_wait(self, wallTimeout.rawValue) == 0 ? .success : .timedOut
	}
}

