// (C) 2020 GoodData Corporation
import get = require("lodash/get");
import mapboxgl = require("mapbox-gl");

import { DEFAULT_CENTER, DEFAULT_WORLD_BOUNDS, DEFAULT_ZOOM, VIEWPORTS } from "../../constants/geoChart";
import { IGeoConfig, IGeoConfigViewport, IGeoLngLat, IGeoLngLatBounds } from "../../interfaces/GeoChart";

interface IGeoViewport {
    bounds?: mapboxgl.LngLatBoundsLike;
    center?: IGeoLngLat;
    zoom?: number;
}

export function getViewportOptions(data: IGeoLngLat[], config: IGeoConfig): IGeoViewport {
    const center: IGeoLngLat = get<IGeoConfig, "center">(config, "center");
    const zoom: number = get<IGeoConfig, "zoom", number>(config, "zoom", DEFAULT_ZOOM);
    const { area }: IGeoConfigViewport = get<IGeoConfig, "viewport", {}>(config, "viewport", {});

    // use `center` config if it exists
    if (!center) {
        if (VIEWPORTS[area]) {
            return {
                bounds: VIEWPORTS[area],
            };
        } else {
            const lngLatBounds: IGeoLngLatBounds = getLngLatBounds(data);
            if (lngLatBounds) {
                return {
                    bounds: [lngLatBounds.northEast, lngLatBounds.southWest],
                };
            }

            return {
                center: DEFAULT_CENTER,
                zoom,
            };
        }
    }

    return {
        center,
        zoom,
    };
}

/*
 * @method getLngLatBounds: IGeoLngLatBounds
 * Represents a rectangular geographical area on a map.
 *
 * @example
 *
 * ```js
 * const corner1 = [40.712, -74.227],
 * const corner2 = [40.774, -74.125],
 * const bounds = getLngLatBounds([corner1, corner2]);
 *
 * bounds && map.fitBounds([bounds.northEast, bounds.southWest], { padding: 60 });
 * ```
 */
export function getLngLatBounds(lnglats: IGeoLngLat[]): IGeoLngLatBounds {
    if (!lnglats || !lnglats.length) {
        return;
    }

    return lnglats.reduce(extendLngLatBounds, undefined) || DEFAULT_WORLD_BOUNDS;
}

// @method extendLngLatBounds: IGeoLngLatBounds
// Extend the bounds to contain the given point
function extendLngLatBounds(bounds: IGeoLngLatBounds, lnglat: IGeoLngLat): IGeoLngLatBounds {
    if (!lnglat) {
        return bounds;
    }

    if (!bounds) {
        return {
            northEast: lnglat,
            southWest: lnglat,
        };
    }

    const { northEast, southWest } = bounds;
    return {
        northEast: {
            lat: Math.max(lnglat.lat, northEast.lat),
            lng: Math.max(lnglat.lng, northEast.lng),
        },
        southWest: {
            lat: Math.min(lnglat.lat, southWest.lat),
            lng: Math.min(lnglat.lng, southWest.lng),
        },
    };
}
