import MapboxMaps
import Foundation

@objc(DataJoinExample)
final class DataJoinExample: UIViewController, ExampleProtocol {
    var mapView: MapView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set up map and camera
        let centerCoordinate = CLLocationCoordinate2D(latitude: 50, longitude: 12)
        let camera = CameraOptions(center: centerCoordinate, zoom: 1.6)
        let mapInitOptions = MapInitOptions(cameraOptions: camera, styleURI: .light)

        mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions)
        mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth]

        view.addSubview(mapView)

        // Add the data layer once the map has finished loading.
        mapView.mapboxMap.onNext(.mapLoaded) { _ in
            self.addJSONDataLayer()
        }
    }

    func addJSONDataLayer() {
        // Create an array of countries and their HDI score
        // Data: UN Human Development Index 2017 Europe extract
        // Source: https://ourworldindata.org/human-development-index
        struct Country {
            let code: String
            let hdi: Double
        }

        let countries = [Country(code: "ROU", hdi: 0.811),
                        Country(code: "RUS", hdi: 0.816),
                        Country(code: "SRB", hdi: 0.787 ),
                        Country(code: "SVK", hdi: 0.855 ),
                        Country(code: "SVN", hdi: 0.896 ),
                        Country(code: "ESP", hdi: 0.891 ),
                        Country(code: "SWE", hdi: 0.933 ),
                        Country(code: "CHE", hdi: 0.944 ),
                        Country(code: "HRV", hdi: 0.831 ),
                        Country(code: "CZE", hdi: 0.888 ),
                        Country(code: "DNK", hdi: 0.929 ),
                        Country(code: "EST", hdi: 0.871 ),
                        Country(code: "FIN", hdi: 0.92 ),
                        Country(code: "FRA", hdi: 0.901 ),
                        Country(code: "DEU", hdi: 0.936 ),
                        Country(code: "GRC", hdi: 0.87 ),
                        Country(code: "ALB", hdi: 0.785 ),
                        Country(code: "AND", hdi: 0.858 ),
                        Country(code: "AUT", hdi: 0.908 ),
                        Country(code: "BLR", hdi: 0.808 ),
                        Country(code: "BEL", hdi: 0.916 ),
                        Country(code: "BIH", hdi: 0.768 ),
                        Country(code: "BGR", hdi: 0.813 ),
                        Country(code: "MKD", hdi: 0.757 ),
                        Country(code: "MLT", hdi: 0.878 ),
                        Country(code: "MDA", hdi: 0.7 ),
                        Country(code: "MNE", hdi: 0.814 ),
                        Country(code: "NLD", hdi: 0.931 ),
                        Country(code: "NOR", hdi: 0.953 ),
                        Country(code: "POL", hdi: 0.865 ),
                        Country(code: "PRT", hdi: 0.847 ),
                        Country(code: "HUN", hdi: 0.838 ),
                        Country(code: "ISL", hdi: 0.935 ),
                        Country(code: "IRL", hdi: 0.938 ),
                        Country(code: "ITA", hdi: 0.88 ),
                        Country(code: "LVA", hdi: 0.847 ),
                        Country(code: "LIE", hdi: 0.916 ),
                        Country(code: "LTU", hdi: 0.858 ),
                        Country(code: "LUX", hdi: 0.904 ),
                        Country(code: "UKR", hdi: 0.751 ),
                        Country(code: "GBR", hdi: 0.922 )]

        // Create the source for country polygons using the Mapbox Countries tileset
        // The polygons contain an ISO 3166 alpha-3 code which can be used to for joining the data
        // https://docs.mapbox.com/vector-tiles/reference/mapbox-countries-v1
        let sourceID = "countries"
        var source = VectorSource()
        source.url = "mapbox://mapbox.country-boundaries-v1"

        // Add layer from the vector tile source to create the choropleth
        var layer = FillLayer(id: "countries")
        layer.source = sourceID
        layer.sourceLayer = "country_boundaries"

        // Build a GL match expression that defines the color for every vector tile feature
        // https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#match
        // Use the ISO 3166-1 alpha 3 code as the lookup key for the country shape
        let expressionHeader =
            """
            [
            "match",
            ["get", "iso_3166_1_alpha_3"],

            """

        // Calculate color values for each country based on 'hdi' value
        var green: Double
        var expressionBody: String = ""
        for country in countries {
            // Convert the range of data values to a suitable color
            green = country.hdi*255
            expressionBody += """
            "\(country.code)",
            "rgb(0, \(green), 0)",

            """
        }

        // Last value is the default, used where there is no data
        let expressionFooter =
            """
            "rgba(0, 0, 0, 0)"
            ]
            """

        // Combine the expression strings into a single JSON expression
        // You can alternatively translate JSON expressions into Swift: https://docs.mapbox.com/ios/maps/guides/styles/use-expressions/
        let jsonExpression = expressionHeader + expressionBody + expressionFooter

        // Add the source
        // Insert the vector layer below the 'admin-1-boundary-bg' layer in the style
        // Join data to the vector layer
        do {
            try mapView.mapboxMap.style.addSource(source, id: sourceID)
            try mapView.mapboxMap.style.addLayer(layer, layerPosition: .below("admin-1-boundary-bg"))
            if let expressionData = jsonExpression.data(using: .utf8) {
                let expJSONObject = try JSONSerialization.jsonObject(with: expressionData, options: [])
                try mapView.mapboxMap.style.setLayerProperty(for: "countries",
                                                           property: "fill-color",
                                                           value: expJSONObject)
            }
        } catch {
            print("Failed to add the data layer. Error: \(error.localizedDescription)")
        }
    }
}
