"""
    ulmo.usgs.ned.core
    ~~~~~~~~~~~~~~~~~~~~~

    This module provides access to the `National Elevation Dataset` provided by the `United States Geological
    Survey`_ `National Map`_ system.

    .. _National Elevation Dataset: http://ned.usgs.gov/
    .. _United States Geological Survey: http://www.usgs.gov/
    .. _National Map: http://nationalmap.gov

"""
from __future__ import print_function
from past.builtins import basestring

from geojson import Feature, FeatureCollection, Polygon
import logging
import os
import requests
from tsgettoolbox.ulmo import util


# NED ftp url.
NED_FTP_URL = "ftp://rockyftp.cr.usgs.gov/vdelivery/Datasets/Staged/NED/<layer>/IMG/"

# default file path (appended to default ulmo path)
DEFAULT_FILE_PATH = "usgs/ned/"

layer_dict = {
    "Alaska 2 arc-second": "4f70aaece4b058caae3f8de9",
    "1 arc-second": "4f70aa71e4b058caae3f8de1",
    "1/3 arc-second": "4f70aa9fe4b058caae3f8de5",
    "1/9 arc-second": "4f70aac4e4b058caae3f8de7",
}

# configure logging
LOG_FORMAT = "%(message)s"
logging.basicConfig(format=LOG_FORMAT)
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)


def get_available_layers():
    """return list of available data layers
    """
    return list(layer_dict.keys())


def get_raster_availability(layer, bbox=None):
    """retrieve metadata for raster tiles that cover the given bounding box
    for the specified data layer.

    Parameters
    ----------
    layer : str
        dataset layer name. (see get_available_layers for list)
    bbox : (sequence of float|str)
        bounding box of in geographic coordinates of area to download tiles
        in the format (min longitude, min latitude, max longitude, max latitude)

    Returns
    -------
    metadata : geojson FeatureCollection
        returns metadata including download urls as a FeatureCollection
    """

    base_url = "https://www.sciencebase.gov/catalog/items"
    params = [
        ("parentId", layer_dict[layer]),
        ("filter", "tags=IMG"),
        ("max", 1000),
        ("fields", "webLinks,spatial,title"),
        ("format", "json"),
    ]

    if bbox:
        xmin, ymin, xmax, ymax = [float(n) for n in bbox]
        polygon = "POLYGON (({}))".format(
            ",".join(
                [
                    (repr(x) + " " + repr(y))
                    for x, y in [
                        (xmin, ymax),
                        (xmin, ymin),
                        (xmax, ymin),
                        (xmax, ymax),
                        (xmin, ymax),
                    ]
                ]
            )
        )
        params.append(
            (
                "filter",
                'spatialQuery={{wkt:"{}",relation:"{}"}}'.format(polygon, "intersects"),
            )
        )

    features = []
    url = base_url
    while url:
        r = requests.get(url, params)
        print("retrieving raster availability from %s" % r.url)
        params = []  # not needed after first request
        content = r.json()
        for item in content["items"]:
            feature = Feature(
                geometry=Polygon(_bbox2poly(item["spatial"]["boundingBox"])),
                id=item["id"],
                properties={
                    "name": item["title"],
                    "layer": layer,
                    "format": ".img",
                    "download url": [
                        x for x in item["webLinks"] if x["type"] == "download"
                    ][0]["uri"],
                },
            )
            features.append(feature)

        if content.get("nextlink"):
            url = content["nextlink"]["url"]
        else:
            break

    return FeatureCollection(features)


def get_raster(
    layer, bbox, path=None, update_cache=False, check_modified=False, mosaic=False
):
    """downloads National Elevation Dataset raster tiles that cover the given bounding box
    for the specified data layer.

    Parameters
    ----------
    layer : str
        dataset layer name. (see get_available_layers for list)
    bbox : (sequence of float|str)
        bounding box of in geographic coordinates of area to download tiles
        in the format (min longitude, min latitude, max longitude, max latitude)
    path : ``None`` or path
        if ``None`` default path will be used
    update_cache: ``True`` or ``False`` (default)
        if ``False`` and output file already exists use it.
    check_modified: ``True`` or ``False`` (default)
        if tile exists in path, check if newer file exists online and download if available.
    mosaic: ``True`` or ``False`` (default)
        if ``True``, mosaic and clip downloaded tiles to the extents of the bbox provided. Requires
        rasterio package and GDAL.

    Returns
    -------
    raster_tiles : geojson FeatureCollection
        metadata as a FeatureCollection. local url of downloaded data is in feature['properties']['file']
    """
    _check_layer(layer)

    raster_tiles = _download_tiles(
        get_raster_availability(layer, bbox), path=path, check_modified=check_modified
    )

    if mosaic:
        if path is None:
            path = os.path.join(util.get_ulmo_dir(), DEFAULT_FILE_PATH)

        util.mkdir_if_doesnt_exist(os.path.join(path, "by_boundingbox"))
        xmin, ymin, xmax, ymax = [float(n) for n in bbox]
        uid = util.generate_raster_uid(layer, xmin, ymin, xmax, ymax)
        output_path = os.path.join(path, "by_boundingbox", uid + ".tif")

        if os.path.isfile(output_path) and not update_cache:
            return output_path

        raster_files = [tile["properties"]["file"] for tile in raster_tiles["features"]]
        util.mosaic_and_clip(raster_files, xmin, ymin, xmax, ymax, output_path)
        return [output_path]

    return raster_tiles


def _check_layer(layer):
    """
    make sure the passed layer name is one of the handled options
    """

    if not layer in get_available_layers():
        err_msg = "The specified layer parameter ({})".format(layer)
        err_msg += "\nis not in the available options:"
        err_msg += "\n\t".join(get_available_layers())
        raise ValueError(err_msg)


def _download_features(feature_ids, path=None, check_modified=False):
    if path is None:
        path = os.path.join(util.get_ulmo_dir(), DEFAULT_FILE_PATH)

    if isinstance(feature_ids, basestring):
        feature_ids = [feature_ids]

    tiles = []
    tile_fmt = ".img"
    for feature_id in feature_ids:
        url = SCIENCEBASE_ITEM_URL % feature_id
        metadata = requests.get(url).json()
        layer = [a for a in list(layer_dict.keys()) if a in metadata["title"]][0]
        layer_path = os.path.join(path, layer_dict[layer])
        tile_urls = [
            link["uri"] for link in metadata["webLinks"] if link["type"] == "download"
        ]
        tiles.append(
            {
                "feature_id": feature_id,
                "tiles": util.download_tiles(
                    layer_path, tile_urls, tile_fmt, check_modified
                ),
            }
        )

    return tiles


def _bbox2poly(bbox):
    xmin = bbox["minX"]
    xmax = bbox["maxX"]
    ymin = bbox["minY"]
    ymax = bbox["maxY"]

    return [[(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin), (xmin, ymin)]]


def _download_tiles(tiles, path=None, check_modified=False):

    if path is None:
        path = os.path.join(util.get_ulmo_dir(), DEFAULT_FILE_PATH)

    for tile in tiles["features"]:

        metadata = tile["properties"]
        layer_path = os.path.join(path, layer_dict[metadata["layer"]])
        tile["properties"]["file"] = util.download_tiles(
            layer_path, metadata["download url"], metadata["format"], check_modified
        )[0]

    return tiles
