from util.generic.string cimport TString
import math

cdef extern from "contrib/libs/h3/h3lib/include/h3api.h":
    ctypedef struct GeoCoord:
        double lat
        double lon

    ctypedef struct GeoBoundary:
        int numVerts
        GeoCoord[10] verts

    unsigned long long geoToH3(GeoCoord* g, int res)

    int h3IsValid(unsigned long long h)

    void h3ToGeoBoundary(unsigned long long h3, GeoBoundary* gp)

    unsigned long long h3ToParent(unsigned long long h3, int res)


cdef _geo_to_h3(double lat, double lon, int resolution):
    cdef GeoCoord c = GeoCoord(
        degs_to_rads(mercator_lat(lat)), 
        degs_to_rads(mercator_lon(lon)),
    )
    return geoToH3(&c, resolution)

cdef _is_valid(unsigned long long h3_int):
    return h3IsValid(h3_int)

cdef _h3_to_geo_boundary(unsigned long long h3_int):
    cdef GeoBoundary boundary
    h3ToGeoBoundary(h3_int, &boundary)
    result = []
    for i in range(boundary.numVerts):
        # TODO: Convert from rads_to_degs
        result.append([
            mercator_lat(rads_to_degs(boundary.verts[i].lat)),
            mercator_lon(rads_to_degs(boundary.verts[i].lon)),
        ])
    return result

cdef _h3_to_parent(unsigned long long h3_int, int res):
    return h3ToParent(h3_int, res)

def rads_to_degs(rad):
    return 180. * rad / math.pi

def degs_to_rads(deg):
    return math.pi * deg / 180.

def mercator_lat(lat):
    return lat - 180 if lat > 90 else lat

def mercator_lon(lon):
    return lon - 360 if lon > 180 else lon

def geo_to_h3(lat, lon, resolution):
    return h3_to_string(_geo_to_h3(lat, lon, resolution))

def h3_to_string(h3_int):
    return format(h3_int, 'x')

def string_to_h3(h3_address):
    return int(h3_address, 16)

def is_valid(h3_address):
    try:
        return _is_valid(string_to_h3(h3_address)) == 1
    except:
        return False

def h3_to_geo_boundary(h3_address):
    return _h3_to_geo_boundary(string_to_h3(h3_address))

def h3_to_parent(h3_address, resolution):
    return h3_to_string(_h3_to_parent(string_to_h3(h3_address), resolution))

def h3_to_center(h3_address):
    # TODO: Test function
    boundary = h3_to_geo_boundary(h3_address)
    mean_lat, mean_lon = 0, 0
    for coords in boundary:
        mean_lat += coords[0]
        mean_lon += coords[1]
    n_coords = float(len(boundary))
    return [mean_lat / n_coords, mean_lon / n_coords]

# TODO implement h3_to_children
