/*:#localized(key: "BridgingADelegate")
 # Bridging a delegate

 Not every old API takes a completion closure. Many are **delegate**-based:
 you set yourself as the delegate, start the work, and the object calls one
 of *your* methods when it's done: `somethingDidFinish(...)` or
 `somethingDidFail(...)`. A **delegate** is just an object that receives
 those "it happened" callbacks.

 When a delegate fires **once** (a single success-or-failure outcome), it's a
 perfect fit for a continuation. The trick: make a small adapter object that
 *is* the delegate, hand it the continuation, and have each delegate method
 resume it.

 Picture the rooftop weather station from earlier chapters. A legacy
 `CalibrationProbe` calibrates a sensor and reports back through a delegate,
 once.
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

enum ProbeError: Error {
    case sensorStuck
}

// --- The legacy delegate-based API (single-shot outcome) ---
protocol CalibrationProbeDelegate: AnyObject {
    func probeDidFinish(reading: Double)
    func probeDidFail(error: Error)
}

final class CalibrationProbe {
    weak var delegate: CalibrationProbeDelegate?

    func begin() {
        DispatchQueue.global().asyncAfter(deadline: .now() + 0.4) { [self] in
            delegate?.probeDidFinish(reading: 21.4)   // fires once
        }
    }
}

// --- The bridge: an adapter that turns delegate calls into one resume ---
final class ProbeCalibration: CalibrationProbeDelegate {
    private var continuation: CheckedContinuation<Double, Error>?
    private var keepAlive: ProbeCalibration?   // stay alive until we resume
    private let probe = CalibrationProbe()

    func run() async throws -> Double {
        try await withCheckedThrowingContinuation { continuation in
            self.continuation = continuation
            self.keepAlive = self       // the probe's delegate is weak...
            probe.delegate = self       // ...so hold ourselves up until done
            probe.begin()
        }
    }

    func probeDidFinish(reading: Double) {
        continuation?.resume(returning: reading)
        continuation = nil              // guard against a second call
        keepAlive = nil
    }

    func probeDidFail(error: Error) {
        continuation?.resume(throwing: error)
        continuation = nil
        keepAlive = nil
    }
}

func calibrateSensor() async throws -> Double {
    try await ProbeCalibration().run()
}

Task {
    do {
        let reading = try await calibrateSensor()
        print("Sensor calibrated to \(reading)°C")
    } catch {
        print("Calibration failed: \(error)")
    }
    PlaygroundPage.current.finishExecution()
}
//#-end-editable-code
/*:#localized(key: "BridgingADelegate-2")
 **Watch it run:** after a short pause you'll see `Sensor calibrated to
 21.4°C`. Two details carry the weight. First, the adapter parks the
 continuation and resumes it in whichever delegate method fires: success *or*
 failure, never both. Second, `keepAlive = self`: the probe holds its
 delegate `weak` (as delegates do, to avoid retain cycles), so without a
 strong reference the adapter would vanish before the callback arrives, and
 the continuation would leak into a hang. Holding `self` until the single
 resume, then releasing, keeps it alive exactly long enough. Setting
 `continuation = nil` after resuming is your own belt-and-suspenders against
 a double-resume.

 [Previous](@previous)  |  page 6 of 8  |  [Next: One Resume Only](@next)
 */
