136 lines
5.3 KiB
Swift
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 {}
|