/// Can generate a reader with an associated domain -> Could be simplified later once all controller use a comon class to pass variables instead of more protocols protocol GenericReaderProvider { associatedtype DomainProvider: GenericDomainProvider init?(domain: DomainProvider, lat: Float, lon: Float, elevation: Float, mode: GridSelectionMode, options: GenericReaderOptions) throws init?(domain: DomainProvider, gridpoint: Int, options: GenericReaderOptions) throws } extension GenericReaderProvider { /// Prepare readers for Point, MultiPoint and BoundingBox queries public static func prepareReaders(domains: [DomainProvider], params: ApiQueryParameter, currentTime: Timestamp, forecastDayDefault: Int, forecastDaysMax: Int, pastDaysMax: Int, allowedRange: Range) throws -> [(locationId: Int, timezone: TimezoneWithOffset, time: ForecastApiTimeRange, perModel: [(domain: DomainProvider, reader: () throws -> (Self?))])] { let options = params.readerOptions let prepared = try params.prepareCoordinates(allowTimezones: true) switch prepared { case .coordinates(let coordinates): return try coordinates.map { prepared in let coordinates = prepared.coordinate let timezone = prepared.timezone let time = try params.getTimerange2(timezone: timezone, current: currentTime, forecastDaysDefault: forecastDayDefault, forecastDaysMax: forecastDaysMax, startEndDate: prepared.startEndDate, allowedRange: allowedRange, pastDaysMax: pastDaysMax) return (coordinates.locationId, timezone, time, domains.compactMap { domain in return (domain, { return try Self.init(domain: domain, lat: coordinates.latitude, lon: coordinates.longitude, elevation: coordinates.elevation, mode: params.cell_selection ?? .land, options: options) }) }) } case .boundingBox(let bbox, dates: let dates, timezone: let timezone): return try domains.flatMap ({ domain in guard let grid = domain.genericDomain?.grid else { throw ForecastapiError.generic(message: "Bounding box calls not supported for domain \(domain)") } guard let gridpoionts = grid.findBox(boundingBox: bbox) else { throw ForecastapiError.generic(message: "Bounding box calls not supported for grid of domain \(domain)") } if dates.count == 0 { let time = try params.getTimerange2(timezone: timezone, current: currentTime, forecastDaysDefault: forecastDayDefault, forecastDaysMax: forecastDaysMax, startEndDate: nil, allowedRange: allowedRange, pastDaysMax: pastDaysMax) var locationId = -1 return gridpoionts.map( { gridpoint in locationId += 1 return (locationId, timezone, time, [(domain, { () -> Self? in guard let reader = try Self.init(domain: domain, gridpoint: gridpoint, options: options) else { return nil } return reader })]) }) } return try dates.flatMap({ date -> [(Int, TimezoneWithOffset, ForecastApiTimeRange, [(DomainProvider, () throws -> Self?)])] in let time = try params.getTimerange2(timezone: timezone, current: currentTime, forecastDaysDefault: forecastDayDefault, forecastDaysMax: forecastDaysMax, startEndDate: date, allowedRange: allowedRange, pastDaysMax: pastDaysMax) var locationId = -1 return gridpoionts.map( { gridpoint in locationId += 1 return (locationId, timezone, time, [(domain, { () -> Self? in guard let reader = try Self.init(domain: domain, gridpoint: gridpoint, options: options) else { return nil } return reader })]) }) }) }) } } } protocol GenericDomainProvider { var genericDomain: GenericDomain? { get } } extension GenericDomain where Self: GenericDomainProvider { var genericDomain: GenericDomain? { self } }