#
# flightdata.py
#
# This was originally pulled from here: 
# https://github.com/martinohanlon/flightdata
#
# I made changes to work with the current mutability version of dump1090 and some tweaks to the default output
# kevinabrandon@gmail.com
#
# Original LICENSE from martinohanlon:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#


import traceback
from urllib.request import urlopen
import json
from time import sleep
import geomath
import math
from datetime import datetime
from configparser import ConfigParser

# Read the configuration file for this application.
parser = ConfigParser()
parser.read('config.ini')

# Assign receiver variables.
receiver_latitude = float(parser.get('receiver', 'latitude'))
receiver_longitude = float(parser.get('receiver', 'longitude'))

class FlightData():
    def __init__(self, data_url=None, parser=None):
        self.data_url = data_url
        self.parser = parser
        self.aircraft = None
        self.refresh()

    def refresh(self):
        try:
            #open the data url
            self.req = urlopen(self.data_url)

            #read data from the url
            self.raw_data = self.req.read()

            #load in the json
            self.json_data = json.loads(self.raw_data.decode())

            #get time from json
            self.time = datetime.fromtimestamp(self.parser.time(self.json_data))

            #load all the aircarft
            self.aircraft = self.parser.aircraft_data(self.json_data, self.time)

        except Exception:
            print("exception in FlightData.refresh():")
            traceback.print_exc()


class AirCraftData():
    def __init__(self,
                 dhex,
                 squawk,
                 flight,
                 registration,
                 lat,
                 lon,
                 altitude,
                 vert_rate,
                 track,
                 speed,
                 messages,
                 seen,
                 mlat,
                 nucp,
                 seen_pos,
                 rssi,
                 dist, 
                 az,
                 el,
                 time):
        self.hex = dhex
        self.squawk = squawk
        self.flight = flight
        self.registration = registration
        self.lat = lat
        self.lon = lon
        self.altitude = altitude
        self.vert_rate = vert_rate
        self.track = track
        self.speed = speed
        self.messages = messages
        self.seen = seen
        self.mlat = mlat
        self.nucp = nucp
        self.seen_pos = seen_pos
        self.rssi = rssi
        self.distance = dist
        self.az = az
        self.el = el
        self.time = time

    def __str__(self):
        return '<{} {} dist={} el={}>'.format(
            self.__class__.__name__,
            self.ident_desc(),
            self.distance,
            self.el)

    def ident_desc(self):
        idents = [self.hex, self.registration]
        if self.flight != self.registration:
            idents.append(self.flight)
        idents = [i for i in idents if i]
        return '/'.join(idents)

class AircraftDataParser(object):
    def __init__(self):
        pass

    def aircraft_data(self, json_data, time):
        raise NotImplementedError

    def time(self, json_data):
        raise NotImplementedError


class VRSDataParser(AircraftDataParser):
    def _parse_aircraft_data(self, a, time):
        alt = a.get('Alt', 0)
        dist = -1
        az = 0
        el = 0
        if 'Lat' in a and 'Long' in a:
            rec_pos = (receiver_latitude, receiver_longitude)
            ac_pos = (a['Lat'], a['Long'])
            dist = geomath.distance(rec_pos, ac_pos)
            az = geomath.bearing(rec_pos, ac_pos)
            el = math.degrees(math.atan(alt / (dist * 5280)))
        speed = 0
        if 'Spd' in a:
            speed = geomath.knot2mph(a['Spd'])
        if 'PosTime' in a:
            last_seen_time = datetime.fromtimestamp(a['PosTime'] / 1000.0)
            seen = (time - last_seen_time).total_seconds()
        else:
            seen = 0
        ac_data = AirCraftData(
            a.get('Icao', None).upper(),
            a.get('Sqk', None),
            a.get('Call', None),
            a.get('Reg', None),
            a.get('Lat', None),
            a.get('Long', None),
            alt,
            a.get('Vsi', 0),
            a.get('Trak', None),
            speed,
            a.get('CMsgs', None),
            seen,
            a.get('Mlat', False),
            None,  # NUCP
            None,  # Seen pos
            10.0 * math.log10(a.get('Sig', 0) / 255.0 + 1e-5),
            dist,
            az,
            el,
            time)
        return ac_data

    def aircraft_data(self, json_data, time):
        aircraft_list = [self._parse_aircraft_data(d, time) for d in json_data['acList']]
        return aircraft_list

    def time(self, json_data):
        return json_data['stm'] / 1000.0


class Dump1090DataParser(AircraftDataParser):
    def aircraft_data(self, json_data, time):
        aircraft_list = []
        for a in json_data["aircraft"]:

            alt = a["altitude"] if "altitude" in a else 0
            if alt == "ground":
                alt = 0
            dist = -1
            az = 0
            el = 0
            if "lat" in a and "lon" in a:
                dist = geomath.distance((receiver_latitude, receiver_longitude), (a["lat"], a["lon"]))
                az = geomath.bearing((receiver_latitude, receiver_longitude), (a["lat"], a["lon"]))
                el = math.degrees(math.atan(alt / (dist*5280)))
            speed = 0
            if "speed" in a:
                speed = geomath.knot2mph(a["speed"])

            aircraftdata = AirCraftData(
                a["hex"].upper() if "hex" in a else None,
                a["squawk"] if "squawk" in a else None,
                a["flight"] if "flight" in a else None,
                None,
                a["lat"] if "lat" in a else None,
                a["lon"] if "lon" in a else None,
                alt,
                a["vert_rate"] if "vert_rate" in a else 0,
                a["track"] if "track" in a else None,
                speed,
                a["messages"] if "messages" in a else None,
                a["seen"] if "seen" in a else None,
                a["mlat"] if "mlat" in a else None,
                a["nucp"] if "nucp" in a else None,
                a["seen_pos"] if "seen_pos" in a else None,
                a["rssi"] if "rssi" in a else None,
                dist,
                az,
                el,
                time)

            aircraft_list.append(aircraftdata)
        return aircraft_list

    def time(self, json_data):
        return json_data['now']


if __name__ == "__main__":
    import os

    flightdata = FlightData()
    while True:
        os.system('clear')
        print("Now: {}".format(flightdata.time.strftime('%Y-%m-%d %H:%M:%S')))
        print("|  icao   | flight  | miles |   az  |  el  |  alt  | mi/h  | vert  | rssi  | mesgs | seen |")
        print("|---------+---------+-------+-------+------+-------+-------+-------+-------+-------+------|")
        sortedlist = []
        for a in flightdata.aircraft:
            if a.lat == None or a.lon == None:
                continue
            sortedlist.append(a)
        
        sortedlist.sort(key=lambda x: x.distance) # actually do the sorting here

        for a in sortedlist:
            print("| {:<7} | {:^8}| {:>5} | {:>5} | {:>4} | {:>5} | {:>5} | {:>+5} | {:>5} | {:>5} | {:>4} |".format(
                a.hex, 
                a.flight, 
                "%.1f" % a.distance,
                "%.1f" % a.az,
                "%.1f" % a.el,
                a.altitude, 
                "%.1f" % a.speed,
                a.vert_rate,
                "%0.1f" % a.rssi,
                a.messages,
                "%.1f" % a.seen))
        sleep(0.5)
        flightdata.refresh()