import ComposableArchitecture import Foundation import SharedModels /// Holds onto shared values for several of the views in this feature. @dynamicMemberLookup public struct SharedPressureEstimationState: Equatable, Sendable { public var budgets: BudgetedPercentEnvelope? public var equipmentMeasurement: EquipmentMeasurement? public var equipmentMetadata: EquipmentMetadata public var flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement? public var flaggedEstimations: IdentifiedArrayOf public var heatingCapacity: Double? public var manufacturersIncludedFilterPressureDrop: Double? public init( budgets: BudgetedPercentEnvelope? = nil, equipmentMeasurement: EquipmentMeasurement? = nil, equipmentMetadata: EquipmentMetadata = .init(), flaggedEquipmentMeasurement: EquipmentMeasurement.FlaggedMeasurement? = nil, flaggedEstimations: IdentifiedArrayOf = [], heatingCapacity: Double? = nil, manufacturersIncludedFilterPressureDrop: Double? = nil ) { self.budgets = budgets self.equipmentMeasurement = equipmentMeasurement self.equipmentMetadata = equipmentMetadata self.flaggedEquipmentMeasurement = flaggedEquipmentMeasurement self.flaggedEstimations = flaggedEstimations self.heatingCapacity = heatingCapacity self.manufacturersIncludedFilterPressureDrop = manufacturersIncludedFilterPressureDrop } public subscript(dynamicMember keyPath: WritableKeyPath) -> T { get { equipmentMetadata[keyPath: keyPath] } set { equipmentMetadata[keyPath: keyPath] = newValue } } @dynamicMemberLookup public struct FlaggedEstimationContainer: Equatable, Identifiable, Sendable { public let id: UUID public var estimationState: EstimationState public var flaggedMeasurement: EquipmentMeasurement.FlaggedMeasurement public init( id: UUID, estimationState: EstimationState, flaggedMeasurement: EquipmentMeasurement.FlaggedMeasurement ) { self.id = id self.estimationState = estimationState self.flaggedMeasurement = flaggedMeasurement } public subscript(dynamicMember keyPath: WritableKeyPath) -> T { get { estimationState[keyPath: keyPath] } set { estimationState[keyPath: keyPath] = newValue } } public subscript(dynamicMember keyPath: KeyPath) -> T { get { estimationState[keyPath: keyPath] } } public struct EstimationState: Equatable, Sendable { public var cfm: CFMContainer public var filterPressureDrop: Double? public var name: String? public init( cfm: CFMContainer, filterPressureDrop: Double? = nil, name: String? = nil ) { self.cfm = cfm self.filterPressureDrop = filterPressureDrop self.name = name } internal init(state: EstimationForm.State) { self.init( cfm: .init(state: state), filterPressureDrop: state.filterPressureDrop, name: state.name.isEmpty ? nil : state.name ) } var displayName: String { guard let name else { return "@\(Int(cfm.airflow)) CFM" } return name } // Compare relevant values on if two states have changes. func hasChanges(_ other: Self) -> Bool { cfm != other.cfm || filterPressureDrop != other.filterPressureDrop } public enum CFMContainer: Equatable, Sendable { case cfm(Int) case cfmPerTon(Int, EquipmentMetadata.CoolingCapacity) init(state: EstimationForm.State) { switch state.airflowSelection { case .cfmPerTon: self = .cfmPerTon(state.cfmTextField ?? 0, state.coolingCapacity) case .cfm: self = .cfm(state.cfmTextField ?? 0) } } var airflow: Double { switch self { case let .cfm(cfm): return Double(cfm) case let .cfmPerTon(cfmPerTon, capacity): return Double(cfmPerTon) * capacity.rawValue } } } } } } extension PersistenceReaderKey where Self == InMemoryKey { static var pressureEstimationState: Self { .inMemory("sharedPressureEstimationState") } }