Files

136 lines
5.3 KiB
Swift

//
// ConvertingServersOperation.swift
// PrivadoVPN
//
// Created by Juraldinio on 3/21/21.
// Copyright © 2021 Privado LLC. All rights reserved.
//
import Foundation
protocol ConvertingServersOperationOutput {
var result: MainOperationsResult? { get }
}
final class ConvertingServersOperation: Operation {
private(set) var result: MainOperationsResult?
private let interactor: MainInteractorInput
init(interactor: MainInteractorInput) {
self.interactor = interactor
}
override func main() {
guard let fetchingOutput = self.dependencies.first as? FetchingServersOperationOutput
, let interactorResult = fetchingOutput.result
, let result = interactorResult.optional else { return }
let serverRecords = ConvertingServersOperation.convert(serverGroups: result.serverGroups)
let preferred = self.interactor.preferredServer
let context = ConvertingServersOperation.prepareContext(using: result.geoLocation,
serverRecords: serverRecords,
preferred: preferred,
interactor: self.interactor)
self.result = MainOperationsResult(serverRecords: serverRecords, context: context)
}
// MARK: - Private static
private static func convert(serverGroups: [ServerGroup]) -> [ServerRecord] {
return serverGroups.reduce(into: []) { acc, group in
group.locations.forEach { location in
let records = location.cities
.sorted(by: <)
.compactMap { city -> ServerRecord? in
let leisureServer: Server? = nil
guard let server = location.servers(in: city)
.reduce(into: leisureServer, { acc, server in
if let srv = acc, srv.capacity < server.capacity { acc = srv } else { acc = server }
}) else {
return nil
}
if acc.first(where: { $0.group == group.group && $0.countryCode == location.country && $0.city == city }).isExist {
return nil
}
return ServerRecord(group: group.group, city: city, countryCode: location.country, server: server)
}
acc.append(contentsOf: records)
}
}
}
private static func prepareContext(using geoLocation: GeoLocation?, serverRecords: [ServerRecord], preferred: PreferredServerType?, interactor: MainInteractorInput) -> MainRecordsContext {
// Best server
let bestRecord = ConvertingServersOperation.calculateBestRecord(using: interactor, for: serverRecords, current: geoLocation)
// Preferred
var record: ServerRecord?
if preferred == .last
, let server = interactor.restoreServer()
, let found = ConvertingServersOperation.searchRecord(for: server, in: serverRecords) {
record = found
} else if preferred == .random {
let availableRecords: [ServerRecord]
if interactor.isFreemium {
availableRecords = serverRecords.filter { $0.isFreemium }
} else if interactor.isOverquota {
availableRecords = serverRecords.filter { $0.isOverquota }
} else {
availableRecords = serverRecords.filter { $0.isPremium }
}
let randomIndex = Int.random(in: 0..<(availableRecords.count))
if let searched = availableRecords[safe: randomIndex] {
record = searched
} else {
record = bestRecord
}
} else {
record = bestRecord
}
return MainRecordsContext(geoLocation: geoLocation, bestRecord: bestRecord, selectedRecord: record)
}
private static func calculateBestRecord(using interactor: MainInteractorInput, for records: [ServerRecord]?, current location: GeoLocation?) -> ServerRecord? {
enum FindBetterserver: Error {
case better
}
guard let records = records else { return nil }
let availableRecords: [ServerRecord]
if interactor.isFreemium {
availableRecords = records.filter { $0.isFreemium }
} else if interactor.isOverquota {
availableRecords = records.filter { $0.isOverquota }
} else {
availableRecords = records.filter { $0.isPremium }
}
guard let location = location
, let latitude = Double(location.lat)
, let longitude = Double(location.long)
, let bestRecord = BestRecord.calculate(for: availableRecords, latitude: latitude, longitude: longitude) else {
return nil
}
return bestRecord.record
}
private static func searchRecord(for server: Server, in records: [ServerRecord]) -> ServerRecord? {
records.first(where: { $0.server == server})
}
}
extension ConvertingServersOperation: ConvertingServersOperationOutput {}