import argparse
import folium
import json
import requests
import numpy as np
import cv2
from icons import TF_ICONS

BASE_ICON_SIZE = 24


ICONS_SIZE_BY_TYPE = {}
def icon_size_by_type(type):
    global ICONS_SIZE_BY_TYPE
    size = ICONS_SIZE_BY_TYPE.get(type, None)
    if size is None:
        icon_url = TF_ICONS.get(type, None)
        if icon_url is None:
            print("Icons for type " + type + " not found")
            return None
        icon = cv2.imdecode(np.frombuffer(requests.get(icon_url.format(BASE_ICON_SIZE)).content, dtype = np.uint8), cv2.IMREAD_COLOR)
        print(type + ": " + icon_url)
        size = icon.shape[:2][::-1]
        if (size[0] >= size[1]):
            size = (BASE_ICON_SIZE, size[1] * BASE_ICON_SIZE // size[0])
        else:
            size = (size[0] * BASE_ICON_SIZE // size[1], BASE_ICON_SIZE)
        ICONS_SIZE_BY_TYPE[type] = size
    return size


def layout_photos(photos):
    def _make_photos_row(photos, cols_cnt, photo_size):
        result = '<tr>'
        full_cnt = min(cols_cnt, len(photos))
        for i in range(full_cnt):
            photo = photos[i]
            result += '<td><a href="{}"><img src="{}" height="{}"/></a></td>'.format(photo["url"],photo["url"], photo_size)
        if (full_cnt < cols_cnt):
            for i in range(full_cnt, cols_cnt):
                result += '<td></td>'
        result += '</tr>'
        return result

    photos_cnt = len(photos)
    if (photos_cnt == 1):
        photo = photos[0]
        photo_size = 500
        result = '<a href="{}"><img src="{}" height="{}"/></a>'.format(photo["url"],photo["url"], photo_size)
    else:
        result = "<table>"
        if (photos_cnt < 5):
            rows_cnt = photos_cnt // 2 + (1 if 0 != (photos_cnt % 2) else 0)
            for j in range(rows_cnt):
                result += _make_photos_row(photos[2*j : 2*j + 2], 2, photo_size = 400)
        elif (photos_cnt < 10):
            rows_cnt = photos_cnt // 3 + (1 if 0 != (photos_cnt % 3) else 0)
            for j in range(rows_cnt):
                result += _make_photos_row(photos[3*j : 3*j + 3], 3, photo_size = 300)
        elif (photos_cnt < 17):
            rows_cnt = photos_cnt // 4 + (1 if 0 != (photos_cnt % 4) else 0)
            for j in range(rows_cnt):
                result += _make_photos_row(photos[4*j : 4*j + 4], 4, photo_size = 200)
        else:
            photo_size = 150
            rows_cnt = photos_cnt // 5 + (1 if 0 != (photos_cnt % 5) else 0)
            for j in range(rows_cnt):
                result += _make_photos_row(photos[5*j : 5*j + 5], 5, photo_size = 150)
        result += "</table>"
    return result


def get_base_location(features):
    base_location = [0., 0.]
    for sign in features:
        sign_coords = sign["geometry"]["coordinates"][::-1]
        base_location[0] += sign_coords[0]
        base_location[1] += sign_coords[1]
    return [x / len(features) for x in base_location]


def get_prefix(type):
    if type == "house_number":
        return "house number"
    elif type == "traffic_light":
        return "traffic light"
    else:
        return type[:type.find('_')]


def make_groups_by_type_prefix(features):
    obj_types = set()
    for obj in features:
        if obj["properties"]["type"] != "sign":
            obj_types.add(obj["properties"]["type"])
        else:
            obj_types.add(obj["properties"]["sign_type"])

    group_by_type = {}
    for type in obj_types:
        prefix = get_prefix(type)
        if prefix not in group_by_type:
            group_by_type[prefix] = folium.FeatureGroup(prefix, show=False)
    return group_by_type


def get_object_icon(obj):
    if obj["properties"]["type"] != "sign":
        type = obj["properties"]["type"]
    else:
        type = obj["properties"]["sign_type"]

    if type != "house_number":
        icon_url = TF_ICONS.get(type, None)
        if icon_url is None:
            return None
        return folium.CustomIcon(icon_url.format(BASE_ICON_SIZE), icon_size=icon_size_by_type(type))
    else:
        number = obj["properties"]["number"]

        height = BASE_ICON_SIZE
        width = (BASE_ICON_SIZE // 2 + 1) * len(number)
        icon_size = (width, height)
        font_size = height - 4

        html_template = "<div style='text-align:center;vertical-align:middle;width:{}px;height:{}px;background:blue;font-size:{}px;font-family:courier new;color:white'><b>{}</b></div>"
        return folium.DivIcon(html=html_template.format(width, height, font_size, number), icon_size=icon_size)


def add_objects_to_map(features, map):
    group_by_type = make_groups_by_type_prefix(features)
    for obj in features:
        if obj["properties"]["type"] != "sign":
            type = obj["properties"]["type"]
        else:
            type = obj["properties"]["sign_type"]

        icon = get_object_icon(obj)
        if icon is None:
            print("Icons for type " + type + " not found")
            continue

        photos = obj["properties"]["photos"]
        folium.Marker(
            location=obj["geometry"]["coordinates"][::-1],
            icon=icon,
            tooltip=type+", " + "used " + str(len(photos)) + " photos for sign generation",
            popup=layout_photos(photos)
        ).add_to(group_by_type[get_prefix(type)])
    for group in group_by_type.values():
        group.add_to(map)


def add_photos_to_map(features, map):
    photo_set = {}
    for sign in features:
        photos = sign["properties"]["photos"]
        for photo in photos:
            url = photo["url"]
            url = url[:url.rfind('?')]
            coordinates = photo["geometry"]["coordinates"][::-1]
            photo_set[url]={'url':url, 'coordinates':coordinates, "date": photo["date"]}

    photo_group = folium.FeatureGroup("Photo", show=True)
    for photo in photo_set.values():
        folium.Marker(
            location=photo["coordinates"],
            icon=folium.Icon(color="green", icon="info-sign"),
            tooltip=photo["date"],
            popup='<a href="{}"><img src="{}" height="{}"/></a>'.format(photo["url"], photo["url"], 500)
        ).add_to(photo_group)
    photo_group.add_to(map)


def main():
    parser = argparse.ArgumentParser(description="Geojson to HTML")

    parser.add_argument('--geojson', required=True,
                        help='Path to input geo-json file with objects and photos')
    parser.add_argument('--html', required=True,
                        help='Path to output html file')

    args = parser.parse_args()

    data = json.load(open(args.geojson))["data"]
    m = folium.Map(location=get_base_location(data["features"]), zoom_start=19,  max_zoom=21)

    add_objects_to_map(data["features"], m)
    add_photos_to_map(data["features"], m)

    folium.LayerControl().add_to(m)
    m.save(args.html)


if __name__ == "__main__":
    main()
