/*:#localized(key: "OrderingWorkWithDependencies")
 # Ordering work with dependencies

 An `OperationQueue` will happily run everything you give it **at once**, in no
 guaranteed order. That's exactly what you want for independent jobs. It's exactly
 what you *don't* want for a pipeline.

 A **dependency** is a rule that says "don't start operation B until operation A has
 finished." You add one with `addDependency(_:)`. Here's the brochure pipeline, with
 each stage handing its result to the next:
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

final class DesignOperation: Operation {
    var layout: String?

    override func main() {
        print("🎨 Designing the layout...")
        layout = "3-fold brochure layout"
    }
}

final class PrintOperation: Operation {
    var pages: String?
    let design: DesignOperation
    
    init(design: DesignOperation) { 
        self.design = design
    }

    override func main() {
        guard let layout = design.layout else {
            print("🖨️ Nothing to print: no layout")
            return
        }
        print("🖨️ Printing from: \(layout)")
        pages = "120 printed pages"
    }
}

final class BindOperation: Operation {
    let printJob: PrintOperation
    
    init(printJob: PrintOperation) { 
        self.printJob = printJob
    }

    override func main() {
        guard let pages = printJob.pages else {
            print("📚 Nothing to bind")
            return
        }
        print("📚 Binding \(pages) into finished brochures")
    }
}

let design = DesignOperation()
let printing = PrintOperation(design: design)
let binding = BindOperation(printJob: printing)

// The ordering rules: print waits for design, bind waits for print.
printing.addDependency(design)
binding.addDependency(printing)

binding.completionBlock = {
    print("✅ Pipeline complete")
    PlaygroundPage.current.finishExecution()
}

let queue = OperationQueue()
// Add them in a scrambled order on purpose.
queue.addOperations([binding, printing, design], waitUntilFinished: false)
print("Jobs queued: dependencies decide the order, not the add order")
//#-end-editable-code
/*:#localized(key: "OrderingWorkWithDependencies-2")
 **Watch it run:** even though we added the operations *bind → print → design*, they
 run **design → print → bind**, every time. The queue held `printing` back until
 `design` finished, and `binding` until `printing` finished. Notice too that each
 stage read the previous stage's output (`design.layout`, `printJob.pages`) and it
 was always there.

 That last point is worth pausing on. A dependency doesn't just *order* the work; it
 guarantees the earlier operation has **completely finished** before the later one
 starts. There's no overlap, so reading `design.layout` from inside `PrintOperation`
 is safe: the two operations never touch that property at the same time. Dependencies
 give you correct ordering *and* a simple, race-free way to pass data along.

 > `addDependency(_:)` only enforces ordering. It does **not** move
 > data for you. Passing the layout into `PrintOperation`'s initializer is your work,
 > not the queue's. Dependencies say *when*; you still wire up *what*.

 * callout(Try it): Delete `binding.addDependency(printing)` and run again: what does
 `BindOperation` print now, and why?

 Dependencies can even cross queues: an operation on one queue can depend on an
 operation on another. The queue simply won't start a dependent operation until all
 of its dependencies report `isFinished`.

 [Previous](@previous)  |  page 2 of 7  |  [Next: Cancellation Is a Polite Request](@next)
 */
