from shapely.geometry import box, Point, shape
import mercantile
import json
import geojson


def version_to_array(version: str):
    """ Convert a semver string into it's components as integers """

    version_array = version.split('.')
    version_major = version_array[0]
    version_minor = version_array[1] or 0
    version_patch = version_array[2] or 0

    return [version_major, version_minor, version_patch]


def bbox_to_polygon_wkt(bbox: list):
    """ Get a polygon from the bbox """

    return box(bbox[0], bbox[1], bbox[2], bbox[3]).wkt


def bbox_str_to_list(bbox: str):
    """ Parse the bbox query param and return a list of floats """

    bboxList = bbox.split(',')
    return list(map(float, bboxList))


def geojson_to_bbox(geojson):
    """ Convert polygon geojson to bbox list """

    polygon = json.loads(geojson)
    bbox = [polygon['coordinates'][0][0][0], polygon['coordinates'][0][0][1], polygon['coordinates'][0][2][0], polygon['coordinates'][0][2][1]]
    return bbox


def point_list_to_wkt(centroid: list):
    """ Convert a python list x,y to WKT """

    return f'SRID=4326;{Point(centroid[0], centroid[1]).wkt}'


def bbox_to_quadkeys(bbox: list, zoom: int):
    """ Find all quadkeys in a bbox """
    tiles = mercantile.tiles(bbox[0], bbox[1], bbox[2], bbox[3], int(zoom))
    quadkeys = []
    for tile in tiles:
        quadkeys.append(mercantile.quadkey(tile))

    return quadkeys


def tuple_to_dict(t):
    """ Convert the results tuple to dict """
    return {"quadkey": t[0], "ml_prediction": t[1], "osm_building_area": t[2]}


def geojson_bounds(geojson):
    """
    Get the bounds of a geojson feature collection
    Based on https://github.com/Luqqk/geojson-bbox/blob/master/gbbox/geojson_bbox.py
    """

    # flatten the coordinates
    coords = list(flatten([f['geometry']['coordinates']
                  for f in geojson['features']]))

    return [min(coords[::2]), min(coords[1::2]),
            max(coords[::2]), max(coords[1::2])]


def flatten(value):
    """
    Helper function to flatten coordinates
    """
    for val in value:
        if isinstance(val, list):
            for subval in flatten(val):
                yield subval
        else:
            yield val


def polygon_to_wkt(geojson):
    """
    Convert a geojson polygon to wkt
    """
    return f'SRID=4326;{shape(geojson).wkt}'


class InvalidGeojson(Exception):
    """ Custom exception for invalid GeoJSON"""
    pass


def validate_geojson(data):
    """
    Validate geojson
    """

    if not (isinstance(data, dict)):
        return False

    if not isinstance(data.get('features'), list):
        return False

    gj = geojson.FeatureCollection([geojson.Feature(f) for f in data['features']])
    return gj.is_valid