#!/usr/bin/env python

import copy
import sys

from aman.types.ArrivalRoute import ArrivalRoute
from aman.types.Runway import Runway
from aman.types.Waypoint import Waypoint

class SctEseFormat:
    def readFile(filename : str):
        fileBlocks = {}
        block = None

        # read the file line by line and create segments based on []-entries
        with open(filename) as file:
            for line in file:
                line = line.strip()

                # found a new segment
                if line.startswith('['):
                    block = line[1:-1]
                    fileBlocks.setdefault(block, [])
                # append the last backend
                elif None != block and 0 != len(line):
                    fileBlocks[block].append(line)

        return fileBlocks

    def parseWaypoint(waypoint : str, nameIdx : int, latitudeIdx : int, longitudeIdx : int):
        split = list(filter(None, waypoint.split(' ')))
        if len(split) <= longitudeIdx:
            sys.stderr.write('Invalid waypoint format: ' + waypoint)
            sys.exit(-1)
        return Waypoint(name = split[nameIdx], latitude = split[latitudeIdx], longitude = split[longitudeIdx])

    def parseRunway(runway : str):
        split = list(filter(None, runway.split(' ')))
        if 9 != len(split) or '' == split[8]:
            return None, None, None

        waypoint0 = Waypoint(name = split[0], latitude = split[4], longitude = split[5])
        waypoint1 = Waypoint(name = split[1], latitude = split[6], longitude = split[7])

        return split[8], Runway(waypoint0, waypoint1), Runway(waypoint1, waypoint0)

    def extractSctInformation(self, sctFilepath : str):
        config = SctEseFormat.readFile(sctFilepath)
        foundAirports = False
        foundRunways = False
        foundVOR = False
        foundNDB = False
        foundFix = False

        for key in config:
            if 'VOR' == key:
                foundVOR = True
            elif 'NDB' == key:
                foundNDB = True
            elif 'FIXES' == key:
                foundFix = True
            elif 'AIRPORT' == key:
                foundAirports = True
            elif 'RUNWAY' == key:
                foundRunways = True

        if False == foundVOR:
            sys.stderr.write('Unable to find VOR-entries in the sector file')
            sys.exit(-1)
        if False == foundNDB:
            sys.stderr.write('Unable to find NDB-entries in the sector file')
            sys.exit(-1)
        if False == foundFix:
            sys.stderr.write('Unable to find FIX-entries in the sector file')
            sys.exit(-1)
        if False == foundAirports:
            sys.stderr.write('Unable to find AIRPORT-entries in the sector file')
            sys.exit(-1)
        if False == foundRunways:
            sys.stderr.write('Unable to find RUNWAY-entries in the sector file')
            sys.exit(-1)

        # extract all waypoints
        for waypoint in config['VOR']:
            waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 2, 3)
            self.Waypoints.setdefault(waypoint.Name, []).append(waypoint)
        for waypoint in config['NDB']:
            waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 1, 2)
            self.Waypoints.setdefault(waypoint.Name, []).append(waypoint)
        for waypoint in config['FIXES']:
            waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 1, 2)
            self.Waypoints.setdefault(waypoint.Name, []).append(waypoint)

        # extract the airports
        for airport in config['AIRPORT']:
            airport  = SctEseFormat.parseWaypoint(airport, 0, 2, 3)
            self.Airports.setdefault(airport.Name, []).append(airport)

        # extract the runways
        for runway in config['RUNWAY']:
            airport, runway0, runway1 = SctEseFormat.parseRunway(runway)
            if None != airport:
                if not airport in self.Runways:
                    self.Runways.setdefault(airport, [])
                self.Runways[airport].append(runway0)
                self.Runways[airport].append(runway1)

    def parseArrivalRoute(self, route : str, airport : Waypoint):
        # split the route and validate that it is a STAR for the airport
        split = route.split(':')
        if 5 != len(split) or 'STAR' != split[0] or split[1] != airport.Name:
            return None

        # find all waypoints
        waypoints = []
        route = list(filter(None, split[4].split(' ')))
        for waypoint in route:
            # find the waypoint in the route
            coordinates = self.Waypoints[waypoint]
            # no waypoint with this name defined
            if None == coordinates:
                sys.stderr.write('Unable to find waypoint ' + waypoint)
                sys.exit(-1)
            # multiple waypoints, but use Haversine distance to distinct between candidates
            elif 1 != len(coordinates):
                minDistance = sys.float_info.max
                nearest = None

                # we assume that waypoints with the same name are not close each other
                for coordinate in coordinates:
                    distance = coordinate.haversine(airport)
                    # found a closer waypoint
                    if minDistance > distance:
                        minDistance = distance
                        nearest = coordinate

                if None == nearest:
                    sys.stderr.write('Unable to find a close waypoint for ' + waypoint)
                    sys.exit(-1)

                waypoints.append(copy.deepcopy(nearest))
            # extend the list of waypoints
            else:
                waypoints.append(copy.deepcopy(coordinates[0]))

        # create the arrival route
        return ArrivalRoute(split[3], split[2], waypoints)

    def extractArrivalRoutes(self, eseFilepath : str, airport : str, allowedRoutes : list):
        config = SctEseFormat.readFile(eseFilepath)
        foundSidsStars = False

        # search the airport in the extracted list
        if not airport in self.Airports:
            sys.stderr.write('Unable to find the requested airport')
            sys.exit(-1)
        airport = self.Airports[airport][0]

        for key in config:
            if 'SIDSSTARS' == key:
                foundSidsStars = True

        if False == foundSidsStars:
            sys.stderr.write('Unable to find SIDSSTARS-entries in the sector file')
            sys.exit(-1)

        # parse all arrival routes
        for line in config['SIDSSTARS']:
            route = self.parseArrivalRoute(line, airport)
            if None != route and route.Name in allowedRoutes:
                self.ArrivalRoutes.setdefault(route.Runway, []).append(route)

    def __init__(self, sctFilepath : str, eseFilepath : str, airport : str, allowedRoutes : list):
        self.ArrivalRoutes = {}
        self.Waypoints = {}
        self.Airports = {}
        self.Runways = {}

        self.extractSctInformation(sctFilepath)
        self.extractArrivalRoutes(eseFilepath, airport, allowedRoutes)
