// // AlertSimulatorViewController.swift // Earthquake Network // // Created by Busi Andrea on 14/11/2020. // Copyright © 2020 Earthquake Network. All rights reserved. // import UIKit import MapKit import Shogun class AlertSimulatorViewController: UIViewController, MKMapViewDelegate { struct MapCircle { let identifier: String let distance: CLLocationDistance let color: UIColor } // MARK: - Internal private let mapCircles = [ MapCircle(identifier: "circle_internal", distance: 50_000, color: AppTheme.Colors.red), MapCircle(identifier: "circle_external", distance: 750_000, color: AppTheme.Colors.green) ] private lazy var viewfinderView: UIView = { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .clear view.isUserInteractionEnabled = false // add central vertiacl and horizontal lines let viewfinderVertical = UIView() view.addSubview(viewfinderVertical) viewfinderVertical.translatesAutoresizingMaskIntoConstraints = false viewfinderVertical.backgroundColor = AppTheme.Colors.lightBlue viewfinderVertical.topAnchor.constraint(equalTo: view.topAnchor).isActive = true viewfinderVertical.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true viewfinderVertical.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true viewfinderVertical.widthAnchor.constraint(equalToConstant: 2.0).isActive = true let viewfinderHorizontal = UIView() view.addSubview(viewfinderHorizontal) viewfinderHorizontal.translatesAutoresizingMaskIntoConstraints = false viewfinderHorizontal.backgroundColor = AppTheme.Colors.lightBlue viewfinderHorizontal.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true viewfinderHorizontal.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true viewfinderHorizontal.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true viewfinderHorizontal.heightAnchor.constraint(equalToConstant: 2.0).isActive = true return view }() private lazy var closeButton: EQNBlurredCloseButton = { let button = EQNBlurredCloseButton() button.translatesAutoresizingMaskIntoConstraints = false button.addTarget(self, action: #selector(closeTapped(_:)), for: .touchUpInside) button.setTitleColor(.darkGray, for: .normal) return button }() private lazy var mapView: MKMapView = { let map = MKMapView() map.translatesAutoresizingMaskIntoConstraints = false map.showsUserLocation = true map.delegate = self return map }() private lazy var calculateButton: UIButton = { let button = EQNRoundedButton(type: .custom) button.backgroundColor = .lightGray button.setTitleColor(AppTheme.Colors.darkGray, for: .normal) button.translatesAutoresizingMaskIntoConstraints = false button.setTitle(NSLocalizedString("globe_simulation_button", comment: "").uppercased(), for: .normal) button.addTarget(self, action: #selector(calculateTapped(_:)), for: .touchUpInside) return button }() // MARK: - View Lifecycle override func viewDidLoad() { super.viewDidLoad() let closeButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(closeTapped(_:))) navigationItem.rightBarButtonItem = closeButton setupUI() configureMap() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // show an alert view functionality details let alert = UIAlertController(title: NSLocalizedString("main_simulator", comment: ""), message: NSLocalizedString("globe_simulation", comment: ""), preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("main_understood", comment: ""), style: .default)) present(alert, animated: true) } // MARK: - Private private func setupUI() { view.addSubview(mapView) view.addSubview(closeButton) view.addSubview(viewfinderView) view.addSubview(calculateButton) mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true mapView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true mapView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true closeButton.addDefaultConstraint(to: view) viewfinderView.topAnchor.constraint(equalTo: mapView.topAnchor).isActive = true viewfinderView.bottomAnchor.constraint(equalTo: mapView.bottomAnchor).isActive = true viewfinderView.leftAnchor.constraint(equalTo: mapView.leftAnchor).isActive = true viewfinderView.rightAnchor.constraint(equalTo: mapView.rightAnchor).isActive = true calculateButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0).isActive = true calculateButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0).isActive = true calculateButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10.0).isActive = true calculateButton.heightAnchor.constraint(equalToConstant: 40.0).isActive = true } private func configureMap() { guard let lastPosition = EQNUser.default().lastPosition else { return } let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0) let region = MKCoordinateRegion(center: lastPosition.coordinate, span: span) mapView.setCenter(lastPosition.coordinate, animated: false) mapView.setRegion(region, animated: true) mapCircles.forEach { (mapCircle) in addCircle(location: lastPosition, radius: mapCircle.distance, id: mapCircle.identifier) } } private func addCircle(location: CLLocation, radius: CLLocationDistance, id: String) { let circle = MKCircle(center: location.coordinate, radius: radius) circle.title = id mapView.addOverlay(circle) } // MARK: - Actions @objc func closeTapped(_ sender: Any) { dismiss(animated: true) } @objc func calculateTapped(_ sender: Any) { guard let lastPosition = EQNUser.default().lastPosition, let circleMin = mapCircles.sorted(by: { $0.distance < $1.distance }).first, let circleMax = mapCircles.sorted(by: { $0.distance > $1.distance }).first else { return } let location = CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude) let distance = lastPosition.distance(from: location) // check for a valid point selected by the user if distance < circleMin.distance || distance > circleMax.distance { let message = distance < circleMin.distance ? "globe_simulation_outside" : "globe_simulation_inside" let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""), message: NSLocalizedString(message, comment: ""), preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .cancel, handler: nil)) present(alert, animated: true) return } let url = EQNGeneratoreURLServer.urlAlertSimulator() ServerRequest.default().inviaInformazioniAlServer(with: url, richiesta: .alertSimulator) { (result) in if let result = result as? String { DispatchQueue.main.async { self.elaborateServerResponse(result, distance: distance) } } } failure: { (error) in DispatchQueue.main.async { let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""), message: error?.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .cancel, handler: nil)) self.present(alert, animated: true) return } } } private func elaborateServerResponse(_ response: String, distance: CLLocationDistance) { let isTop10k = EQNPurchaseUtility.isTop10kEnabled() let isTop100k = EQNPurchaseUtility.isTop100kEnabled() // server response (like '3411#12') let split = response.split(separator: "#") let people: Int let popleTop10k: Int if split.count > 1 { //caso top10k=0 e top100k=0 people = Int(split[0]) ?? 0 popleTop10k = Int(split[1]) ?? 0 } else { //caso top10k=1 oppure top100k=1 people = Int(split[0]) ?? 0 popleTop10k = 0 } let distanceKm = distance / 1000 let delay = round(Double(people) / 1000.0 * 0.15) //calcolo il ritardo di allerta dovuto alla coda di utenti let leadTime = round(Double(distanceKm) / 8.0 - 3.0) //calcolo il tempo di allerta teorico nel caso di 0 utenti in coda let actualTime = leadTime - delay //calcolo il tempo di allerta per l'utente //il messaggio mostrato all'utente cambia in base al fatto di possedere un abbonamento ed al tempo di allerta calcolato //il messaggio include tag html perché cambio il colore di parte delle stringa, non so se si fa così anche in iOS var message = "" if (isTop10k) { if (people == 0) { message = String(format: NSLocalizedString("globe_simulation_message6", comment: ""), "TOP 10K", leadTime) } else { message = String(format: NSLocalizedString("globe_simulation_message4", comment: ""), "TOP 10K", leadTime, Double(people)) } } else if (isTop100k) { message = String(format: NSLocalizedString("globe_simulation_message5", comment: ""), "TOP 100K", leadTime, Double(people), "TOP 10K") } else { if (leadTime / actualTime < 1.4) { message = String(format: NSLocalizedString("globe_simulation_message3", comment: ""), leadTime, Double(people), Double(popleTop10k)) } else { if (actualTime > 0) { message = String(format: NSLocalizedString("globe_simulation_message1", comment: ""), leadTime, Double(people), actualTime, Double(popleTop10k)) } else { let updatedActualTime = actualTime * -1; message = String(format: NSLocalizedString("globe_simulation_message2", comment: ""), leadTime, Double(people), updatedActualTime, Double(popleTop10k)) } } } let alert = UIAlertController(title: NSLocalizedString("main_simulator", comment: ""), message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("globe_simulation_priority", comment: ""), style: .default) { (action) in self.navigateToSubscriptions() }) alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .cancel, handler: nil)) present(alert, animated: true) } private func navigateToSubscriptions() { let controller = SubscriptionsViewController() let navigationController = UINavigationController(rootViewController: controller) present(navigationController, animated: true) } // MARK: MKMapViewDelegate func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { if let overlay = overlay as? MKCircle, let mapCircle = mapCircles.first(where: { $0.identifier == overlay.title }) { let circle = MKCircleRenderer(overlay: overlay) circle.strokeColor = mapCircle.color circle.fillColor = AppTheme.Colors.green.withAlphaComponent(0.1) circle.lineWidth = 2.0 return circle } return MKOverlayRenderer(overlay: overlay) } }