import cv2 as cv
import numpy as np
import math
import base64
import io
import os
import sys
import urllib.request

from yandex.maps import geolib3

from .feature import Point


def download(url, retry=3):
    data = None
    for _ in range(retry):
        try:
            response = urllib.request.urlopen(url)
            data = response.read()
            break
        except:
            pass

    assert data is not None
    return data


def image_to_png_base64(image):
    ok, png = cv.imencode('.png', image)
    assert ok

    return str(base64.b64encode(png), encoding='ascii')


def mask_from_png_base64(s):
    buffer = bytes(s, encoding='ascii')
    png = np.frombuffer(base64.b64decode(buffer), dtype=np.uint8)
    return cv.imdecode(png, cv.IMREAD_GRAYSCALE)


def get_archive():
    main_path = os.path.abspath(sys.argv[0])
    dir = os.path.dirname(main_path)

    names = filter(
        lambda name: os.path.isfile(name) and name.endswith('.py'),
        map(lambda name: os.path.join(dir, name), os.listdir(dir)),
    )

    return list(names)


def array_to_base64(dictionary):
    stream = io.BytesIO()
    np.savez(stream, **dictionary)
    return str(base64.b64encode(stream.getvalue()), encoding='ascii')


def array_from_base64(s):
    buffer = base64.b64decode(bytes(s, encoding='ascii'))
    stream = io.BytesIO(buffer)
    return np.load(stream, allow_pickle=True)


def mercator2geodetic(x, y):  # longtitude, latitude
    geolib_point = geolib3.mercator_to_geo(geolib3.Point2(x, y))
    return Point(geolib_point.x, geolib_point.y)


def mercatorRatio(geoPoint):
    return math.cos(geoPoint.y * math.pi / 180)


def mercatorToMeters(mercatorDistance, mercatorPoint):
    geoPoint = mercator2geodetic(mercatorPoint.x, mercatorPoint.y)
    return mercatorDistance * mercatorRatio(geoPoint)


def distance(firstMercatorPoint, secondMercatorPoint):
    mercatorDistance = math.sqrt(
        (firstMercatorPoint.x - secondMercatorPoint.x) ** 2 + (firstMercatorPoint.y - secondMercatorPoint.y) ** 2
    )

    return mercatorToMeters(mercatorDistance, firstMercatorPoint)


def heading_abs_diff(first, second):
    diff = first - second

    if diff < 0:
        diff += 360

    if diff > 180:
        diff = 360 - diff

    return diff
