#!/bin/python
"""create a map with all the places of the photos in a folder"""

import os
import sys
from typing import Generator, Iterable, Tuple

import matplotlib.pyplot as plt
from cartopy import crs, feature
from PIL import Image
from PIL.ExifTags import GPSTAGS, IFD

RATIO = 16.0/10
BACKGROUND=(0.0, 0.0, 0.0)
FOREGROUND=(1.0, 1.0, 1.0)
MARKERS=(1.0, 0.0, 0.0)

def hex_to_rgb(value: str) -> Tuple[float, float, float]:
    """convert hex colors to rgb"""
    value = value.lstrip('#')
    lv = len(value)
    return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))


def main():
    """ main function """
    if len(sys.argv) > 1:
        folder_name = sys.argv[1]
    else:
        print("USAGE: mapper folder [output]")
        return

    if len(sys.argv) > 2:
        make_plt(folder_name, 4, 2, 15)
        plt.savefig(
            sys.argv[2],
            bbox_inches = 'tight',
            pad_inches = 0)
    else:
        make_plt(folder_name, 1, 1, 1)
        plt.show()

def make_plt(folder:str , coast_width: int, borders_width: int, marker_width: int):
    """ create a plot object """
    photos = list(get_all_photos(folder))
    print("Total photos: ", len(photos))

    coords = list(get_all_coordinates(photos))
    print(f"Photos with GPS info: {len(coords)} ({int(len(coords)/len(photos)*100)}%)")

    plt.figure(figsize=(RATIO * 100, 100 / RATIO))

    plot = plt.axes(projection=crs.PlateCarree())

    plot.set_facecolor(BACKGROUND)

    plot.add_feature(
        feature.COASTLINE.with_scale('10m'),
        color=FOREGROUND,
        linewidth=coast_width)

    plot.add_feature(
        feature.BORDERS.with_scale('10m'),
        linestyle=':',
        color=FOREGROUND,
        linewidth=borders_width)

    plot.set_extent(
        get_curr_extent(coords, RATIO),
        crs=crs.PlateCarree())

    for lat, long in coords:
        plot.plot(long, lat, markersize=marker_width, marker='o', color=MARKERS)

    return plot

def get_all_photos(folder: str) -> Generator[str, None, None]:
    """return all photos in a folder recursively"""
    for currentpath, _, files in os.walk(folder):
        for file in files:
            _, extension = os.path.splitext(file)
            if extension in ['.jpeg', '.jpg', '.png']:
                yield os.path.join(currentpath, file)

def decimal_coords(coords: Tuple[int, int, int], ref: str) -> float:
    """convert from coords tuples to deciaml coords"""
    decimal_degrees = coords[0] + coords[1] / 60 + coords[2] / 3600
    if ref in ['S', 'W']:
        decimal_degrees = -decimal_degrees
    return decimal_degrees


def get_all_coordinates(photos: Iterable[str]) -> Generator[Tuple[float,float], None, None]:
    """get the coordinates of all photos inside a folder recursively"""
    for photo in photos:
        img=Image.open(photo)

        gps_info={}
        for key, value in img.getexif().get_ifd(IFD.GPSInfo).items():
            geo_tag=GPSTAGS.get(key)
            gps_info[geo_tag]=value

        try:
            yield (decimal_coords(gps_info['GPSLatitude'], gps_info['GPSLatitudeRef']),
            decimal_coords(gps_info['GPSLongitude'], gps_info['GPSLongitudeRef']))

        except KeyError:
            continue

def get_curr_extent(coords: Iterable[Tuple[float, float]], ratio: float) -> Tuple[float, float, float, float]:
    """ compute a border arround a list of coords respecting a specific ratio"""
    # calculate min and max
    lats, longs = zip(*coords)
    minlat = min(lats)
    maxlat = max(lats)
    minlong = min(longs)
    maxlong = max(longs)

    # calculta center coordinates
    centerlat = (minlat + maxlat)/2
    centerlong = (minlong + maxlong)/2

    # calculate side vector
    veclat = abs(maxlat - minlat)
    veclong = abs(maxlong - minlong)

    if centerlat + veclat > 90 or centerlat - veclat < -90 :
        veclat = veclat / 1.9

    if centerlong + veclong > 180 or centerlong - veclong < -180 :
        veclong = veclong / 1.9

    # set ratio
    if veclat < veclong / ratio:
        veclat = veclong / ratio
    elif veclong < veclat * ratio:
        veclong = veclat * ratio

    # calculate extent
    minlat = centerlat - veclat
    maxlat = centerlat + veclat
    minlong = centerlong - veclong
    maxlong = centerlong + veclong

    # fix out of bounds extent
    if maxlong > 180:
        minlong -= maxlong - 180
        maxlong = 180

    if minlong < -180:
        maxlong -= minlong + 180
        minlong = -180

    if maxlat > 90:
        minlat -= maxlat - 90
        maxlat = 90

    if minlat < -90:
        maxlat -= minlat + 90
        minlat = -90

    return (minlong, maxlong, minlat, maxlat)

main()
