//
//  MapLibreVectorTilesView.swift
//  Go Map!!
//
//  Created by Bryce Cogswell on 3/4/24.
//  Copyright © 2024 Bryce Cogswell. All rights reserved.
//

#if canImport(MapLibre)
import MapLibre

class MapLibreVectorTilesView: MLNMapView, MLNMapViewDelegate {
	let viewPort: MapViewPort
	let tileServer: TileServer

	init(viewPort: MapViewPort, tileServer: TileServer) {
		self.viewPort = viewPort
		self.tileServer = tileServer
		super.init(frame: .zero,
		           styleURL: URL(string: tileServer.url)!)

		// don't use MapLibre built-in gestures
		gestureRecognizers = nil
		delegate = self
		logoView.isHidden = true
		compassView.isHidden = true
		attributionButton.isHidden = true

		setPreferredFrameRate()

		updateUsingCurrentMapTransform()
		viewPort.mapTransform.onChange.subscribe(self) { [weak self] in
			self?.updateUsingCurrentMapTransform()
		}
	}

	func updateUsingCurrentMapTransform() {
		let center = viewPort.mapTransform.latLon(forScreenPoint: viewPort.mapTransform.center)
		let zoom = viewPort.mapTransform.zoom() - 1.0
		let dir = (360.0 + viewPort.mapTransform.rotation() * 180 / .pi).remainder(dividingBy: 360.0)
		guard
			center.lat.isFinite,
			center.lon.isFinite,
			zoom.isFinite
		else { return }

		self.setCenter(CLLocationCoordinate2D(latitude: center.lat,
		                                      longitude: center.lon),
		               zoomLevel: zoom,
		               direction: 360 - dir,
		               animated: false)
	}

	@available(*, unavailable)
	required init?(coder: NSCoder) {
		fatalError("init(coder:) has not been implemented")
	}

	deinit {}

	func mapView(_ map: MLNMapView, didFinishLoading style: MLNStyle) {
		let preferredKeys = preferredLanguageCodes()
		let args = preferredKeys.map { NSExpression(forKeyPath: $0) }
		let coalesceExpr = NSExpression(forFunction: "mgl_coalesce:",
		                                arguments: [NSExpression(forAggregate: args)])
		for layer in style.layers {
			guard
				let symbolLayer = layer as? MLNSymbolStyleLayer
			else {
				continue
			}
			symbolLayer.text = coalesceExpr
		}
	}

	func mapViewDidFinishLoadingMap(_ mapView: MLNMapView) {}

	private func preferredLanguageCodes() -> [String] {
		var keys: [String] = []
		func addKey(_ key: String) {
			if !keys.contains(key) {
				keys.append(key)
			}
		}
		// Walk the user's preferred languages and add name: variants
		for lang in Locale.preferredLanguages {
			let parts = lang.split(separator: "-")
			for i in (1...parts.count).reversed() {
				addKey("name:\(parts.prefix(i).joined(separator: "-"))")
				if i == 2, parts.count >= 3, let region = parts.last {
					addKey("name:\(parts[0])-\(region)")
				}
			}
		}
		addKey("name")
		addKey("name:en")
		return keys
	}

	func setPreferredFrameRate() {
		if #available(iOS 15.0, *) {
			let rate = Int(DisplayLink.shared.displayLink.preferredFrameRateRange.maximum)
			preferredFramesPerSecond = MLNMapViewPreferredFramesPerSecond(rawValue: rate)
		}
	}
}

extension MapLibreVectorTilesView: TilesProvider {
	func currentTiles() -> [String] {
		return []
	}

	func zoomLevel() -> Int {
		return 18
	}

	func maxZoom() -> Int {
		return 21
	}

	func downloadTile(forKey cacheKey: String, completion: @escaping () -> Void) {
		print("xx")
	}

	func purgeTileCache() {
		MLNOfflineStorage.shared.resetDatabase(completionHandler: { _ in })
		URLCache.shared.removeAllCachedResponses()
		setNeedsLayout()
	}
}

extension MapLibreVectorTilesView: DiskCacheSizeProtocol {
	func getDiskCacheSize() async -> (size: Int, count: Int) {
		let size = Int(MLNOfflineStorage.shared.countOfBytesCompleted)
		return (size, 1)
	}
}
#else

// Create a minimal dummy implementation that asserts if used
import UIKit

class MapLibreVectorTilesView: UIView, TilesProvider, DiskCacheSizeProtocol {
	var viewPort: MapViewPort
	let tileServer: TileServer
	var styleURL: URL

	init(viewPort: MapViewPort, tileServer: TileServer) {
		// Sorry, but MapLibre isn't supported in this environment
		fatalError()
	}

	@available(*, unavailable)
	required init?(coder: NSCoder) {
		// Sorry, but MapLibre isn't supported in this environment
		fatalError()
	}

	func currentTiles() -> [String] {
		return []
	}

	func zoomLevel() -> Int {
		return 0
	}

	func maxZoom() -> Int {
		return 0
	}

	func downloadTile(forKey cacheKey: String, completion: @escaping () -> Void) {}

	func purgeTileCache() {}

	func getDiskCacheSize() async -> (size: Int, count: Int) {
		return (0, 0)
	}

	func setPreferredFrameRate() {}
}
#endif

extension MapLibreVectorTilesView: MapLayersView.LayerOrView {
	var hasTileServer: TileServer? {
		return tileServer
	}

	func removeFromSuper() {
		removeFromSuperview()
	}
}
