import collections
import simplekml
from pathlib2 import Path


def _degrees_float_to_degrees_minutes_seconds(dd, lon_or_lat):
    """Converts from floating point degrees to degrees/minutes/seconds.

    Args:
        dd: a float, degrees to be converted
        lon_or_lat: a string, argument used to calculate the hemisphere;
        options are 'lon' - for longitude or 'lat' - for latitude

    Returns:
        A namedtuple with hemisphere, degrees, minutes and floating point
        seconds elements.
    """
    ddmmss = collections.namedtuple(
        'ddmmss', ['hemisphere', 'degrees', 'minutes', 'seconds'])
    negative = dd < 0
    dd = abs(dd)
    minutes, seconds = divmod(dd * 3600, 60)
    degrees, minutes = divmod(minutes, 60)
    if lon_or_lat == 'lon':
        hemisphere = 'E'
    elif lon_or_lat == 'lat':
        hemisphere = 'N'

    if negative:
        if lon_or_lat == 'lon':
            hemisphere = 'W'
        elif lon_or_lat == 'lat':
            hemisphere = 'S'

    return ddmmss(hemisphere, degrees, minutes, seconds)


def dump_thermals_to_wpt_file(flight, wptfilename_local, endpoints=False):
    """Dump flight's thermals to a .wpt file in Geo format.

    Args:
        flight: an igc_lib.Flight, the flight to be written
        wptfilename_local: File to be written. If it exists it will be overwritten.
        endpoints: optional argument. If true thermal endpoints as well
        as startpoints will be written with suffix END in the waypoint label.
    """
    wptfilename = Path(wptfilename_local).expanduser().absolute()
    with wptfilename.open('w') as wpt:
        wpt.write(u"$FormatGEO\n")

        for x, thermal in enumerate(flight.thermals):
            lat = _degrees_float_to_degrees_minutes_seconds(
                flight.thermals[x].enter_fix.lat, 'lat')
            lon = _degrees_float_to_degrees_minutes_seconds(
                flight.thermals[x].enter_fix.lon, 'lon')
            wpt.write(u"%02d        " % x)
            wpt.write(u"%s %02d %02d %05.2f    " % (
                lat.hemisphere, lat.degrees, lat.minutes, lat.seconds))
            wpt.write(u"%s %03d %02d %05.2f     " % (
                lon.hemisphere, lon.degrees, lon.minutes, lon.seconds))
            wpt.write(u"          %d\n" % flight.thermals[x].enter_fix.gnss_alt)

            if endpoints:
                lat = _degrees_float_to_degrees_minutes_seconds(
                    flight.thermals[x].exit_fix.lat, 'lat')
                lon = _degrees_float_to_degrees_minutes_seconds(
                    flight.thermals[x].exit_fix.lon, 'lon')
                wpt.write(u"%02dEND     " % x)
                wpt.write(u"%s %02d %02d %05.2f    " % (
                    lat.hemisphere, lat.degrees, lat.minutes, lat.seconds))
                wpt.write(u"%s %03d %02d %05.2f     " % (
                    lon.hemisphere, lon.degrees, lon.minutes, lon.seconds))
                wpt.write(u"          %d\n" % (
                    flight.thermals[x].exit_fix.gnss_alt))


def dump_thermals_to_cup_file(flight, cup_filename_local):
    """Dump flight's thermals to a .cup file (SeeYou).

    Args:
        flight: an igc_lib.Flight, the flight to be written
        cup_filename_local: a string, the name of the file to be written.
    """
    cup_filename = Path(cup_filename_local).expanduser().absolute()
    with cup_filename.open('wt') as wpt:
        wpt.write(u'name,code,country,lat,')
        wpt.write(u'lon,elev,style,rwdir,rwlen,freq,desc,userdata,pics\n')

        def write_fix(name, fix):
            lat = _degrees_float_to_degrees_minutes_seconds(fix.lat, 'lat')
            lon = _degrees_float_to_degrees_minutes_seconds(fix.lon, 'lon')
            wpt.write(u'"%s",,,%02d%02d.%03d%s,' % (
                name, lat.degrees, lat.minutes,
                int(round(lat.seconds/60.0*1000.0)), lat.hemisphere))
            wpt.write(u'%03d%02d.%03d%s,%fm,,,,,,,' % (
                lon.degrees, lon.minutes,
                int(round(lon.seconds/60.0*1000.0)), lon.hemisphere,
                fix.gnss_alt))
            wpt.write(u'\n')

        for i, thermal in enumerate(flight.thermals):
            write_fix(u'%02d' % i, thermal.enter_fix)
            write_fix(u'%02d_END' % i, thermal.exit_fix)


def dump_flight_to_kml(flight, kml_filename_local):
    """Dumps the flight to KML format.

    Args:
        flight: an igc_lib.Flight, the flight to be saved
        kml_filename_local: a string, the name of the output file
    """
    assert flight.valid
    kml = simplekml.Kml()

    def add_point(name, fix):
        kml.newpoint(name=name, coords=[(fix.lon, fix.lat)])

    coords = []
    for fix in flight.fixes:
        coords.append((fix.lon, fix.lat))
    kml.newlinestring(coords=coords)

    add_point(name="Takeoff", fix=flight.takeoff_fix)
    add_point(name="Landing", fix=flight.landing_fix)

    for i, thermal in enumerate(flight.thermals):
        add_point(name="thermal_%02d" % i, fix=thermal.enter_fix)
        add_point(name="thermal_%02d_END" % i, fix=thermal.exit_fix)
        kml_filename = Path(kml_filename_local).expanduser().absolute()
    kml.save(kml_filename.as_posix())


def dump_flight_to_csv(flight, track_filename_local, thermals_filename_local):
    """Dumps flight data to CSV files.

    Args:
        flight: an igc_lib.Flight, the flight to be written
        track_filename_local: a string, the name of the output CSV with track data
        thermals_filename_local: a string, the name of the output CSV with thermal data
    """
    track_filename = Path(track_filename_local).expanduser().absolute()
    with track_filename.open('wt') as csv:
        csv.write(u"timestamp,lat,lon,bearing,bearing_change_rate,"
                  u"gsp,flying,circling\n")
        for fix in flight.fixes:
            csv.write(u"%f,%f,%f,%f,%f,%f,%s,%s\n" % (
                fix.timestamp, fix.lat, fix.lon,
                fix.bearing, fix.bearing_change_rate,
                fix.gsp, str(fix.flying), str(fix.circling)))

    thermals_filename = Path(thermals_filename_local).expanduser().absolute()
    with thermals_filename.open('wt') as csv:
        csv.write(u"timestamp_enter,timestamp_exit\n")
        for thermal in flight.thermals:
            csv.write(u"%f,%f\n" % (
                thermal.enter_fix.timestamp, thermal.exit_fix.timestamp))