/*:#localized(key: "CancellationIsAPoliteRequest")
 # Cancellation is a polite request

 Say the customer calls halfway through a 200-page run and cancels. You want to stop
 wasting toner *now*. Every `Operation` has a `cancel()` method, but here's the part
 that surprises people:

 **`cancel()` does not stop your code. It just sets a flag (`isCancelled`) to
 `true`. Your operation has to notice that flag and bail out on its own.**

 This is called **cooperative cancellation**: the system asks, and your code
 cooperates. Nothing gets force-killed mid-instruction. If your `main()` never checks
 `isCancelled`, calling `cancel()` changes nothing; the work runs to completion.

 So the rule is: check `isCancelled` at sensible points, especially inside loops and
 before expensive steps.
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

final class RenderBookOperation: Operation {
    let pageCount: Int
    init(pageCount: Int) { self.pageCount = pageCount }

    override func main() {
        for page in 1...pageCount {
            // A sensible checkpoint: before each slow page.
            if isCancelled {
                print("🚫 Rendering stopped early, before page \(page)")
                return
            }
            Thread.sleep(forTimeInterval: 0.3) // pretend each page is slow
            print("🖨️ Rendered page \(page)/\(pageCount)")
        }
        print("✅ All \(pageCount) pages rendered")
    }
}

let queue = OperationQueue()
let render = RenderBookOperation(pageCount: 10)

render.completionBlock = {
    print("Finished. Was it cancelled? \(render.isCancelled)")
    PlaygroundPage.current.finishExecution()
}

queue.addOperation(render)

// The customer changes their mind after a second.
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    print("📣 Customer cancelled the order")
    render.cancel()
}
//#-end-editable-code
/*:#localized(key: "CancellationIsAPoliteRequest-2")
 **Watch it run:** you'll see three or four pages render, then
 `📣 Customer cancelled the order`, then the very next loop iteration sees
 `isCancelled == true` and prints `🚫 Rendering stopped early...`. The remaining pages
 never render. Try deleting the `if isCancelled` check and running again: now all ten
 pages render regardless of the cancel: proof that `cancel()` alone is only a
 request.

 * callout(Try it): Change the cancel delay from `1` to `0.1`: how many pages render
 before it stops?

 Two details to lock in:

 * **The `completionBlock` still runs.** A cancelled operation is still a *finished*
 operation. That's why you often check `isCancelled` inside the completion to decide
 whether you succeeded or bailed.
 * **To cancel everything on a queue at once,** call `queue.cancelAllOperations()`.
 It sends `cancel()` to every operation the queue knows about, but each one still
 has to check the flag itself.

 [Previous](@previous)  |  page 3 of 7  |  [Next: Asynchronous Operations](@next)
 */
